import { renderHook, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import { useModels, useCreateModel, useUpdateModel, useDeleteModel } from '@/hooks/useModels';
import type { Model, CreateModelInput, UpdateModelInput } from '@/types';
import { MessagePlugin } from 'tdesign-react';

// Mock MessagePlugin
vi.mock('tdesign-react', () => ({
  MessagePlugin: {
    success: vi.fn(),
    error: vi.fn(),
  },
}));

// Test data
const mockModels: Model[] = [
  {
    id: 'model-1',
    providerId: 'provider-1',
    modelName: 'gpt-4o',
    enabled: true,
    createdAt: '2026-01-01T00:00:00Z',
  },
  {
    id: 'model-2',
    providerId: 'provider-1',
    modelName: 'gpt-4o-mini',
    enabled: true,
    createdAt: '2026-01-02T00:00:00Z',
  },
];

const mockFilteredModels: Model[] = [
  {
    id: 'model-3',
    providerId: 'provider-2',
    modelName: 'claude-sonnet-4-5',
    enabled: true,
    createdAt: '2026-02-01T00:00:00Z',
  },
];

const mockCreatedModel: Model = {
  id: 'model-4',
  providerId: 'provider-1',
  modelName: 'gpt-4.1',
  enabled: true,
  createdAt: '2026-03-01T00:00:00Z',
};

// MSW handlers
const handlers = [
  http.get('/api/models', ({ request }) => {
    const url = new URL(request.url);
    const providerId = url.searchParams.get('provider_id');
    if (providerId === 'provider-2') {
      return HttpResponse.json(mockFilteredModels);
    }
    return HttpResponse.json(mockModels);
  }),
  http.post('/api/models', async ({ request }) => {
    const body = await request.json() as Record<string, unknown>;
    return HttpResponse.json({
      ...mockCreatedModel,
      ...body,
    });
  }),
  http.put('/api/models/:id', async ({ request, params }) => {
    const body = await request.json() as Record<string, unknown>;
    const existing = mockModels.find((m) => m.id === params['id']);
    return HttpResponse.json({ ...existing, ...body });
  }),
  http.delete('/api/models/:id', () => {
    return new HttpResponse(null, { status: 204 });
  }),
];

const server = setupServer(...handlers);

function createTestQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });
}

function createWrapper() {
  const testQueryClient = createTestQueryClient();
  return function Wrapper({ children }: { children: React.ReactNode }) {
    return (
      <QueryClientProvider client={testQueryClient}>
        {children}
      </QueryClientProvider>
    );
  };
}

beforeAll(() => server.listen());
afterEach(() => {
  server.resetHandlers();
  vi.clearAllMocks();
});
afterAll(() => server.close());

describe('useModels', () => {
  it('fetches model list', async () => {
    const { result } = renderHook(() => useModels(), {
      wrapper: createWrapper(),
    });

    await waitFor(() => expect(result.current.isSuccess).toBe(true));

    expect(result.current.data).toEqual(mockModels);
    expect(result.current.data).toHaveLength(2);
    expect(result.current.data![0]!.modelName).toBe('gpt-4o');
  });

  it('with providerId passes it to API and returns filtered models', async () => {
    const { result } = renderHook(() => useModels('provider-2'), {
      wrapper: createWrapper(),
    });

    await waitFor(() => expect(result.current.isSuccess).toBe(true));

    expect(result.current.data).toEqual(mockFilteredModels);
    expect(result.current.data).toHaveLength(1);
    expect(result.current.data![0]!.modelName).toBe('claude-sonnet-4-5');
  });
});

describe('useCreateModel', () => {
  it('calls API and invalidates model queries', async () => {
    const queryClient = createTestQueryClient();
    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

    function Wrapper({ children }: { children: React.ReactNode }) {
      return (
        <QueryClientProvider client={queryClient}>
          {children}
        </QueryClientProvider>
      );
    }

    const { result } = renderHook(() => useCreateModel(), {
      wrapper: Wrapper,
    });

    const input: CreateModelInput = {
      id: 'model-4',
      providerId: 'provider-1',
      modelName: 'gpt-4.1',
      enabled: true,
    };

    result.current.mutate(input);

    await waitFor(() => expect(result.current.isSuccess).toBe(true));

    expect(result.current.data).toMatchObject({
      id: 'model-4',
      modelName: 'gpt-4.1',
    });
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['models'] });
    expect(MessagePlugin.success).toHaveBeenCalledWith('模型创建成功');
  });

  it('calls message.error on failure', async () => {
    server.use(
      http.post('/api/models', () => {
        return HttpResponse.json({ message: '创建失败' }, { status: 500 });
      }),
    );

    const { result } = renderHook(() => useCreateModel(), {
      wrapper: createWrapper(),
    });

    const input: CreateModelInput = {
      id: 'model-4',
      providerId: 'provider-1',
      modelName: 'gpt-4.1',
      enabled: true,
    };

    result.current.mutate(input);

    await waitFor(() => expect(result.current.isError).toBe(true));
    expect(MessagePlugin.error).toHaveBeenCalled();
  });
});

describe('useUpdateModel', () => {
  it('calls API and invalidates model queries', async () => {
    const queryClient = createTestQueryClient();
    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

    function Wrapper({ children }: { children: React.ReactNode }) {
      return (
        <QueryClientProvider client={queryClient}>
          {children}
        </QueryClientProvider>
      );
    }

    const { result } = renderHook(() => useUpdateModel(), {
      wrapper: Wrapper,
    });

    const input: UpdateModelInput = { modelName: 'gpt-4o-updated' };

    result.current.mutate({ id: 'model-1', input });

    await waitFor(() => expect(result.current.isSuccess).toBe(true));

    expect(result.current.data).toMatchObject({
      modelName: 'gpt-4o-updated',
    });
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['models'] });
    expect(MessagePlugin.success).toHaveBeenCalledWith('模型更新成功');
  });

  it('calls message.error on failure', async () => {
    server.use(
      http.put('/api/models/:id', () => {
        return HttpResponse.json({ message: '更新失败' }, { status: 500 });
      }),
    );

    const { result } = renderHook(() => useUpdateModel(), {
      wrapper: createWrapper(),
    });

    result.current.mutate({ id: 'model-1', input: { modelName: 'Updated' } });

    await waitFor(() => expect(result.current.isError).toBe(true));
    expect(MessagePlugin.error).toHaveBeenCalled();
  });
});

describe('useDeleteModel', () => {
  it('calls API and invalidates model queries', async () => {
    const queryClient = createTestQueryClient();
    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

    function Wrapper({ children }: { children: React.ReactNode }) {
      return (
        <QueryClientProvider client={queryClient}>
          {children}
        </QueryClientProvider>
      );
    }

    const { result } = renderHook(() => useDeleteModel(), {
      wrapper: Wrapper,
    });

    result.current.mutate('model-1');

    await waitFor(() => expect(result.current.isSuccess).toBe(true));

    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['models'] });
    expect(MessagePlugin.success).toHaveBeenCalledWith('模型删除成功');
  });

  it('calls message.error on failure', async () => {
    server.use(
      http.delete('/api/models/:id', () => {
        return HttpResponse.json({ message: '删除失败' }, { status: 500 });
      }),
    );

    const { result } = renderHook(() => useDeleteModel(), {
      wrapper: createWrapper(),
    });

    result.current.mutate('model-1');

    await waitFor(() => expect(result.current.isError).toBe(true));
    expect(MessagePlugin.error).toHaveBeenCalled();
  });
});
