import { describe, it, expect, beforeEach, vi } from 'vitest';
import Fastify, { FastifyInstance } from 'fastify';
import conversationRoutes from './conversations.js';

function createMockDataStore(overrides: Record<string, any> = {}) {
  return {
    getCharacter: vi.fn().mockResolvedValue({ id: 'char-1', status: 'active' }),
    listConversations: vi.fn().mockResolvedValue([]),
    getConversationHistory: vi.fn().mockResolvedValue(null),
    deleteConversation: vi.fn().mockResolvedValue(false),
    ...overrides,
  } as any;
}

function createMockConversationEngine() {
  return {} as any;
}

async function buildApp(dataStoreOverrides: Record<string, any> = {}): Promise<{
  app: FastifyInstance;
  dataStore: any;
}> {
  const app = Fastify();
  const dataStore = createMockDataStore(dataStoreOverrides);
  await app.register(conversationRoutes, {
    dataStore,
    conversationEngine: createMockConversationEngine(),
  });
  return { app, dataStore };
}

describe('conversationRoutes', () => {
  describe('GET /characters/:id/conversations', () => {
    it('returns 404 when character not found', async () => {
      const { app } = await buildApp({ getCharacter: vi.fn().mockResolvedValue(null) });
      const res = await app.inject({ method: 'GET', url: '/characters/unknown/conversations' });
      expect(res.statusCode).toBe(404);
      expect(res.json()).toEqual({ success: false, error: 'Character not found: unknown' });
    });

    it('returns empty list when no conversations', async () => {
      const { app } = await buildApp();
      const res = await app.inject({ method: 'GET', url: '/characters/char-1/conversations' });
      expect(res.statusCode).toBe(200);
      expect(res.json()).toEqual({ success: true, data: [] });
    });

    it('returns conversation summaries', async () => {
      const conversations = [
        {
          telegramUserId: 123,
          userName: 'Alice',
          userInfo: { name: 'Alice' },
          totalMessages: 10,
          totalStarsSpent: 5,
          lastActivity: '2025-01-01T00:00:00.000Z',
        },
        {
          telegramUserId: 456,
          userInfo: { name: 'Bob' },
          totalMessages: 3,
          totalStarsSpent: 0,
          lastActivity: '2025-01-02T00:00:00.000Z',
        },
      ];
      const { app } = await buildApp({ listConversations: vi.fn().mockResolvedValue(conversations) });
      const res = await app.inject({ method: 'GET', url: '/characters/char-1/conversations' });
      expect(res.statusCode).toBe(200);
      const body = res.json();
      expect(body.success).toBe(true);
      expect(body.data).toHaveLength(2);
      expect(body.data[0].telegramUserId).toBe(123);
      expect(body.data[0].userName).toBe('Alice');
      expect(body.data[1].userName).toBe('Bob');
    });

    it('returns 500 on internal error', async () => {
      const { app } = await buildApp({
        getCharacter: vi.fn().mockRejectedValue(new Error('disk failure')),
      });
      const res = await app.inject({ method: 'GET', url: '/characters/char-1/conversations' });
      expect(res.statusCode).toBe(500);
      expect(res.json().success).toBe(false);
    });

    it('returns lastMessagePreview with first 50 chars of last message', async () => {
      const longContent = 'A'.repeat(80);
      const conversations = [
        {
          telegramUserId: 123,
          userName: 'Alice',
          userInfo: { name: 'Alice' },
          totalMessages: 2,
          totalStarsSpent: 0,
          lastActivity: '2025-01-01T00:00:00.000Z',
          messages: [
            { id: 'm1', role: 'user', content: 'first message', timestamp: '2025-01-01T00:00:00.000Z' },
            { id: 'm2', role: 'assistant', content: longContent, timestamp: '2025-01-01T00:01:00.000Z' },
          ],
        },
      ];
      const { app } = await buildApp({ listConversations: vi.fn().mockResolvedValue(conversations) });
      const res = await app.inject({ method: 'GET', url: '/characters/char-1/conversations' });
      expect(res.statusCode).toBe(200);
      const body = res.json();
      expect(body.data[0].lastMessagePreview).toBe('A'.repeat(50));
    });

    it('returns lastMessagePreview as null when no messages', async () => {
      const conversations = [
        {
          telegramUserId: 123,
          userName: 'Alice',
          userInfo: { name: 'Alice' },
          totalMessages: 0,
          totalStarsSpent: 0,
          lastActivity: '2025-01-01T00:00:00.000Z',
          messages: [],
        },
      ];
      const { app } = await buildApp({ listConversations: vi.fn().mockResolvedValue(conversations) });
      const res = await app.inject({ method: 'GET', url: '/characters/char-1/conversations' });
      expect(res.statusCode).toBe(200);
      const body = res.json();
      expect(body.data[0].lastMessagePreview).toBeNull();
    });

    it('returns full content as preview when message is shorter than 50 chars', async () => {
      const conversations = [
        {
          telegramUserId: 123,
          userName: 'Alice',
          userInfo: { name: 'Alice' },
          totalMessages: 1,
          totalStarsSpent: 0,
          lastActivity: '2025-01-01T00:00:00.000Z',
          messages: [
            { id: 'm1', role: 'user', content: 'Hello!', timestamp: '2025-01-01T00:00:00.000Z' },
          ],
        },
      ];
      const { app } = await buildApp({ listConversations: vi.fn().mockResolvedValue(conversations) });
      const res = await app.inject({ method: 'GET', url: '/characters/char-1/conversations' });
      expect(res.statusCode).toBe(200);
      const body = res.json();
      expect(body.data[0].lastMessagePreview).toBe('Hello!');
    });
  });

  describe('GET /characters/:id/conversations/:userId', () => {
    it('returns 404 when character not found', async () => {
      const { app } = await buildApp({ getCharacter: vi.fn().mockResolvedValue(null) });
      const res = await app.inject({ method: 'GET', url: '/characters/unknown/conversations/123' });
      expect(res.statusCode).toBe(404);
    });

    it('returns 400 for non-numeric userId', async () => {
      const { app } = await buildApp();
      const res = await app.inject({ method: 'GET', url: '/characters/char-1/conversations/abc' });
      expect(res.statusCode).toBe(400);
      expect(res.json().error).toContain('Invalid userId');
    });

    it('returns 404 when conversation not found', async () => {
      const { app } = await buildApp();
      const res = await app.inject({ method: 'GET', url: '/characters/char-1/conversations/999' });
      expect(res.statusCode).toBe(404);
      expect(res.json().error).toContain('Conversation not found');
    });

    it('returns conversation history', async () => {
      const history = {
        characterId: 'char-1',
        telegramUserId: 123,
        messages: [{ id: 'm1', role: 'user', content: 'hello', timestamp: '2025-01-01T00:00:00.000Z' }],
        totalMessages: 1,
        totalStarsSpent: 0,
        lastActivity: '2025-01-01T00:00:00.000Z',
        userInfo: {},
      };
      const { app } = await buildApp({
        getConversationHistory: vi.fn().mockResolvedValue(history),
      });
      const res = await app.inject({ method: 'GET', url: '/characters/char-1/conversations/123' });
      expect(res.statusCode).toBe(200);
      const body = res.json();
      expect(body.success).toBe(true);
      expect(body.data.characterId).toBe('char-1');
      expect(body.data.messages).toHaveLength(1);
    });
  });

  describe('DELETE /characters/:id/conversations/:userId', () => {
    it('returns 404 when character not found', async () => {
      const { app } = await buildApp({ getCharacter: vi.fn().mockResolvedValue(null) });
      const res = await app.inject({ method: 'DELETE', url: '/characters/unknown/conversations/123' });
      expect(res.statusCode).toBe(404);
    });

    it('returns 400 for non-numeric userId', async () => {
      const { app } = await buildApp();
      const res = await app.inject({ method: 'DELETE', url: '/characters/char-1/conversations/abc' });
      expect(res.statusCode).toBe(400);
    });

    it('returns 404 when conversation not found', async () => {
      const { app } = await buildApp();
      const res = await app.inject({ method: 'DELETE', url: '/characters/char-1/conversations/999' });
      expect(res.statusCode).toBe(404);
    });

    it('deletes conversation successfully', async () => {
      const { app } = await buildApp({
        deleteConversation: vi.fn().mockResolvedValue(true),
      });
      const res = await app.inject({ method: 'DELETE', url: '/characters/char-1/conversations/123' });
      expect(res.statusCode).toBe(200);
      const body = res.json();
      expect(body.success).toBe(true);
      expect(body.data.message).toContain('123');
    });

    it('returns 500 on internal error', async () => {
      const { app } = await buildApp({
        deleteConversation: vi.fn().mockRejectedValue(new Error('io error')),
      });
      const res = await app.inject({ method: 'DELETE', url: '/characters/char-1/conversations/123' });
      expect(res.statusCode).toBe(500);
      expect(res.json().success).toBe(false);
    });
  });
});
