import { ExecutionContext, CallHandler } from '@nestjs/common';
import { of, throwError } from 'rxjs';
import { AuditLogInterceptor } from '../../../src/shared/interceptors/audit-log.interceptor';

describe('AuditLogInterceptor', () => {
  let interceptor: AuditLogInterceptor;

  const createMockContext = (
    method: string = 'POST',
    url: string = '/backup-share/store',
    body: any = {},
    sourceService: string = 'identity-service',
    sourceIp: string = '127.0.0.1',
  ): ExecutionContext => {
    return {
      switchToHttp: () => ({
        getRequest: () => ({
          method,
          url,
          body,
          sourceService,
          sourceIp,
        }),
      }),
    } as ExecutionContext;
  };

  const createMockCallHandler = (response: any = {}): CallHandler => ({
    handle: () => of(response),
  });

  const createErrorCallHandler = (error: Error): CallHandler => ({
    handle: () => throwError(() => error),
  });

  beforeEach(() => {
    interceptor = new AuditLogInterceptor();
  });

  describe('intercept', () => {
    it('should allow request to proceed', (done) => {
      const context = createMockContext();
      const handler = createMockCallHandler({ success: true });

      interceptor.intercept(context, handler).subscribe({
        next: (response) => {
          expect(response).toEqual({ success: true });
          done();
        },
      });
    });

    it('should not modify the response', (done) => {
      const context = createMockContext();
      const originalResponse = { shareId: '123', success: true };
      const handler = createMockCallHandler(originalResponse);

      interceptor.intercept(context, handler).subscribe({
        next: (response) => {
          expect(response).toEqual(originalResponse);
          done();
        },
      });
    });

    it('should propagate errors', (done) => {
      const context = createMockContext();
      const error = new Error('Test error');
      const handler = createErrorCallHandler(error);

      interceptor.intercept(context, handler).subscribe({
        error: (err) => {
          expect(err).toBe(error);
          done();
        },
      });
    });

    it('should sanitize sensitive fields in body', () => {
      const sensitiveBody = {
        userId: '12345',
        encryptedShareData: 'secret-data',
        recoveryToken: 'secret-token',
        password: 'secret-password',
        publicKey: '02abc...',
      };

      // Access the private method through prototype
      const sanitized = (interceptor as any).sanitizeBody(sensitiveBody);

      expect(sanitized.userId).toBe('12345');
      expect(sanitized.publicKey).toBe('02abc...');
      expect(sanitized.encryptedShareData).toBe('[REDACTED]');
      expect(sanitized.recoveryToken).toBe('[REDACTED]');
      expect(sanitized.password).toBe('[REDACTED]');
    });

    it('should handle null body', () => {
      const sanitized = (interceptor as any).sanitizeBody(null);
      expect(sanitized).toBeNull();
    });

    it('should handle undefined body', () => {
      const sanitized = (interceptor as any).sanitizeBody(undefined);
      expect(sanitized).toBeUndefined();
    });

    it('should handle empty body', () => {
      const sanitized = (interceptor as any).sanitizeBody({});
      expect(sanitized).toEqual({});
    });

    it('should not modify original body object', () => {
      const originalBody = {
        userId: '12345',
        encryptedShareData: 'secret-data',
      };
      const bodyCopy = { ...originalBody };

      (interceptor as any).sanitizeBody(originalBody);

      expect(originalBody).toEqual(bodyCopy);
    });
  });

  describe('logging behavior', () => {
    it('should handle different HTTP methods', (done) => {
      const methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'];

      let completed = 0;
      methods.forEach((method) => {
        const context = createMockContext(method, '/test');
        const handler = createMockCallHandler();

        interceptor.intercept(context, handler).subscribe({
          complete: () => {
            completed++;
            if (completed === methods.length) {
              done();
            }
          },
        });
      });
    });

    it('should handle different URL paths', (done) => {
      const paths = [
        '/backup-share/store',
        '/backup-share/retrieve',
        '/backup-share/revoke',
        '/health',
      ];

      let completed = 0;
      paths.forEach((url) => {
        const context = createMockContext('GET', url);
        const handler = createMockCallHandler();

        interceptor.intercept(context, handler).subscribe({
          complete: () => {
            completed++;
            if (completed === paths.length) {
              done();
            }
          },
        });
      });
    });

    it('should handle unknown source service', (done) => {
      const context = createMockContext('POST', '/test', {}, undefined, '127.0.0.1');
      const handler = createMockCallHandler();

      interceptor.intercept(context, handler).subscribe({
        complete: () => {
          done();
        },
      });
    });

    it('should handle unknown source IP', (done) => {
      const context = createMockContext('POST', '/test', {}, 'identity-service', undefined);
      const handler = createMockCallHandler();

      interceptor.intercept(context, handler).subscribe({
        complete: () => {
          done();
        },
      });
    });
  });
});
