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

describe('tRPC API Layer - Character System', () => {
  let ctx: TRPCContext;
  let caller: ReturnType<typeof appRouter.createCaller>;
  let projectId: number;

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

    const project = await caller.project.createProject({
      name: 'Character System Test Project',
    });
    projectId = project.id;
  });

  describe('createCharacter', () => {
    it('should create a character with all profile fields', async () => {
      const character = await caller.project.createCharacter({
        name: 'John Doe',
        bio: 'A brave hero',
        role: 'protagonist',
        arc: 'Grows from coward to leader',
        arcType: 'positive',
        age: 30,
        gender: 'male',
        voice: 'Deep, commanding',
        traits: 'Brave, loyal, stubborn',
        motivation: 'Protect his family',
        conflict: 'Internal fear of failure',
        secret: 'Afraid of heights',
        projectId,
      });

      expect(character).toMatchObject({
        name: 'John Doe',
        bio: 'A brave hero',
        role: 'protagonist',
        arcType: 'positive',
        age: 30,
        projectId,
      });
      expect(character.slug).toBe('john-doe');
    });

    it('should default role to supporting when not provided', async () => {
      const character = await caller.project.createCharacter({
        name: 'Jane Smith',
        projectId,
      });

      expect(character.role).toBe('supporting');
    });
  });

  describe('updateCharacter', () => {
    it('should update character profile fields', async () => {
      const created = await caller.project.createCharacter({
        name: 'Original',
        projectId,
      });

      const updated = await caller.project.updateCharacter({
        id: created.id,
        name: 'Updated Name',
        bio: 'New bio',
        role: 'antagonist',
      });

      expect(updated.name).toBe('Updated Name');
      expect(updated.slug).toBe('updated-name');
      expect(updated.bio).toBe('New bio');
      expect(updated.role).toBe('antagonist');
    });
  });

  describe('searchCharacters', () => {
    it('should filter characters by query', async () => {
      await caller.project.createCharacter({
        name: 'Alice',
        bio: 'The hero',
        projectId,
      });
      await caller.project.createCharacter({
        name: 'Bob',
        bio: 'The villain',
        projectId,
      });

      const results = await caller.project.searchCharacters({
        projectId,
        query: 'hero',
      });

      expect(results.length).toBe(1);
      expect(results[0].name).toBe('Alice');
    });

    it('should filter characters by role', async () => {
      await caller.project.createCharacter({
        name: 'Protag',
        role: 'protagonist',
        projectId,
      });
      await caller.project.createCharacter({
        name: 'Antag',
        role: 'antagonist',
        projectId,
      });

      const results = await caller.project.searchCharacters({
        projectId,
        role: 'protagonist',
      });

      expect(results.length).toBe(1);
      expect(results[0].name).toBe('Protag');
    });
  });

  describe('createRelationship', () => {
    it('should create a relationship between two characters', async () => {
      const charA = await caller.project.createCharacter({
        name: 'Character A',
        projectId,
      });
      const charB = await caller.project.createCharacter({
        name: 'Character B',
        projectId,
      });

      const rel = await caller.project.createRelationship({
        characterIdA: charA.id,
        characterIdB: charB.id,
        relationshipType: 'friendship',
        strength: 80,
        isAntagonistic: false,
      });

      expect(rel.characterIdA).toBe(charA.id);
      expect(rel.characterIdB).toBe(charB.id);
      expect(rel.relationshipType).toBe('friendship');
      expect(rel.strength).toBe(80);
    });

    it('should prevent self-relationships', async () => {
      const charA = await caller.project.createCharacter({
        name: 'Character A',
        projectId,
      });

      await expect(
        caller.project.createRelationship({
          characterIdA: charA.id,
          characterIdB: charA.id,
          relationshipType: 'friendship',
        })
      ).rejects.toThrow('Cannot create a relationship with the same character');
    });

    it('should prevent duplicate relationships', async () => {
      const charA = await caller.project.createCharacter({
        name: 'Character A',
        projectId,
      });
      const charB = await caller.project.createCharacter({
        name: 'Character B',
        projectId,
      });

      await caller.project.createRelationship({
        characterIdA: charA.id,
        characterIdB: charB.id,
        relationshipType: 'friendship',
      });

      await expect(
        caller.project.createRelationship({
          characterIdA: charA.id,
          characterIdB: charB.id,
          relationshipType: 'rivalry',
        })
      ).rejects.toThrow('Relationship already exists between these characters');
    });
  });

  describe('deleteCharacter', () => {
    it('should remove associated relationships when deleting a character', async () => {
      const charA = await caller.project.createCharacter({
        name: 'Character A',
        projectId,
      });
      const charB = await caller.project.createCharacter({
        name: 'Character B',
        projectId,
      });

      await caller.project.createRelationship({
        characterIdA: charA.id,
        characterIdB: charB.id,
        relationshipType: 'friendship',
      });

      await caller.project.deleteCharacter({ id: charA.id });

      const rels = await caller.project.getRelationshipsForCharacter({
        characterId: charB.id,
      });

      expect(rels.length).toBe(0);
    });
  });

  describe('getCharacterStats', () => {
    it('should return stats for a character', async () => {
      const charA = await caller.project.createCharacter({
        name: 'TestChar',
        projectId,
      });

      const stats = await caller.project.getCharacterStats({
        characterId: charA.id,
      });

      expect(stats.characterId).toBe(charA.id);
      expect(stats.sceneCount).toBe(0);
      expect(stats.totalDialogueLines).toBe(0);
      expect(stats.relationshipCount).toBe(0);
    });
  });
});
