/**
 * Payment Service 单元测试
 * 测试订单创建、支付回调、会员激活
 */

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { generateOrderNo } from '@/modules/payment/payment.service';

// 直接 Mock prisma 模型
const mockOrderFindFirst = vi.fn();
const mockOrderFindUnique = vi.fn();
const mockOrderUpdate = vi.fn();
const mockOrderCreate = vi.fn();
const mockUserUpdate = vi.fn();
const mockUserFindUnique = vi.fn();
const mockPlanFindUnique = vi.fn();
const mockSubscriptionFindFirst = vi.fn();
const mockSubscriptionCreate = vi.fn();
const mockSubscriptionUpdate = vi.fn();
const mockTokenBalanceFindUnique = vi.fn();
const mockTokenBalanceUpdate = vi.fn();
const mockTokenBalanceCreate = vi.fn();

vi.mock('@/models', () => ({
  prisma: {
    order: {
      findFirst: (...args: any[]) => mockOrderFindFirst(...args),
      findUnique: (...args: any[]) => mockOrderFindUnique(...args),
      update: (...args: any[]) => mockOrderUpdate(...args),
      create: (...args: any[]) => mockOrderCreate(...args),
    },
    user: {
      update: (...args: any[]) => mockUserUpdate(...args),
      findUnique: (...args: any[]) => mockUserFindUnique(...args),
    },
    subscriptionPlan: {
      findUnique: (...args: any[]) => mockPlanFindUnique(...args),
    },
    subscription: {
      findFirst: (...args: any[]) => mockSubscriptionFindFirst(...args),
      create: (...args: any[]) => mockSubscriptionCreate(...args),
      update: (...args: any[]) => mockSubscriptionUpdate(...args),
    },
    tokenBalance: {
      findUnique: (...args: any[]) => mockTokenBalanceFindUnique(...args),
      update: (...args: any[]) => mockTokenBalanceUpdate(...args),
      create: (...args: any[]) => mockTokenBalanceCreate(...args),
    },
  },
}));

describe('generateOrderNo', () => {
  it('生成格式正确的订单号', () => {
    const orderNo = generateOrderNo();
    expect(orderNo).toMatch(/^PAY\d{8}[A-Z0-9]{6}$/);
  });

  it('每次调用生成不同的订单号', () => {
    const orderNo1 = generateOrderNo();
    const orderNo2 = generateOrderNo();
    expect(orderNo1).not.toBe(orderNo2);
  });

  it('订单号包含当前日期', () => {
    const orderNo = generateOrderNo();
    const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
    expect(orderNo).toContain(today);
  });
});

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

  it('套餐不存在时抛出错误', async () => {
    const { createPaymentOrder } = await import('@/modules/payment/payment.service');
    mockPlanFindUnique.mockResolvedValue(null);

    await expect(
      createPaymentOrder(1, 999, 'mock')
    ).rejects.toThrow('套餐不存在');
  });

  it('成功创建 mock 订单', async () => {
    const { createPaymentOrder } = await import('@/modules/payment/payment.service');

    mockPlanFindUnique.mockResolvedValue({ id: 1, name: '专业版', priceMonthly: 59, priceYearly: 590 });
    mockOrderCreate.mockResolvedValue({ id: 1, status: 'pending' });

    const result = await createPaymentOrder(1, 1, 'mock');

    expect(result.planName).toBe('专业版');
    expect(result.amount).toBe(59);
    expect(result.orderNo).toBeDefined();
  });

  it('月付使用 monthly 价格', async () => {
    const { createPaymentOrder } = await import('@/modules/payment/payment.service');

    mockPlanFindUnique.mockResolvedValue({ id: 1, name: '专业版', priceMonthly: 59, priceYearly: 590 });
    mockOrderCreate.mockResolvedValue({ id: 1, status: 'pending' });

    const result = await createPaymentOrder(1, 1, 'mock', 'monthly');

    expect(result.amount).toBe(59);
  });

  it('年付使用 yearly 价格', async () => {
    const { createPaymentOrder } = await import('@/modules/payment/payment.service');

    mockPlanFindUnique.mockResolvedValue({ id: 1, name: '专业版', priceMonthly: 59, priceYearly: 590 });
    mockOrderCreate.mockResolvedValue({ id: 1, status: 'pending' });

    const result = await createPaymentOrder(1, 1, 'mock', 'yearly');

    expect(result.amount).toBe(590);
  });
});

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

  it('订单不存在时抛出错误', async () => {
    const { handlePaymentCallback } = await import('@/modules/payment/payment.service');
    mockOrderFindFirst.mockResolvedValue(null);

    await expect(
      handlePaymentCallback('NONEXISTENT', 'PAY123', 'success')
    ).rejects.toThrow('订单不存在');
  });

  it('订单已支付时返回幂等成功', async () => {
    const { handlePaymentCallback } = await import('@/modules/payment/payment.service');

    mockOrderFindFirst.mockResolvedValue({
      id: 1,
      orderNo: 'PAY20260519000001',
      status: 'paid',
      userId: 1,
    });

    const result = await handlePaymentCallback('PAY20260519000001', 'PAY123', 'success');

    expect(result.success).toBe(true);
    expect(result.message).toContain('已支付');
  });

  it('支付失败时更新订单状态', async () => {
    const { handlePaymentCallback } = await import('@/modules/payment/payment.service');

    mockOrderFindFirst
      .mockResolvedValueOnce({
        id: 1,
        orderNo: 'PAY20260519000001',
        status: 'pending',
        userId: 1,
      })
      .mockResolvedValueOnce({
        id: 1,
        orderNo: 'PAY20260519000001',
        status: 'failed',
        userId: 1,
      });

    mockOrderUpdate.mockResolvedValue({ id: 1, status: 'failed' });

    const result = await handlePaymentCallback('PAY20260519000001', 'PAY123', 'failed');

    expect(result.success).toBe(false);
    expect(result.message).toContain('失败');
  });

  it('支付成功时调用 activateSubscription', async () => {
    const { handlePaymentCallback } = await import('@/modules/payment/payment.service');

    mockOrderFindFirst
      .mockResolvedValueOnce({
        id: 1,
        orderNo: 'PAY20260519000001',
        status: 'pending',
        userId: 1,
        planId: 2,
        productType: 'monthly',
      })
      .mockResolvedValueOnce({
        id: 1,
        orderNo: 'PAY20260519000001',
        status: 'paid',
        userId: 1,
        planId: 2,
      });

    mockOrderUpdate.mockResolvedValue({});
    mockPlanFindUnique.mockResolvedValue({ id: 2, level: 2, monthlyTokens: 15000 });
    mockSubscriptionFindFirst.mockResolvedValue(null);
    mockUserUpdate.mockResolvedValue({});
    mockTokenBalanceFindUnique.mockResolvedValue(null);
    mockTokenBalanceCreate.mockResolvedValue({});

    const result = await handlePaymentCallback('PAY20260519000001', 'PAY123', 'success');

    expect(result.success).toBe(true);
    expect(result.message).toContain('成功');
  });
});