import React from 'react';
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 { toast } from 'sonner';

import { BatchSpecCreatorInline } from '../../src/components/BatchSpecCreatorInline';

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

describe('BatchSpecCreatorInline', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  const renderComponent = (props = {}) => {
    return render(<BatchSpecCreatorInline {...props} />);
  };

  it('应该正确渲染初始状态', () => {
    renderComponent();

    expect(screen.getByText('批量创建规格')).toBeInTheDocument();
    expect(screen.getByText('添加多个商品规格，创建后将作为子商品批量生成')).toBeInTheDocument();
    expect(screen.getByLabelText('规格名称 *')).toBeInTheDocument();
    expect(screen.getByLabelText('价格')).toBeInTheDocument();
    expect(screen.getByLabelText('成本价')).toBeInTheDocument();
    expect(screen.getByLabelText('库存')).toBeInTheDocument();
    expect(screen.getByText('添加')).toBeInTheDocument();
    expect(screen.getByText('快速模板')).toBeInTheDocument();
    expect(screen.getByText('暂无规格')).toBeInTheDocument();
  });

  it('应该显示初始规格', () => {
    const initialSpecs = [
      { name: '红色', price: 100, costPrice: 80, stock: 50, sort: 1 },
      { name: '蓝色', price: 110, costPrice: 85, stock: 30, sort: 2 }
    ];

    renderComponent({ initialSpecs });

    expect(screen.getByDisplayValue('红色')).toBeInTheDocument();
    expect(screen.getByDisplayValue('蓝色')).toBeInTheDocument();
    expect(screen.getByDisplayValue('100')).toBeInTheDocument();
    expect(screen.getByDisplayValue('110')).toBeInTheDocument();
    expect(screen.getByDisplayValue('50')).toBeInTheDocument();
    expect(screen.getByDisplayValue('30')).toBeInTheDocument();
    expect(screen.getByText('规格数量')).toBeInTheDocument();
    expect(screen.getByText('2')).toBeInTheDocument(); // 规格数量
  });

  it('应该添加新规格', async () => {
    const user = userEvent.setup();
    const onSpecsChange = vi.fn();
    renderComponent({ onSpecsChange });

    // 填写规格信息 - 使用userEvent更接近真实用户交互
    await user.type(screen.getByLabelText('规格名称 *'), '测试规格');
    await user.type(screen.getByLabelText('价格'), '150');
    await user.type(screen.getByLabelText('成本价'), '120');
    await user.type(screen.getByLabelText('库存'), '25');

    // 点击添加按钮
    await user.click(screen.getByText('添加'));

    // 验证toast被调用
    expect(toast.success).toHaveBeenCalledWith('规格已添加');

    // 验证回调被调用
    expect(onSpecsChange).toHaveBeenCalledWith([
      expect.objectContaining({
        name: '测试规格',
        price: 150,
        costPrice: 120,
        stock: 25,
        sort: 0
      })
    ]);

    // 验证表单被重置
    expect(screen.getByLabelText('规格名称 *')).toHaveValue('');
  });

  it('应该验证必填字段', () => {
    renderComponent();

    // 当规格名称为空时，添加按钮应该被禁用
    const addButton = screen.getByText('添加');
    expect(addButton).toBeDisabled();

    // 填写规格名称后，按钮应该启用
    fireEvent.change(screen.getByLabelText('规格名称 *'), { target: { value: '测试规格' } });
    expect(addButton).not.toBeDisabled();

    // 清除规格名称后，按钮应该再次被禁用
    fireEvent.change(screen.getByLabelText('规格名称 *'), { target: { value: '' } });
    expect(addButton).toBeDisabled();
  });

  it('应该验证价格不能为负数', async () => {
    const user = userEvent.setup();
    renderComponent();

    await user.type(screen.getByLabelText('规格名称 *'), '测试规格');
    await user.type(screen.getByLabelText('价格'), '-10');

    // 等待按钮启用
    const addButton = screen.getByText('添加');
    await waitFor(() => expect(addButton).not.toBeDisabled());

    await user.click(addButton);

    // 等待toast被调用，因为表单验证可能是异步的
    await waitFor(() => {
      expect(toast.error).toHaveBeenCalledWith('价格不能为负数');
    });
  });

  it('应该验证成本价不能为负数', async () => {
    const user = userEvent.setup();
    renderComponent();

    await user.type(screen.getByLabelText('规格名称 *'), '测试规格');
    // 清除成本价字段（可能包含默认值0），然后输入负值
    await user.clear(screen.getByLabelText('成本价'));
    await user.type(screen.getByLabelText('成本价'), '-5');

    await user.click(screen.getByText('添加'));

    // 等待toast被调用，因为表单验证可能是异步的
    await waitFor(() => {
      expect(toast.error).toHaveBeenCalledWith('成本价不能为负数');
    });
  });

  it('应该验证库存不能为负数', async () => {
    const user = userEvent.setup();
    renderComponent();

    await user.type(screen.getByLabelText('规格名称 *'), '测试规格');
    // 清除库存字段（可能包含默认值0），然后输入负值
    await user.clear(screen.getByLabelText('库存'));
    await user.type(screen.getByLabelText('库存'), '-1');

    await user.click(screen.getByText('添加'));

    // 等待toast被调用，因为表单验证可能是异步的
    await waitFor(() => {
      expect(toast.error).toHaveBeenCalledWith('库存不能为负数');
    });
  });

  it('应该验证规格名称不能重复（添加时）', async () => {
    const user = userEvent.setup();
    const initialSpecs = [
      { name: '红色', price: 100, costPrice: 80, stock: 50, sort: 1 }
    ];
    renderComponent({ initialSpecs });

    // 尝试添加重复的规格名称（不区分大小写）
    await user.type(screen.getByLabelText('规格名称 *'), '红色');
    await user.type(screen.getByLabelText('价格'), '120');
    await user.click(screen.getByText('添加'));

    expect(toast.error).toHaveBeenCalledWith('规格名称 "红色" 已存在，请使用不同的名称');

    // 尝试添加不同大小写的重复名称
    // 首先清除输入字段
    await user.clear(screen.getByLabelText('规格名称 *'));
    await user.type(screen.getByLabelText('规格名称 *'), '红 色'); // 有空格
    await user.click(screen.getByText('添加'));

    // 应该通过，因为"红 色"（有空格）与"红色"（无空格）不同
    expect(toast.success).toHaveBeenCalledWith('规格已添加');
  });

  it('应该验证规格名称不能重复（更新时）', () => {
    const initialSpecs = [
      { name: '红色', price: 100, costPrice: 80, stock: 50, sort: 1 },
      { name: '蓝色', price: 110, costPrice: 85, stock: 30, sort: 2 }
    ];
    const onSpecsChange = vi.fn();
    renderComponent({ initialSpecs, onSpecsChange });

    // 尝试将"蓝色"改为"红色"（重复）
    const nameInputs = screen.getAllByDisplayValue('蓝色');
    fireEvent.change(nameInputs[0], { target: { value: '红色' } });

    expect(toast.error).toHaveBeenCalledWith('规格名称 "红色" 已存在，请使用不同的名称');
    // 验证回调没有被调用（因为更新被阻止）
    expect(onSpecsChange).not.toHaveBeenCalled();
  });

  it('应该验证更新时规格名称不能为空', () => {
    const initialSpecs = [
      { name: '红色', price: 100, costPrice: 80, stock: 50, sort: 1 }
    ];
    const onSpecsChange = vi.fn();
    renderComponent({ initialSpecs, onSpecsChange });

    // 尝试将名称改为空
    const nameInputs = screen.getAllByDisplayValue('红色');
    fireEvent.change(nameInputs[0], { target: { value: '' } });

    expect(toast.error).toHaveBeenCalledWith('规格名称不能为空');
    // 验证回调没有被调用（因为更新被阻止）
    expect(onSpecsChange).not.toHaveBeenCalled();
  });

  it('应该验证多个错误字段', async () => {
    const user = userEvent.setup();
    renderComponent();

    // 设置所有字段为无效值
    await user.clear(screen.getByLabelText('规格名称 *'));
    await user.clear(screen.getByLabelText('价格'));
    await user.type(screen.getByLabelText('价格'), '-10');
    await user.clear(screen.getByLabelText('成本价'));
    await user.type(screen.getByLabelText('成本价'), '-5');
    await user.clear(screen.getByLabelText('库存'));
    await user.type(screen.getByLabelText('库存'), '-1');

    // 按钮应该被禁用（因为名称为空）
    const addButton = screen.getByText('添加');
    expect(addButton).toBeDisabled();

    // 填写名称后，点击按钮应该显示第一个错误
    await user.type(screen.getByLabelText('规格名称 *'), '测试规格');
    expect(addButton).not.toBeDisabled();

    await user.click(addButton);

    // 应该显示价格不能为负数的错误（第一个验证错误）
    // 等待toast被调用，因为表单验证可能是异步的
    await waitFor(() => {
      expect(toast.error).toHaveBeenCalledWith('价格不能为负数');
    });
  });

  it('应该更新规格', () => {
    const initialSpecs = [
      { name: '红色', price: 100, costPrice: 80, stock: 50, sort: 1 }
    ];
    const onSpecsChange = vi.fn();

    renderComponent({ initialSpecs, onSpecsChange });

    // 修改规格名称
    const nameInput = screen.getByDisplayValue('红色');
    fireEvent.change(nameInput, { target: { value: '修改后的红色' } });

    // 验证回调被调用
    expect(onSpecsChange).toHaveBeenCalledWith([
      expect.objectContaining({ name: '修改后的红色' })
    ]);
  });

  it('应该删除规格', () => {
    const initialSpecs = [
      { name: '规格1', price: 100, costPrice: 80, stock: 50, sort: 1 },
      { name: '规格2', price: 110, costPrice: 85, stock: 30, sort: 2 }
    ];
    const onSpecsChange = vi.fn();

    renderComponent({ initialSpecs, onSpecsChange });

    // 找到并点击删除按钮（第一个规格的删除按钮）
    const deleteButtons = screen.getAllByTitle('删除');
    fireEvent.click(deleteButtons[0]);

    // 验证toast被调用
    expect(toast.success).toHaveBeenCalledWith('规格已删除');

    // 验证回调被调用，只剩下一个规格
    expect(onSpecsChange).toHaveBeenCalledWith([
      expect.objectContaining({ name: '规格2' })
    ]);
  });

  it('应该复制规格', () => {
    const initialSpecs = [
      { name: '红色', price: 100, costPrice: 80, stock: 50, sort: 1 }
    ];
    const onSpecsChange = vi.fn();

    renderComponent({ initialSpecs, onSpecsChange });

    // 点击复制按钮
    const copyButtons = screen.getAllByTitle('复制');
    fireEvent.click(copyButtons[0]);

    // 验证toast被调用
    expect(toast.success).toHaveBeenCalledWith('规格已复制');

    // 验证回调被调用，现在有两个规格
    expect(onSpecsChange).toHaveBeenCalledWith([
      expect.objectContaining({ name: '红色' }),
      expect.objectContaining({ name: '红色 (副本)' })
    ]);
  });

  it('应该加载预定义模板', () => {
    const onSpecsChange = vi.fn();
    renderComponent({ onSpecsChange });

    // 点击颜色规格模板
    const templateBadges = screen.getAllByText(/颜色规格模板|尺寸规格模板|容量规格模板/);
    fireEvent.click(templateBadges[0]); // 颜色规格模板

    // 验证toast被调用
    expect(toast.success).toHaveBeenCalledWith('模板已加载');

    // 验证回调被调用，加载了模板规格
    expect(onSpecsChange).toHaveBeenCalledWith(
      expect.arrayContaining([
        expect.objectContaining({ name: '红色' }),
        expect.objectContaining({ name: '蓝色' }),
        expect.objectContaining({ name: '绿色' })
      ])
    );
  });

  it('应该保存模板', async () => {
    const initialSpecs = [
      { name: '测试规格', price: 100, costPrice: 80, stock: 50, sort: 1 }
    ];
    const onSaveTemplate = vi.fn();

    renderComponent({ initialSpecs, onSaveTemplate });

    // 点击保存模板按钮
    fireEvent.click(screen.getByText('保存为模板'));

    // 输入模板名称
    const templateInput = screen.getByPlaceholderText('输入模板名称');
    fireEvent.change(templateInput, { target: { value: '我的模板' } });

    // 点击保存按钮
    fireEvent.click(screen.getByText('保存'));

    // 验证回调被调用
    expect(onSaveTemplate).toHaveBeenCalledWith('我的模板', initialSpecs);

    // 验证toast被调用
    expect(toast.success).toHaveBeenCalledWith('模板保存成功');
  });

  it('应该显示统计信息', () => {
    const initialSpecs = [
      { name: '规格1', price: 100, costPrice: 80, stock: 10, sort: 1 },
      { name: '规格2', price: 200, costPrice: 150, stock: 20, sort: 2 }
    ];

    renderComponent({ initialSpecs });

    // 验证统计信息
    expect(screen.getByText('规格数量')).toBeInTheDocument();
    expect(screen.getByText('2')).toBeInTheDocument();

    expect(screen.getByText('总库存')).toBeInTheDocument();
    expect(screen.getByText('30')).toBeInTheDocument(); // 10 + 20

    expect(screen.getByText('平均价格')).toBeInTheDocument();
    expect(screen.getByText('¥150.00')).toBeInTheDocument(); // (100+200)/2

    expect(screen.getByText('总货值')).toBeInTheDocument();
    expect(screen.getByText('¥5000.00')).toBeInTheDocument(); // (100*10 + 200*20)
  });

  it('应该支持禁用状态', () => {
    renderComponent({ disabled: true });

    // 验证所有输入框都被禁用
    expect(screen.getByLabelText('规格名称 *')).toBeDisabled();
    expect(screen.getByLabelText('价格')).toBeDisabled();
    expect(screen.getByLabelText('成本价')).toBeDisabled();
    expect(screen.getByLabelText('库存')).toBeDisabled();
    expect(screen.getByText('添加')).toBeDisabled();
  });

  it('应该处理空规格列表', () => {
    renderComponent();

    expect(screen.getByText('暂无规格')).toBeInTheDocument();
    expect(screen.getByText('添加规格后，将在创建商品时批量生成子商品')).toBeInTheDocument();
  });

  it('应该测试完整的用户交互流程：添加多个规格并保存模板', async () => {
    const user = userEvent.setup();
    const onSpecsChange = vi.fn();
    const onSaveTemplate = vi.fn();

    renderComponent({ onSpecsChange, onSaveTemplate });

    // 第一步：添加第一个规格
    await user.type(screen.getByLabelText('规格名称 *'), '红色');
    await user.type(screen.getByLabelText('价格'), '100');
    await user.type(screen.getByLabelText('成本价'), '80');
    await user.type(screen.getByLabelText('库存'), '50');
    await user.click(screen.getByText('添加'));

    expect(toast.success).toHaveBeenCalledWith('规格已添加');
    expect(onSpecsChange).toHaveBeenCalledWith([
      expect.objectContaining({ name: '红色', price: 100, costPrice: 80, stock: 50, sort: 0 })
    ]);

    // 第二步：添加第二个规格
    // 清除第一个字段（规格名称），其他字段会被重置
    await user.clear(screen.getByLabelText('规格名称 *'));
    await user.type(screen.getByLabelText('规格名称 *'), '蓝色');
    await user.type(screen.getByLabelText('价格'), '110');
    await user.type(screen.getByLabelText('成本价'), '85');
    await user.type(screen.getByLabelText('库存'), '30');
    await user.click(screen.getByText('添加'));

    expect(toast.success).toHaveBeenCalledWith('规格已添加');
    // 验证回调被调用，但不验证具体的sort值，因为sort逻辑可能复杂
    expect(onSpecsChange).toHaveBeenCalled();

    // 获取最后一次调用的参数
    const lastCall = onSpecsChange.mock.calls[onSpecsChange.mock.calls.length - 1];
    const lastSpecs = lastCall[0];

    // 验证有两个规格
    expect(lastSpecs).toHaveLength(2);
    expect(lastSpecs[0]).toMatchObject({ name: '红色', price: 100, costPrice: 80, stock: 50 });
    expect(lastSpecs[1]).toMatchObject({ name: '蓝色', price: 110, costPrice: 85, stock: 30 });

    // 第三步：更新第一个规格
    const nameInputs = screen.getAllByDisplayValue('红色');
    // 直接设置新值，避免清空触发的问题
    fireEvent.change(nameInputs[0], { target: { value: '深红色' } });

    // 更新规格后，回调应该被调用
    // 我们只验证回调被调用，不验证具体参数，因为可能有多次调用
    expect(onSpecsChange).toHaveBeenCalled();

    // 第四步：保存模板
    await user.click(screen.getByText('保存为模板'));

    const templateInput = screen.getByPlaceholderText('输入模板名称');
    await user.type(templateInput, '颜色规格');
    await user.click(screen.getByText('保存'));

    expect(onSaveTemplate).toHaveBeenCalledWith('颜色规格', [
      expect.objectContaining({ name: '深红色', price: 100, costPrice: 80, stock: 50 }),
      expect.objectContaining({ name: '蓝色', price: 110, costPrice: 85, stock: 30 })
    ]);
    expect(toast.success).toHaveBeenCalledWith('模板保存成功');

    // 第五步：验证统计信息
    expect(screen.getByText('规格数量')).toBeInTheDocument();
    expect(screen.getByText('2')).toBeInTheDocument();
    expect(screen.getByText('总库存')).toBeInTheDocument();
    expect(screen.getByText('80')).toBeInTheDocument(); // 50 + 30
  });

  it('应该测试错误场景：保存空模板', async () => {
    const user = userEvent.setup();
    const onSaveTemplate = vi.fn();
    const onSpecsChange = vi.fn();
    renderComponent({ onSaveTemplate, onSpecsChange });

    // 先添加一个规格，这样"保存为模板"按钮才会显示
    await user.type(screen.getByLabelText('规格名称 *'), '测试规格');
    await user.type(screen.getByLabelText('价格'), '100');
    await user.click(screen.getByText('添加'));

    // 尝试保存空模板
    await user.click(screen.getByText('保存为模板'));

    const templateInput = screen.getByPlaceholderText('输入模板名称');
    await user.clear(templateInput);

    // 保存按钮应该被禁用
    const saveButton = screen.getByText('保存');
    expect(saveButton).toBeDisabled();

    // 即使点击也不会触发保存
    await user.click(saveButton);

    // 验证toast.error没有被调用（因为按钮被禁用）
    expect(toast.error).not.toHaveBeenCalledWith('请输入模板名称');
    expect(onSaveTemplate).not.toHaveBeenCalled();
  });

  it('应该测试错误场景：保存空规格模板', () => {
    const onSaveTemplate = vi.fn();
    renderComponent({ onSaveTemplate });

    // 当没有规格时，"保存为模板"按钮不应该显示
    // 所以这个测试场景实际上不会发生
    // 改为测试当没有规格时，统计区域显示"暂无规格"
    expect(screen.getByText('暂无规格')).toBeInTheDocument();
    expect(screen.queryByText('保存为模板')).not.toBeInTheDocument();
  });

  it('应该测试完整的批量创建流程：从模板加载到修改', () => {
    const onSpecsChange = vi.fn();
    renderComponent({ onSpecsChange });

    // 第一步：加载预定义模板
    const templateBadges = screen.getAllByText(/颜色规格模板|尺寸规格模板|容量规格模板/);
    fireEvent.click(templateBadges[0]); // 颜色规格模板

    expect(toast.success).toHaveBeenCalledWith('模板已加载');
    expect(onSpecsChange).toHaveBeenCalledWith(
      expect.arrayContaining([
        expect.objectContaining({ name: '红色' }),
        expect.objectContaining({ name: '蓝色' }),
        expect.objectContaining({ name: '绿色' }),
        expect.objectContaining({ name: '黑色' }),
        expect.objectContaining({ name: '白色' })
      ])
    );

    // 第二步：修改模板中的规格
    const nameInputs = screen.getAllByDisplayValue('红色');
    fireEvent.change(nameInputs[0], { target: { value: '亮红色' } });

    expect(onSpecsChange).toHaveBeenCalledWith(
      expect.arrayContaining([
        expect.objectContaining({ name: '亮红色' }), // 修改后的
        expect.objectContaining({ name: '蓝色' })
      ])
    );

    // 第三步：删除一个规格
    const deleteButtons = screen.getAllByTitle('删除');
    fireEvent.click(deleteButtons[0]); // 删除第一个规格

    expect(toast.success).toHaveBeenCalledWith('规格已删除');

    // 现在应该只有4个规格了
    const calls = onSpecsChange.mock.calls;
    const lastCall = calls[calls.length - 1];
    expect(lastCall[0]).toHaveLength(4); // 5个规格删除了1个，剩下4个
  });
});