import { ConfigService } from '@nestjs/config';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { AppLoggerService } from '../../infrastructure/logging/app-logger.service';
import { MediaStorageService } from './media-storage.service';

jest.mock('@aws-sdk/s3-request-presigner', () => ({
  getSignedUrl: jest.fn(),
}));

const mockedGetSignedUrl = jest.mocked(getSignedUrl);

describe('MediaStorageService response URLs', () => {
  const config: Record<string, unknown> = {
    'storage.provider': 's3',
    'storage.mediaAccessMode': 'signed',
    'storage.signedUrlExpiresSeconds': 86400,
    'storage.basePath': 'uploads',
    'storage.publicBaseUrl': 'https://s3.cumin.dev',
    'storage.s3.bucket': 'oudelaa',
    'storage.s3.region': 'garage',
    'storage.s3.endpoint': 'https://s3.cumin.dev',
    'storage.s3.accessKeyId': 'test-access-key',
    'storage.s3.secretAccessKey': 'test-secret-key',
    'storage.s3.forcePathStyle': true,
    publicBaseUrl: 'https://api.oudelaa.test',
    globalPrefix: 'api/v1',
    'security.refreshTokenHashSecret': 'test-hls-signing-secret',
  };
  const configService = {
    get: jest.fn((key: string, defaultValue?: unknown) => config[key] ?? defaultValue),
  } as unknown as ConfigService;
  const appLogger = {
    error: jest.fn(),
  } as unknown as AppLoggerService;

  beforeEach(() => {
    mockedGetSignedUrl.mockReset();
    (appLogger.error as jest.Mock).mockReset();
  });

  it('signs a managed S3 URL with the configured expiry', async () => {
    mockedGetSignedUrl.mockResolvedValue(
      'https://s3.cumin.dev/oudelaa/uploads/posts/image.jpg?X-Amz-Signature=signed',
    );
    const service = new MediaStorageService(configService, appLogger);

    await expect(
      service.resolveResponseUrl('https://s3.cumin.dev/uploads/posts/image.jpg'),
    ).resolves.toContain('X-Amz-Signature=signed');

    expect(mockedGetSignedUrl).toHaveBeenCalledWith(
      expect.anything(),
      expect.objectContaining({
        input: {
          Bucket: 'oudelaa',
          Key: 'uploads/posts/image.jpg',
        },
      }),
      { expiresIn: 86400 },
    );
  });

  it('rewrites a managed HLS URL to the backend playlist endpoint', async () => {
    const service = new MediaStorageService(configService, appLogger);

    await expect(
      service.resolveResponseUrl('https://s3.cumin.dev/uploads/posts/hls/stream-1/master.m3u8'),
    ).resolves.toMatch(
      /^https:\/\/api\.oudelaa\.test\/api\/v1\/media\/hls\/uploads\/posts\/hls\/stream-1\/master\.m3u8\?expires=\d+&signature=/,
    );
    expect(mockedGetSignedUrl).not.toHaveBeenCalled();
  });

  it('leaves external URLs unchanged', async () => {
    const service = new MediaStorageService(configService, appLogger);

    await expect(service.resolveResponseUrl('https://cdn.example.com/external.jpg')).resolves.toBe(
      'https://cdn.example.com/external.jpg',
    );
    expect(mockedGetSignedUrl).not.toHaveBeenCalled();
  });

  it('extracts the key from a path-style URL that includes the bucket', async () => {
    config['storage.publicBaseUrl'] = '';
    mockedGetSignedUrl.mockResolvedValue(
      'https://s3.cumin.dev/oudelaa/uploads/posts/video.mp4?X-Amz-Signature=signed',
    );
    const service = new MediaStorageService(configService, appLogger);

    await service.resolveResponseUrl('https://s3.cumin.dev/oudelaa/uploads/posts/video.mp4');

    expect(mockedGetSignedUrl).toHaveBeenCalledWith(
      expect.anything(),
      expect.objectContaining({
        input: {
          Bucket: 'oudelaa',
          Key: 'uploads/posts/video.mp4',
        },
      }),
      { expiresIn: 86400 },
    );
    config['storage.publicBaseUrl'] = 'https://s3.cumin.dev';
  });

  it('leaves local storage URLs unchanged', async () => {
    config['storage.provider'] = 'local';
    const service = new MediaStorageService(configService, appLogger);

    await expect(service.resolveResponseUrl('/uploads/posts/image.jpg')).resolves.toBe(
      '/uploads/posts/image.jpg',
    );
    expect(mockedGetSignedUrl).not.toHaveBeenCalled();
    config['storage.provider'] = 's3';
  });

  it('logs key, bucket and error details without returning a direct URL', async () => {
    mockedGetSignedUrl.mockRejectedValue(new Error('signing failed'));
    const service = new MediaStorageService(configService, appLogger);

    await expect(
      service.resolveResponseUrl('https://s3.cumin.dev/uploads/posts/image.jpg'),
    ).resolves.toBe('');
    expect(appLogger.error).toHaveBeenCalledWith(
      expect.objectContaining({
        event: 's3_presigned_url_failed',
        key: 'uploads/posts/image.jpg',
        bucket: 'oudelaa',
        errorName: 'Error',
        errorMessage: 'signing failed',
      }),
      expect.any(String),
      'MediaStorageService',
    );
  });
});
