import { describe, it, expect, beforeEach } from 'vitest';
import { appRouter } from './index';
import { getTestDb, resetTestDb } from './test-setup';
import { resetInMemoryState } from './revisions-router';
import type { TRPCContext } from './types';

describe('revisionsRouter', () => {
  let ctx: TRPCContext;
  let caller: ReturnType<typeof appRouter.createCaller>;

  beforeEach(async () => {
    await resetTestDb();
    const db = await getTestDb();
    await resetInMemoryState(db);
    ctx = { clerkUserId: 'user_test', db };
    caller = appRouter.createCaller(ctx);
  });

  describe('createRevision', () => {
    it('should create a revision with version 1', async () => {
      const result = await caller.revisions.createRevision({
        scriptId: 1,
        title: 'Initial draft',
        content: 'FADE IN:\n\nINT. ROOM - DAY',
      });

      expect(result.versionNumber).toBe(1);
      expect(result.branchName).toBe('main');
      expect(result.status).toBe('draft');
      expect(result.authorId).toBe(1);
    });

    it('should increment version number for same script', async () => {
      await caller.revisions.createRevision({
        scriptId: 1,
        title: 'v1',
        content: 'content1',
      });

      const result = await caller.revisions.createRevision({
        scriptId: 1,
        title: 'v2',
        content: 'content2',
      });

      expect(result.versionNumber).toBe(2);
    });

    it('should support custom branch', async () => {
      const result = await caller.revisions.createRevision({
        scriptId: 1,
        title: 'Branch revision',
        content: 'branch content',
        branchName: 'feature-act2',
      });

      expect(result.branchName).toBe('feature-act2');
    });
  });

  describe('listRevisions', () => {
    it('should return empty array for script with no revisions', async () => {
      const result = await caller.revisions.listRevisions({ scriptId: 1 });
      expect(result).toEqual([]);
    });

    it('should throw NOT_FOUND for unknown script', async () => {
      const { TRPCError } = await import('./router');
      await expect(
        caller.revisions.listRevisions({ scriptId: 999 })
      ).rejects.toThrow(TRPCError);
    });

    it('should filter by branch', async () => {
      await caller.revisions.createRevision({
        scriptId: 1,
        title: 'main v1',
        content: 'main',
        branchName: 'main',
      });

      await caller.revisions.createRevision({
        scriptId: 1,
        title: 'feature v1',
        content: 'feature',
        branchName: 'feature',
      });

      const mainRevisions = await caller.revisions.listRevisions({
        scriptId: 1,
        branchName: 'main',
      });

      expect(mainRevisions).toHaveLength(1);
      expect(mainRevisions[0]!.branchName).toBe('main');
    });
  });

  describe('acceptRevision', () => {
    it('should accept a revision', async () => {
      const created = await caller.revisions.createRevision({
        scriptId: 1,
        title: 'To accept',
        content: 'content',
      });

      const result = await caller.revisions.acceptRevision({
        revisionId: created.id,
      });

      expect(result.status).toBe('accepted');
      expect(result.reviewedById).toBe(1);
      expect(result.reviewedAt).toBeDefined();
    });
  });

  describe('rejectRevision', () => {
    it('should reject a revision with reason', async () => {
      const created = await caller.revisions.createRevision({
        scriptId: 1,
        title: 'To reject',
        content: 'content',
      });

      const result = await caller.revisions.rejectRevision({
        revisionId: created.id,
        reason: 'Needs more work on dialogue',
      });

      expect(result.status).toBe('rejected');
      expect(result.summary).toContain('Needs more work on dialogue');
    });
  });

  describe('rollbackToRevision', () => {
    it('should create a new revision with old content', async () => {
      const original = await caller.revisions.createRevision({
        scriptId: 1,
        title: 'Original',
        content: 'original content',
      });

      await caller.revisions.createRevision({
        scriptId: 1,
        title: 'Changed',
        content: 'changed content',
      });

      const rollback = await caller.revisions.rollbackToRevision({
        scriptId: 1,
        revisionId: original.id,
      });

      expect(rollback.content).toBe('original content');
      expect(rollback.versionNumber).toBe(3);
      expect(rollback.title).toContain('Rollback');
    });
  });

  describe('compareRevisions', () => {
    it('should compare two revisions', async () => {
      const rev1 = await caller.revisions.createRevision({
        scriptId: 1,
        title: 'v1',
        content: 'line1\nline2\nline3',
      });

      const rev2 = await caller.revisions.createRevision({
        scriptId: 1,
        title: 'v2',
        content: 'line1\nchanged\nline3',
      });

      const result = await caller.revisions.compareRevisions({
        baseRevisionId: rev1.id,
        targetRevisionId: rev2.id,
      });

      expect(result.diff.modifications).toBe(1);
      expect(result.diff.additions).toBe(0);
      expect(result.diff.deletions).toBe(0);
    });
  });

  describe('getTimeline', () => {
    it('should return timeline entries in chronological order', async () => {
      await caller.revisions.createRevision({
        scriptId: 1,
        title: 'First',
        content: 'first',
      });

      await caller.revisions.createRevision({
        scriptId: 1,
        title: 'Second',
        content: 'second',
      });

      const timeline = await caller.revisions.getTimeline({ scriptId: 1 });

      expect(timeline).toHaveLength(2);
      expect(timeline[0]!.revision.title).toBe('First');
      expect(timeline[1]!.revision.title).toBe('Second');
    });
  });

  describe('getBranches', () => {
    it('should return branch information', async () => {
      await caller.revisions.createRevision({
        scriptId: 1,
        title: 'main v1',
        content: 'main',
      });

      await caller.revisions.createBranch({
        scriptId: 1,
        branchName: 'feature',
      });

      const branches = await caller.revisions.getBranches({ scriptId: 1 });

      expect(branches).toHaveLength(2);
      const branchNames = branches.map((b: any) => b.branchName);
      expect(branchNames).toContain('main');
      expect(branchNames).toContain('feature');
    });
  });

  describe('deleteRevision', () => {
    it('should delete a revision', async () => {
      const created = await caller.revisions.createRevision({
        scriptId: 1,
        title: 'To delete',
        content: 'content',
      });

      const result = await caller.revisions.deleteRevision({ id: created.id });

      expect(result.success).toBe(true);

      await expect(
        caller.revisions.getRevision({ id: created.id })
      ).rejects.toThrow();
    });
  });
});
