import { BadRequestException } from '@nestjs/common';
import { AppLoggerService } from '../../infrastructure/logging/app-logger.service';
import { HlsPlaylistService } from './hls-playlist.service';
import { MediaStorageService } from './media-storage.service';

describe('HlsPlaylistService', () => {
  const storageService = {
    shouldSignResponseUrls: jest.fn(() => true),
    isValidHlsPlaylistAccess: jest.fn(() => true),
    getBucketName: jest.fn(() => 'oudelaa'),
    isManagedKey: jest.fn((key: string) => key.startsWith('uploads/')),
    getObjectText: jest.fn(),
    getHlsPlaylistUrl: jest.fn((key: string) => `https://api.oudelaa.test/api/v1/media/hls/${key}`),
    getSignedObjectUrl: jest.fn(
      async (key: string) => `https://s3.cumin.dev/oudelaa/${key}?X-Amz-Signature=signed`,
    ),
    resolveKeyFromUrl: jest.fn(() => null),
  } as unknown as MediaStorageService;
  const logger = {
    log: jest.fn(),
    error: jest.fn(),
  } as unknown as AppLoggerService;
  const service = new HlsPlaylistService(storageService, logger);

  beforeEach(() => {
    jest.clearAllMocks();
    (storageService.isValidHlsPlaylistAccess as jest.Mock).mockReturnValue(true);
  });

  it('rejects an invalid or expired playlist access signature', async () => {
    (storageService.isValidHlsPlaylistAccess as jest.Mock).mockReturnValue(false);

    await expect(
      service.rewritePlaylist('uploads/posts/hls/stream-1/master.m3u8'),
    ).rejects.toThrow(BadRequestException);
    expect(storageService.getObjectText).not.toHaveBeenCalled();
  });

  it('rewrites master playlist variants through the backend endpoint', async () => {
    (storageService.getObjectText as jest.Mock).mockResolvedValue(
      [
        '#EXTM3U',
        '#EXT-X-STREAM-INF:BANDWIDTH=800000',
        'stream_0/playlist.m3u8',
        '#EXT-X-STREAM-INF:BANDWIDTH=1600000',
        'stream_1/playlist.m3u8',
      ].join('\n'),
    );

    const result = await service.rewritePlaylist('uploads/posts/hls/stream-1/master.m3u8');

    expect(result).toContain(
      'https://api.oudelaa.test/api/v1/media/hls/uploads/posts/hls/stream-1/stream_0/playlist.m3u8',
    );
    expect(result).toContain(
      'https://api.oudelaa.test/api/v1/media/hls/uploads/posts/hls/stream-1/stream_1/playlist.m3u8',
    );
    expect(storageService.getSignedObjectUrl).not.toHaveBeenCalled();
  });

  it('rewrites variant segments and EXT-X-MAP assets to presigned S3 URLs', async () => {
    (storageService.getObjectText as jest.Mock).mockResolvedValue(
      [
        '#EXTM3U',
        '#EXT-X-MAP:URI="init.mp4"',
        '#EXTINF:4.0,',
        'segment-000.m4s',
        '#EXTINF:4.0,',
        'segment-001.m4s',
      ].join('\n'),
    );

    const result = await service.rewritePlaylist(
      'uploads/posts/hls/stream-1/stream_0/playlist.m3u8',
    );

    expect(result).toContain(
      'URI="https://s3.cumin.dev/oudelaa/uploads/posts/hls/stream-1/stream_0/init.mp4?X-Amz-Signature=signed"',
    );
    expect(result).toContain(
      'https://s3.cumin.dev/oudelaa/uploads/posts/hls/stream-1/stream_0/segment-000.m4s?X-Amz-Signature=signed',
    );
    expect(storageService.getSignedObjectUrl).toHaveBeenCalledTimes(3);
  });

  it('rejects playlists outside managed storage', async () => {
    await expect(service.rewritePlaylist('private/master.m3u8')).rejects.toBeInstanceOf(
      BadRequestException,
    );
  });
});
