import { Types } from 'mongoose';
import { MediaRepository } from './media.repository';
import { AiMusicArchiveType } from './schemas/ai-music-archive.schema';

const chain = (result: unknown) => {
  const query: Record<string, jest.Mock> = {};
  for (const method of ['sort', 'skip', 'limit']) query[method] = jest.fn(() => query);
  query.exec = jest.fn().mockResolvedValue(result);
  return query;
};

describe('MediaRepository', () => {
  const model = {
    create: jest.fn(), find: jest.fn(), countDocuments: jest.fn(), findOne: jest.fn(), findOneAndUpdate: jest.fn(),
  };
  const repository = new MediaRepository(model as never);
  const userId = new Types.ObjectId().toString();
  const itemId = new Types.ObjectId().toString();

  beforeEach(() => jest.clearAllMocks());

  it('creates archive items scoped to an ObjectId owner', async () => {
    model.create.mockResolvedValue({ id: itemId });
    const payload = { type: AiMusicArchiveType.TEXT_TO_MUSIC, title: 'Song', prompt: 'calm', tag: 'ambient', audioUrl: '/song.mp3' };
    await expect(repository.createAiMusicArchiveItem(userId, payload)).resolves.toEqual({ id: itemId });
    expect(model.create).toHaveBeenCalledWith(expect.objectContaining({ ...payload, userId: new Types.ObjectId(userId), isDeleted: false }));
  });

  it('lists and counts non-deleted archive items', async () => {
    const listQuery = chain([{ id: itemId }]);
    model.find.mockReturnValue(listQuery);
    await expect(repository.findAiMusicArchiveItemsByUser(userId, 20, 10)).resolves.toEqual([{ id: itemId }]);
    expect(listQuery.sort).toHaveBeenCalledWith({ createdAt: -1 });
    expect(listQuery.skip).toHaveBeenCalledWith(20);
    expect(listQuery.limit).toHaveBeenCalledWith(10);
    const countQuery = chain(4);
    model.countDocuments.mockReturnValue(countQuery);
    await expect(repository.countAiMusicArchiveItemsByUser(userId)).resolves.toBe(4);
  });

  it('rejects invalid item ids before querying', async () => {
    await expect(repository.findAiMusicArchiveItemByUser(userId, 'bad')).resolves.toBeNull();
    await expect(repository.updateAiMusicArchiveTitle(userId, 'bad', 'title')).resolves.toBeNull();
    await expect(repository.softDeleteAiMusicArchiveItem(userId, 'bad')).resolves.toBe(false);
    expect(model.findOne).not.toHaveBeenCalled();
    expect(model.findOneAndUpdate).not.toHaveBeenCalled();
  });

  it('finds, renames, and soft deletes owned valid items', async () => {
    model.findOne.mockReturnValue(chain({ id: itemId }));
    await expect(repository.findAiMusicArchiveItemByUser(userId, itemId)).resolves.toEqual({ id: itemId });
    model.findOneAndUpdate.mockReturnValueOnce(chain({ id: itemId, title: 'New' })).mockReturnValueOnce(chain({ id: itemId }));
    await expect(repository.updateAiMusicArchiveTitle(userId, itemId, 'New')).resolves.toEqual({ id: itemId, title: 'New' });
    await expect(repository.softDeleteAiMusicArchiveItem(userId, itemId)).resolves.toBe(true);
    model.findOneAndUpdate.mockReturnValueOnce(chain(null));
    await expect(repository.softDeleteAiMusicArchiveItem(userId, itemId)).resolves.toBe(false);
  });
});
