import { ProductService } from '../services/product/ProductService';
import db from '../config/database';

// 模拟数据库连接
jest.mock('../config/database', () => ({
  schema: {
    hasTable: jest.fn(),
    createTable: jest.fn(),
    hasColumn: jest.fn(),
    alterTable: jest.fn(),
  },
  where: jest.fn().mockReturnThis(),
  first: jest.fn(),
  insert: jest.fn().mockReturnThis(),
  returning: jest.fn(),
  update: jest.fn().mockReturnThis(),
  delete: jest.fn(),
  select: jest.fn(),
  raw: jest.fn(),
  from: jest.fn().mockReturnThis(),
  table: jest.fn().mockReturnThis(),
}));

// 模拟MultiTenantCore
jest.mock('../domains/Tenant/MultiTenantCore', () => ({
  MultiTenantCore: {
    from: jest.fn().mockReturn({
      where: jest.fn().mockReturnThis(),
      first: jest.fn(),
      select: jest.fn(),
      update: jest.fn(),
      delete: jest.fn(),
      table: jest.fn().mockReturnThis(),
    }),
    ensureOwnership: jest.fn().mockResolvedValue(true),
  },
}));

// 模拟数据库初始化
jest.mock('../config/database', () => ({
  ...jest.requireActual('../config/database'),
  default: {
    schema: {
      hasTable: jest.fn(),
      createTable: jest.fn(),
      hasColumn: jest.fn(),
      alterTable: jest.fn(),
    },
    where: jest.fn().mockReturnThis(),
    first: jest.fn(),
    insert: jest.fn().mockReturnThis(),
    returning: jest.fn(),
    update: jest.fn().mockReturnThis(),
    delete: jest.fn(),
    select: jest.fn(),
    raw: jest.fn(),
    from: jest.fn().mockReturnThis(),
    table: jest.fn().mockReturnThis(),
  },
  initializeDatabase: jest.fn().mockResolvedValue(undefined),
}));


// 模拟其他服务
jest.mock('../core/pipeline/CDCPipeline', () => ({
  CDCPipeline: {
    onProductChange: jest.fn().mockResolvedValue(undefined),
  },
}));

jest.mock('../core/runtime/DomainEventBus', () => ({
  DomainEventBus: {
    getInstance: jest.fn().mockReturn({
      publish: jest.fn(),
    }),
  },
}));

describe('ProductService', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('initTable', () => {
    it('should create product table if it does not exist', async () => {
      // 模拟表不存在
      (db.schema.hasTable as jest.Mock).mockResolvedValue(false);

      await ProductService.initTable();

      expect(db.schema.createTable).toHaveBeenCalledWith('cf_product', expect.any(Function));
    });

    it('should add missing columns if table exists', async () => {
      // 模拟表存在但缺少列
      (db.schema.hasTable as jest.Mock).mockResolvedValue(true);
      (db.schema.hasColumn as jest.Mock)
        .mockResolvedValueOnce(false) // tenant_id
        .mockResolvedValueOnce(false) // phash
        .mockResolvedValueOnce(false) // description
        .mockResolvedValueOnce(false) // semantic_hash
        .mockResolvedValueOnce(false) // vector_embedding
        .mockResolvedValueOnce(false) // status
        .mockResolvedValueOnce(false); // ai_confidence

      await ProductService.initTable();

      expect(db.schema.alterTable).toHaveBeenCalledTimes(7);
    });
  });

  describe('getAll', () => {
    it('should return products with filters', async () => {
      const mockProducts = [
        { id: 1, name: 'Product 1', price: 100 },
        { id: 2, name: 'Product 2', price: 200 },
      ];

      const mockQuery = {
        where: jest.fn().mockReturnThis(),
        select: jest.fn().mockResolvedValue(mockProducts),
      };

      (require('../domains/Tenant/MultiTenantCore').MultiTenantCore.from as jest.Mock).mockReturn(mockQuery);

      const result = await ProductService.getAll('tenant-1', { platform: 'shopify', status: 'active' });

      expect(mockQuery.where).toHaveBeenCalledWith({ platform: 'shopify' });
      expect(mockQuery.where).toHaveBeenCalledWith({ status: 'active' });
      expect(mockQuery.select).toHaveBeenCalledWith('*');
      expect(result).toEqual(mockProducts);
    });
  });

  describe('getById', () => {
    it('should return product by id', async () => {
      const mockProduct = { id: 1, name: 'Product 1', price: 100 };

      const mockQuery = {
        where: jest.fn().mockReturnThis(),
        first: jest.fn().mockResolvedValue(mockProduct),
      };

      (require('../domains/Tenant/MultiTenantCore').MultiTenantCore.from as jest.Mock).mockReturn(mockQuery);

      const result = await ProductService.getById('tenant-1', 1);

      expect(mockQuery.where).toHaveBeenCalledWith({ id: 1 });
      expect(mockQuery.first).toHaveBeenCalled();
      expect(result).toEqual(mockProduct);
    });

    it('should return null if product not found', async () => {
      const mockQuery = {
        where: jest.fn().mockReturnThis(),
        first: jest.fn().mockResolvedValue(null),
      };

      (require('../domains/Tenant/MultiTenantCore').MultiTenantCore.from as jest.Mock).mockReturn(mockQuery);

      const result = await ProductService.getById('tenant-1', 1);

      expect(result).toBeNull();
    });
  });

  describe('create', () => {
    it('should create a new product', async () => {
      const productData = { name: 'New Product', price: 100 };
      const mockInsertResult = [1];

      (db.insert as jest.Mock).mockReturnThis();
      (db.returning as jest.Mock).mockResolvedValue(mockInsertResult);

      const result = await ProductService.create('tenant-1', productData);

      expect(db.insert).toHaveBeenCalledWith(expect.objectContaining({
        ...productData,
        tenant_id: 'tenant-1',
      }));
      expect(result).toBe(1);
    });
  });

  describe('update', () => {
    it('should update product by id', async () => {
      const productData = { name: 'Updated Product', price: 200 };
      const mockProduct = { id: 1, name: 'Original Product', price: 100 };

      const mockQuery = {
        where: jest.fn().mockReturnThis(),
        first: jest.fn().mockResolvedValue(mockProduct),
        update: jest.fn().mockReturnThis(),
      };

      (require('../domains/Tenant/MultiTenantCore').MultiTenantCore.from as jest.Mock).mockReturn(mockQuery);

      await ProductService.update('tenant-1', 1, productData);

      expect(mockQuery.where).toHaveBeenCalledWith({ id: 1 });
      expect(mockQuery.update).toHaveBeenCalledWith(expect.objectContaining({
        ...productData,
        updated_at: expect.any(Date),
      }));
    });
  });

  describe('delete', () => {
    it('should delete product by id', async () => {
      const mockProduct = { id: 1, name: 'Product 1', price: 100 };

      const mockQuery = {
        where: jest.fn().mockReturnThis(),
        first: jest.fn().mockResolvedValue(mockProduct),
        delete: jest.fn(),
      };

      (require('../domains/Tenant/MultiTenantCore').MultiTenantCore.from as jest.Mock).mockReturn(mockQuery);

      await ProductService.delete('tenant-1', 1);

      expect(mockQuery.where).toHaveBeenCalledWith({ id: 1 });
      expect(mockQuery.delete).toHaveBeenCalled();
    });
  });

  describe('analyzeProductArbitrage', () => {
    it('should analyze product arbitrage', async () => {
      const mockProduct = { id: 1, title: 'Product 1', costPrice: 100 };

      const mockQuery = {
        where: jest.fn().mockReturnThis(),
        first: jest.fn().mockResolvedValue(mockProduct),
      };

      (require('../domains/Tenant/MultiTenantCore').MultiTenantCore.from as jest.Mock).mockReturn(mockQuery);

      // 模拟ArbitrageService
      const mockArbitrageService = {
        getInstance: jest.fn().mockReturn({
          createOpportunity: jest.fn().mockResolvedValue({ arbitrage: true, profit: 250 }),
        }),
      };

      jest.mock('../services/core/ArbitrageService', () => mockArbitrageService);

      const result = await ProductService.analyzeProductArbitrage('tenant-1', 1);

      expect(result).toEqual({ arbitrage: true, profit: 250 });
    });
  });

  describe('washAndLocalize', () => {
    it('should wash and localize product', async () => {
      const mockProduct = { id: 1, title: 'Original Title', description: 'Original Description', mainImage: 'image.jpg' };

      const mockQuery = {
        where: jest.fn().mockReturnThis(),
        first: jest.fn().mockResolvedValue(mockProduct),
        update: jest.fn().mockReturnThis(),
      };

      (require('../domains/Tenant/MultiTenantCore').MultiTenantCore.from as jest.Mock).mockReturn(mockQuery);

      // 模拟AIService
      const mockAIService = {
        localizeForIndependentStation: jest.fn().mockResolvedValue({
          seoTitle: 'Localized Title',
          localizedBodyHtml: 'Localized Description',
          tags: ['tag1', 'tag2'],
          jsonLd: { type: 'Product' },
        }),
      };

      // 模拟VisionFactoryService
      const mockVisionFactoryService = {
        generateMarketingImage: jest.fn().mockResolvedValue('marketing-image.jpg'),
      };

      jest.mock('../services/ai/AIService', () => mockAIService);
      jest.mock('../services/core/VisionFactoryService', () => mockVisionFactoryService);

      const result = await ProductService.washAndLocalize('tenant-1', 1, 'US', 'en');

      expect(result).toEqual({
        id: 1,
        originalTitle: 'Original Title',
        newTitle: 'Localized Title',
        marketingImage: 'marketing-image.jpg',
        tags: ['tag1', 'tag2'],
      });
    });
  });
});
