import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { CreditBalanceDialog } from '../../src/components/CreditBalanceDialog';
import { creditBalanceClient } from '../../src/api/creditBalanceClient';

// 完整的mock响应对象
const createMockResponse = (status: number, data?: any) => ({
  status,
  ok: status >= 200 && status < 300,
  body: null,
  bodyUsed: false,
  statusText: status === 200 ? 'OK' : status === 201 ? 'Created' : status === 204 ? 'No Content' : 'Error',
  headers: new Headers(),
  url: '',
  redirected: false,
  type: 'basic' as ResponseType,
  json: async () => data || {},
  text: async () => '',
  blob: async () => new Blob(),
  arrayBuffer: async () => new ArrayBuffer(0),
  formData: async () => new FormData(),
  clone: function() { return this; }
});

// Mock API client
vi.mock('../../src/api/creditBalanceClient', () => {
  const mockCreditBalanceClient = {
    ':userId': {
      $get: vi.fn(() => Promise.resolve({ status: 200, json: async () => ({}) })),
      $put: vi.fn(() => Promise.resolve({ status: 200, json: async () => ({}) })),
      adjust: {
        $post: vi.fn(() => Promise.resolve({ status: 200, json: async () => ({}) }))
      },
      logs: {
        $get: vi.fn(() => Promise.resolve({ status: 200, json: async () => ({ data: [], pagination: { total: 0, page: 1, pageSize: 10 } }) }))
      }
    },
    checkout: {
      $post: vi.fn(() => Promise.resolve({ status: 200, json: async () => ({}) }))
    }
  };

  const mockCreditBalanceClientManager = {
    get: vi.fn(() => mockCreditBalanceClient),
    init: vi.fn(() => mockCreditBalanceClient),
    reset: vi.fn(),
  };

  return {
    creditBalanceClient: mockCreditBalanceClient,
    creditBalanceClientManager: mockCreditBalanceClientManager,
  };
});

// Mock toast
vi.mock('sonner', () => ({
  toast: {
    success: vi.fn(() => {}),
    error: vi.fn(() => {}),
  },
}));

const createTestQueryClient = () =>
  new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
      },
    },
  });

const renderWithProviders = (component: React.ReactElement) => {
  const queryClient = createTestQueryClient();
  return render(
    <QueryClientProvider client={queryClient}>
      {component as any}
    </QueryClientProvider>
  );
};

describe('信用额度管理对话框集成测试', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('应该完成完整的信用额度管理流程', async () => {
    const mockBalanceData = {
      id: 1,
      userId: 123,
      totalLimit: 10000,
      usedAmount: 2500,
      availableAmount: 7500,
      isEnabled: 1,
      createdAt: '2024-01-01T00:00:00Z',
      updatedAt: '2024-01-01T00:00:00Z'
    };

    const mockLogsData = {
      data: [
        {
          id: 1,
          userId: 123,
          changeType: 'SET_LIMIT',
          changeAmount: 10000,
          beforeTotal: 0,
          afterTotal: 10000,
          beforeUsed: 0,
          afterUsed: 0,
          remark: '设置初始信用额度',
          createdAt: '2024-01-01T00:00:00Z'
        },
        {
          id: 2,
          userId: 123,
          changeType: 'ADJUSTMENT',
          changeAmount: 2000,
          beforeTotal: 10000,
          afterTotal: 12000,
          beforeUsed: 0,
          afterUsed: 0,
          remark: '增加额度',
          createdAt: '2024-01-02T00:00:00Z'
        },
        {
          id: 3,
          userId: 123,
          changeType: 'PAYMENT',
          changeAmount: -500,
          beforeTotal: 12000,
          afterTotal: 12000,
          beforeUsed: 0,
          afterUsed: 500,
          remark: '订单支付',
          createdAt: '2024-01-03T00:00:00Z'
        },
        {
          id: 4,
          userId: 123,
          changeType: 'PAYMENT',
          changeAmount: -300,
          beforeTotal: 12000,
          afterTotal: 12000,
          beforeUsed: 500,
          afterUsed: 800,
          remark: '订单支付',
          createdAt: '2024-01-04T00:00:00Z'
        },
        {
          id: 5,
          userId: 123,
          changeType: 'PAYMENT',
          changeAmount: -200,
          beforeTotal: 12000,
          afterTotal: 12000,
          beforeUsed: 800,
          afterUsed: 1000,
          remark: '订单支付',
          createdAt: '2024-01-05T00:00:00Z'
        },
        {
          id: 6,
          userId: 123,
          changeType: 'PAYMENT',
          changeAmount: -400,
          beforeTotal: 12000,
          afterTotal: 12000,
          beforeUsed: 1000,
          afterUsed: 1400,
          remark: '订单支付',
          createdAt: '2024-01-06T00:00:00Z'
        },
        {
          id: 7,
          userId: 123,
          changeType: 'PAYMENT',
          changeAmount: -600,
          beforeTotal: 12000,
          afterTotal: 12000,
          beforeUsed: 1400,
          afterUsed: 2000,
          remark: '订单支付',
          createdAt: '2024-01-07T00:00:00Z'
        },
        {
          id: 8,
          userId: 123,
          changeType: 'PAYMENT',
          changeAmount: -700,
          beforeTotal: 12000,
          afterTotal: 12000,
          beforeUsed: 2000,
          afterUsed: 2700,
          remark: '订单支付',
          createdAt: '2024-01-08T00:00:00Z'
        },
        {
          id: 9,
          userId: 123,
          changeType: 'PAYMENT',
          changeAmount: -800,
          beforeTotal: 12000,
          afterTotal: 12000,
          beforeUsed: 2700,
          afterUsed: 3500,
          remark: '订单支付',
          createdAt: '2024-01-09T00:00:00Z'
        },
        {
          id: 10,
          userId: 123,
          changeType: 'PAYMENT',
          changeAmount: -900,
          beforeTotal: 12000,
          afterTotal: 12000,
          beforeUsed: 3500,
          afterUsed: 4400,
          remark: '订单支付',
          createdAt: '2024-01-10T00:00:00Z'
        },
        {
          id: 11,
          userId: 123,
          changeType: 'CHECKOUT',
          changeAmount: 1000,
          beforeTotal: 12000,
          afterTotal: 12000,
          beforeUsed: 4400,
          afterUsed: 3400,
          remark: '结账恢复额度',
          createdAt: '2024-01-11T00:00:00Z'
        },
        {
          id: 12,
          userId: 123,
          changeType: 'CHECKOUT',
          changeAmount: 500,
          beforeTotal: 12000,
          afterTotal: 12000,
          beforeUsed: 3400,
          afterUsed: 2900,
          remark: '结账恢复额度',
          createdAt: '2024-01-12T00:00:00Z'
        }
      ],
      pagination: {
        total: 12,
        page: 1,
        pageSize: 10
      }
    };

    const { toast } = await import('sonner');

    // Mock initial balance data
    (creditBalanceClient[':userId'].$get as any).mockResolvedValue(
      createMockResponse(200, mockBalanceData)
    );

    // Mock logs data
    (creditBalanceClient[':userId'].logs.$get as any).mockResolvedValue(
      createMockResponse(200, mockLogsData)
    );

    // Mock update operations
    (creditBalanceClient[':userId'].$put as any).mockResolvedValue(
      createMockResponse(200, { success: true })
    );

    (creditBalanceClient[':userId'].adjust.$post as any).mockResolvedValue(
      createMockResponse(200, { success: true })
    );

    (creditBalanceClient.checkout.$post as any).mockResolvedValue(
      createMockResponse(200, { success: true })
    );

    renderWithProviders(
      <CreditBalanceDialog
        userId={123}
        userName="测试用户"
        open={true}
        onOpenChange={() => {}}
      />
    );

    // 1. 验证初始数据加载
    await waitFor(() => {
      expect(creditBalanceClient[':userId'].$get).toHaveBeenCalledWith({
        param: { userId: '123' }
      });
    });

    // 等待数据加载完成
    await waitFor(() => {
      expect(screen.getByTestId('total-limit-card')).toBeInTheDocument();
      expect(screen.getByTestId('used-amount-card')).toBeInTheDocument();
      expect(screen.getByTestId('available-amount-card')).toBeInTheDocument();
    });

    // 验证额度概览显示
    expect(screen.getByText('总额度')).toBeInTheDocument();
    expect(screen.getByText('已用额度')).toBeInTheDocument();
    expect(screen.getByText('可用额度')).toBeInTheDocument();

    // 2. 测试设置额度功能
    // 切换到额度操作标签页
    const operationsTab = screen.getByText('额度操作');

    // 使用userEvent模拟真实用户点击
    const user = userEvent.setup();
    await user.click(operationsTab);

    // 等待表单渲染 - 使用test ID查找表单卡片
    await waitFor(() => {
      expect(screen.getByTestId('set-limit-form-card')).toBeInTheDocument();
    });

    // 使用test ID查找输入框
    const totalLimitInput = screen.getByTestId('total-limit-input');
    fireEvent.change(totalLimitInput, { target: { value: '15000' } });

    // 提交设置额度表单 - 使用test ID查找按钮
    const setLimitButton = screen.getByTestId('set-limit-button');
    fireEvent.click(setLimitButton);

    // 等待mutation完成
    await waitFor(() => {
      expect(creditBalanceClient[':userId'].$put).toHaveBeenCalled();
    }, { timeout: 5000 });

    // 验证调用参数
    expect(creditBalanceClient[':userId'].$put).toHaveBeenCalledWith({
      param: { userId: '123' },
      json: {
        totalLimit: 15000,
        remark: ''
      }
    });
    expect(toast.success).toHaveBeenCalledWith('额度设置成功');

    // 3. 测试调整额度功能
    // 切换到调整额度标签 - 使用test ID查找表单卡片
    await waitFor(() => {
      expect(screen.getByTestId('adjust-limit-form-card')).toBeInTheDocument();
    });

    // 填写调整额度表单 - 使用test ID查找输入框
    const adjustAmountInput = screen.getByTestId('adjust-amount-input');
    fireEvent.change(adjustAmountInput, { target: { value: '3000' } });

    // 提交调整额度表单 - 使用test ID查找按钮
    const adjustLimitButton = screen.getByTestId('adjust-limit-button');
    fireEvent.click(adjustLimitButton);

    await waitFor(() => {
      expect(creditBalanceClient[':userId'].adjust.$post).toHaveBeenCalledWith({
        param: { userId: '123' },
        json: {
          adjustAmount: 3000,
          remark: ''
        }
      });
      expect(toast.success).toHaveBeenCalledWith('额度调整成功');
    });

    // 4. 测试结账恢复额度功能
    // 切换到结账恢复标签 - 使用test ID查找表单卡片
    await waitFor(() => {
      expect(screen.getByTestId('checkout-form-card')).toBeInTheDocument();
    });

    // 填写结账恢复表单 - 使用test ID查找输入框
    const checkoutAmountInput = screen.getByTestId('checkout-amount-input');
    fireEvent.change(checkoutAmountInput, { target: { value: '1000' } });

    // 填写关联ID
    fireEvent.change(screen.getByLabelText('关联ID（订单号等）'), { target: { value: 'ORDER-12345' } });

    // 提交结账恢复表单 - 使用test ID查找按钮
    const checkoutButton = screen.getByTestId('checkout-button');
    fireEvent.click(checkoutButton);

    await waitFor(() => {
      expect(creditBalanceClient.checkout.$post).toHaveBeenCalledWith({
        json: {
          userId: 123,
          amount: 1000,
          referenceId: 'ORDER-12345',
          remark: ''
        }
      });
      expect(toast.success).toHaveBeenCalledWith('结账恢复额度成功');
    });

    // 5. 测试变更记录查询
    // 切换到变更记录标签页
    const logsTab = screen.getByText('变更记录');
    await user.click(logsTab);

    // 等待logs查询被触发
    await waitFor(() => {
      expect(creditBalanceClient[':userId'].logs.$get).toHaveBeenCalledWith({
        param: { userId: '123' },
        query: {
          page: 1,
          pageSize: 10
        }
      });
    }, { timeout: 5000 });

    // 验证变更记录显示
    expect(screen.getByText('变更类型')).toBeInTheDocument();
    expect(screen.getByText('变更金额')).toBeInTheDocument();
    expect(screen.getByText('时间')).toBeInTheDocument();

    // 6. 测试分页功能
    // 等待分页控件渲染
    await waitFor(() => {
      expect(screen.getByText('下一页')).toBeInTheDocument();
    }, { timeout: 5000 });

    // 点击下一页
    fireEvent.click(screen.getByText('下一页'));

    await waitFor(() => {
      expect(creditBalanceClient[':userId'].logs.$get).toHaveBeenCalledWith({
        param: { userId: '123' },
        query: {
          page: 2,
          pageSize: 10
        }
      });
    });
  });

  it('应该处理表单验证错误', async () => {
    const mockBalanceData = {
      id: 1,
      userId: 123,
      totalLimit: 10000,
      usedAmount: 2500,
      availableAmount: 7500,
      isEnabled: 1,
      createdAt: '2024-01-01T00:00:00Z',
      updatedAt: '2024-01-01T00:00:00Z'
    };

    (creditBalanceClient[':userId'].$get as any).mockResolvedValue(
      createMockResponse(200, mockBalanceData)
    );

    renderWithProviders(
      <CreditBalanceDialog
        userId={123}
        userName="测试用户"
        open={true}
        onOpenChange={() => {}}
      />
    );

    // 等待初始数据加载
    await waitFor(() => {
      expect(screen.getByTestId('total-limit-card')).toBeInTheDocument();
    });

    // 切换到额度操作标签页
    const user = userEvent.setup();
    const operationsTab = screen.getByText('额度操作');
    await user.click(operationsTab);

    // 等待表单渲染 - 使用test ID查找表单卡片
    await waitFor(() => {
      expect(screen.getByTestId('set-limit-form-card')).toBeInTheDocument();
    });

    // 直接输入无效值 -100，不测试空字符串
    const totalLimitInput = screen.getByTestId('total-limit-input');
    await userEvent.clear(totalLimitInput);
    await userEvent.type(totalLimitInput, '-100');

    // 尝试提交无效表单 - 直接点击设置额度按钮
    const setLimitButton = screen.getByTestId('set-limit-button');
    await userEvent.click(setLimitButton);

    // 等待并检查验证错误 - 应该显示"总额度必须大于等于0"
    await waitFor(() => {
      // 检查是否有包含"总额度必须大于等于0"的错误消息
      expect(screen.getByText('总额度必须大于等于0')).toBeInTheDocument();
    }, { timeout: 5000 });
  });

  it('应该处理获取信用额度API错误', async () => {
    const { toast } = await import('sonner');

    // Mock API error
    (creditBalanceClient[':userId'].$get as any).mockRejectedValue(
      new Error('获取信用额度失败')
    );

    renderWithProviders(
      <CreditBalanceDialog
        userId={123}
        userName="测试用户"
        open={true}
        onOpenChange={() => {}}
      />
    );

    // 应该显示错误提示
    await waitFor(() => {
      expect(toast.error).toHaveBeenCalledWith('获取信用额度失败: 获取信用额度失败');
    }, { timeout: 3000 });
  });

  it('应该处理设置额度API错误', async () => {
    const { toast } = await import('sonner');

    const mockBalanceData = {
      id: 1,
      userId: 123,
      totalLimit: 10000,
      usedAmount: 2500,
      availableAmount: 7500,
      isEnabled: 1,
      createdAt: '2024-01-01T00:00:00Z',
      updatedAt: '2024-01-01T00:00:00Z'
    };

    // Mock 成功获取额度数据
    (creditBalanceClient[':userId'].$get as any).mockResolvedValue(
      createMockResponse(200, mockBalanceData)
    );

    // Mock 设置额度API错误
    (creditBalanceClient[':userId'].$put as any).mockRejectedValue(
      new Error('设置额度失败')
    );

    renderWithProviders(
      <CreditBalanceDialog
        userId={123}
        userName="测试用户"
        open={true}
        onOpenChange={() => {}}
      />
    );

    // 等待初始数据加载
    await waitFor(() => {
      expect(screen.getByTestId('total-limit-card')).toBeInTheDocument();
    });

    // 切换到额度操作标签页
    const user = userEvent.setup();
    const operationsTab = screen.getByText('额度操作');
    await user.click(operationsTab);

    // 等待表单渲染
    await waitFor(() => {
      expect(screen.getByTestId('set-limit-form-card')).toBeInTheDocument();
    });

    // 填写表单 - 使用test ID查找输入框
    const totalLimitInput = screen.getByTestId('total-limit-input');
    fireEvent.change(totalLimitInput, { target: { value: '15000' } });

    // 提交表单 - 使用test ID查找按钮
    const setLimitButton = screen.getByTestId('set-limit-button');
    fireEvent.click(setLimitButton);

    await waitFor(() => {
      expect(toast.error).toHaveBeenCalledWith('设置失败: 设置额度失败');
    });
  });

  it('应该支持多租户场景', async () => {
    const mockBalanceData = {
      id: 1,
      userId: 123,
      totalLimit: 10000,
      usedAmount: 2500,
      availableAmount: 7500,
      isEnabled: 1,
      createdAt: '2024-01-01T00:00:00Z',
      updatedAt: '2024-01-01T00:00:00Z'
    };

    (creditBalanceClient[':userId'].$get as any).mockResolvedValue(
      createMockResponse(200, mockBalanceData)
    );

    renderWithProviders(
      <CreditBalanceDialog
        userId={123}
        userName="测试用户"
        open={true}
        onOpenChange={() => {}}
        tenantId={456}
      />
    );

    await waitFor(() => {
      expect(creditBalanceClient[':userId'].$get).toHaveBeenCalledWith({
        param: { userId: '123' }
      });
    });

    // 等待初始数据加载
    await waitFor(() => {
      expect(screen.getByTestId('total-limit-card')).toBeInTheDocument();
    });

    // 测试带租户ID的操作
    (creditBalanceClient[':userId'].$put as any).mockResolvedValue(
      createMockResponse(200, { success: true })
    );

    // 切换到额度操作标签页
    const user = userEvent.setup();
    const operationsTab = screen.getByText('额度操作');
    await user.click(operationsTab);

    // 等待表单渲染
    await waitFor(() => {
      expect(screen.getByTestId('set-limit-form-card')).toBeInTheDocument();
    });

    // 填写表单 - 使用test ID查找输入框
    const totalLimitInput = screen.getByTestId('total-limit-input');
    fireEvent.change(totalLimitInput, { target: { value: '15000' } });

    // 提交表单 - 使用test ID查找按钮
    const setLimitButton = screen.getByTestId('set-limit-button');
    fireEvent.click(setLimitButton);

    await waitFor(() => {
      expect(creditBalanceClient[':userId'].$put).toHaveBeenCalledWith({
        param: { userId: '123' },
        json: {
          totalLimit: 15000,
          remark: ''
        }
      });
    });
  });

  it('应该处理欠款警告显示', async () => {
    const mockBalanceData = {
      id: 1,
      userId: 123,
      totalLimit: 10000,
      usedAmount: 12000, // 使用量超出额度，应该显示欠款警告
      availableAmount: -2000, // 负值表示欠款
      isEnabled: 1,
      createdAt: '2024-01-01T00:00:00Z',
      updatedAt: '2024-01-01T00:00:00Z'
    };

    (creditBalanceClient[':userId'].$get as any).mockResolvedValue(
      createMockResponse(200, mockBalanceData)
    );

    renderWithProviders(
      <CreditBalanceDialog
        userId={123}
        userName="测试用户"
        open={true}
        onOpenChange={() => {}}
      />
    );

    // 等待数据加载
    await waitFor(() => {
      expect(screen.getByTestId('total-limit-card')).toBeInTheDocument();
    });

    // 应该显示欠款警告
    expect(screen.getByText('用户存在欠款')).toBeInTheDocument();
    expect(screen.getByText(/用户已超出信用额度 ¥2000\.00/)).toBeInTheDocument();
  });

  it('应该处理用户未开通额度账户的场景', async () => {
    const { toast } = await import('sonner');

    // Mock 404响应 - 用户额度账户不存在
    (creditBalanceClient[':userId'].$get as any).mockResolvedValue(
      createMockResponse(404, { message: '用户额度账户不存在' })
    );

    // Mock 设置额度API成功响应（用于开通额度）
    (creditBalanceClient[':userId'].$put as any).mockResolvedValue(
      createMockResponse(200, { success: true })
    );

    renderWithProviders(
      <CreditBalanceDialog
        userId={123}
        userName="测试用户"
        open={true}
        onOpenChange={() => {}}
      />
    );

    // 等待数据加载完成（404不会抛出错误）
    await waitFor(() => {
      // 应该显示用户未开通额度的提示 - 使用getAllByText处理多个元素
      const alerts = screen.getAllByText('用户尚未开通信用额度');
      expect(alerts.length).toBeGreaterThan(0);

      // 检查开通额度表单是否显示
      expect(screen.getByTestId('open-credit-account-card')).toBeInTheDocument();
    });

    // 验证没有显示错误toast（404是正常情况）
    expect(toast.error).not.toHaveBeenCalled();

    // 应该显示开通额度表单
    expect(screen.getByTestId('open-credit-account-card')).toBeInTheDocument();
    // 使用getAllByText处理多个"开通信用额度"元素
    const openCreditTexts = screen.getAllByText('开通信用额度');
    expect(openCreditTexts.length).toBeGreaterThan(0);
    expect(screen.getByTestId('open-credit-total-limit-input')).toBeInTheDocument();
    expect(screen.getByTestId('open-credit-account-button')).toBeInTheDocument();

    // 测试开通额度功能
    const totalLimitInput = screen.getByTestId('open-credit-total-limit-input');
    fireEvent.change(totalLimitInput, { target: { value: '5000' } });

    const openCreditButton = screen.getByTestId('open-credit-account-button');
    fireEvent.click(openCreditButton);

    // 等待API调用
    await waitFor(() => {
      expect(creditBalanceClient[':userId'].$put).toHaveBeenCalledWith({
        param: { userId: '123' },
        json: {
          totalLimit: 5000,
          remark: ''
        }
      });
    });

    // 应该显示开通成功的toast
    expect(toast.success).toHaveBeenCalledWith('信用额度开通成功');

    // 测试切换到其他标签页时的提示
    const user = userEvent.setup();

    // 切换到额度操作标签页
    const operationsTab = screen.getByText('额度操作');
    await user.click(operationsTab);

    // 应该显示提示信息，而不是操作表单
    await waitFor(() => {
      expect(screen.getByText('请先在"额度概览"标签页中为该用户开通信用额度，然后才能进行额度操作。')).toBeInTheDocument();
    });

    // 切换到变更记录标签页
    const logsTab = screen.getByText('变更记录');
    await user.click(logsTab);

    // 应该显示提示信息，而不是记录表格
    await waitFor(() => {
      expect(screen.getByText('请先在"额度概览"标签页中为该用户开通信用额度，然后才能查看变更记录。')).toBeInTheDocument();
    });

    // 验证没有调用变更记录API（因为用户没有额度账户）
    expect(creditBalanceClient[':userId'].logs.$get).not.toHaveBeenCalled();
  });
});