import { describe, it, expect, beforeEach, vi } from 'vitest';
import { DataSource, Repository } from 'typeorm';
import { OrderService } from '../../src/services/order.service';
import { OrderPersonAsset } from '../../src/entities/order-person-asset.entity';
import { EmploymentOrder } from '../../src/entities/employment-order.entity';
import { File } from '@d8d/core-module/file-module';
import { AssetType, AssetFileType } from '../../src/schemas/order.schema';

/**
 * OrderService 单元测试
 *
 * 重点测试 getCompanyVideos 方法：
 * - 验证 leftJoin 能返回所有视频记录（包括没有关联订单的视频）
 * - 验证企业数据隔离正确性
 */
describe('OrderService - getCompanyVideos', () => {
  let orderService: OrderService;
  let mockAssetRepository: Partial<Repository<OrderPersonAsset>>;
  let mockQueryBuilder: any;

  beforeEach(() => {
    // 创建 mock queryBuilder
    mockQueryBuilder = {
      innerJoin: vi.fn().mockReturnThis(),
      leftJoin: vi.fn().mockReturnThis(),
      where: vi.fn().mockReturnThis(),
      andWhere: vi.fn().mockReturnThis(),
      orderBy: vi.fn().mockReturnThis(),
      leftJoinAndSelect: vi.fn().mockReturnThis(),
      skip: vi.fn().mockReturnThis(),
      take: vi.fn().mockReturnThis(),
      getMany: vi.fn(),
      getCount: vi.fn()
    };

    // 创建 mock repository
    mockAssetRepository = {
      createQueryBuilder: vi.fn().mockReturnValue(mockQueryBuilder)
    };

    // 创建 OrderService 实例
    orderService = new OrderService({
      getRepository: vi.fn().mockReturnValue(mockAssetRepository)
    } as Partial<DataSource> as DataSource);
  });

  describe('leftJoin vs innerJoin 行为验证', () => {
    it('应该使用 leftJoin 而不是 innerJoin 来获取企业视频', async () => {
      // 准备测试数据
      const mockVideoAssets = [
        {
          id: 1,
          orderId: 100,
          personId: 1,
          assetType: AssetType.WORK_VIDEO,
          assetFileType: AssetFileType.VIDEO,
          fileId: 1,
          relatedTime: new Date('2024-01-01'),
          createTime: new Date('2024-01-01'),
          updateTime: new Date('2024-01-01'),
          file: {
            id: 1,
            name: 'test-video.mp4',
            type: 'video/mp4',
            size: 1024000,
            path: 'videos/test-video.mp4',
            fullUrl: 'http://example.com/videos/test-video.mp4',
            uploadTime: new Date('2024-01-01')
          }
        }
      ];

      mockQueryBuilder.getMany.mockResolvedValue(mockVideoAssets);
      mockQueryBuilder.getCount.mockResolvedValue(1);

      // 调用 getCompanyVideos 方法
      const result = await orderService.getCompanyVideos(1, {
        assetType: AssetType.WORK_VIDEO,
        page: 1,
        pageSize: 10
      });

      // 验证结果
      expect(result.data).toHaveLength(1);
      expect(result.total).toBe(1);

      // 关键验证：应该调用 leftJoin 而不是 innerJoin
      expect(mockQueryBuilder.leftJoin).toHaveBeenCalledWith(
        'asset.order',
        'order'
      );
      expect(mockQueryBuilder.innerJoin).not.toHaveBeenCalled();
    });

    it('应该正确过滤企业数据（通过 companyId）', async () => {
      const mockVideoAssets = [
        {
          id: 1,
          orderId: 100,
          personId: 1,
          assetType: AssetType.SALARY_VIDEO,
          assetFileType: AssetFileType.VIDEO,
          fileId: 1,
          relatedTime: new Date('2024-01-01'),
          createTime: new Date('2024-01-01'),
          updateTime: new Date('2024-01-01'),
          file: {
            id: 1,
            name: 'salary-video.mp4',
            type: 'video/mp4',
            size: 2048000,
            path: 'videos/salary-video.mp4',
            fullUrl: 'http://example.com/videos/salary-video.mp4',
            uploadTime: new Date('2024-01-01')
          }
        }
      ];

      mockQueryBuilder.getMany.mockResolvedValue(mockVideoAssets);
      mockQueryBuilder.getCount.mockResolvedValue(1);

      // 调用方法，指定 companyId = 123
      await orderService.getCompanyVideos(123, {
        page: 1,
        pageSize: 10
      });

      // 验证 WHERE 条件包含 companyId 过滤（修复后支持 order.companyId 为 NULL 的情况）
      expect(mockQueryBuilder.where).toHaveBeenCalledWith(
        '(order.companyId = :companyId OR order.companyId IS NULL)',
        { companyId: 123 }
      );
    });

    it('应该只返回视频类型的资产（assetFileType = video）', async () => {
      const mockVideoAssets = [
        {
          id: 1,
          orderId: 100,
          personId: 1,
          assetType: AssetType.CHECKIN_VIDEO,
          assetFileType: AssetFileType.VIDEO,
          fileId: 1,
          relatedTime: new Date('2024-01-01'),
          createTime: new Date('2024-01-01'),
          updateTime: new Date('2024-01-01'),
          file: {
            id: 1,
            name: 'checkin-video.mp4',
            type: 'video/mp4',
            size: 512000,
            path: 'videos/checkin-video.mp4',
            fullUrl: 'http://example.com/videos/checkin-video.mp4',
            uploadTime: new Date('2024-01-01')
          }
        }
      ];

      mockQueryBuilder.getMany.mockResolvedValue(mockVideoAssets);
      mockQueryBuilder.getCount.mockResolvedValue(1);

      await orderService.getCompanyVideos(1, {
        page: 1,
        pageSize: 10
      });

      // 验证 AND WHERE 条件包含视频文件类型过滤（使用IN查询兼容mp4）
      expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith(
        'asset.assetFileType IN (:...fileTypes)',
        { fileTypes: ['video', 'mp4'] }
      );
    });

    it('应该支持按资产类型过滤（assetType 参数）', async () => {
      const mockVideoAssets = [];
      mockQueryBuilder.getMany.mockResolvedValue(mockVideoAssets);
      mockQueryBuilder.getCount.mockResolvedValue(0);

      // 调用方法，指定 assetType 过滤
      await orderService.getCompanyVideos(1, {
        assetType: AssetType.TAX_VIDEO,
        page: 1,
        pageSize: 10
      });

      // 验证调用了两次 andWhere：一次是视频文件类型（使用IN查询兼容mp4），一次是资产类型
      expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith(
        'asset.assetFileType IN (:...fileTypes)',
        { fileTypes: ['video', 'mp4'] }
      );
      expect(mockQueryBuilder.andWhere).toHaveBeenCalledWith(
        'asset.assetType = :assetType',
        { assetType: AssetType.TAX_VIDEO }
      );
    });

    it('应该正确格式化返回数据', async () => {
      const mockVideoAssets = [
        {
          id: 1,
          orderId: 100,
          personId: 1,
          assetType: AssetType.WORK_VIDEO,
          assetFileType: AssetFileType.VIDEO,
          fileId: 1,
          relatedTime: new Date('2024-01-01'),
          createTime: new Date('2024-01-01'),
          updateTime: new Date('2024-01-01'),
          file: {
            id: 1,
            name: 'test-video.mp4',
            type: 'video/mp4',
            size: 1024000,
            path: 'videos/test-video.mp4',
            fullUrl: 'http://example.com/videos/test-video.mp4',
            uploadTime: new Date('2024-01-01'),
            description: 'Test video description'
          }
        }
      ];

      mockQueryBuilder.getMany.mockResolvedValue(mockVideoAssets);
      mockQueryBuilder.getCount.mockResolvedValue(1);

      const result = await orderService.getCompanyVideos(1, {
        page: 1,
        pageSize: 10
      });

      // 验证返回数据格式
      expect(result.data[0]).toMatchObject({
        id: 1,
        orderId: 100,
        personId: 1,
        assetType: AssetType.WORK_VIDEO,
        assetFileType: AssetFileType.VIDEO,
        fileId: 1,
        relatedTime: expect.any(Date),
        createTime: expect.any(Date),
        updateTime: expect.any(Date),
        file: {
          id: 1,
          name: 'test-video.mp4',
          type: 'video/mp4',
          size: 1024000,
          path: 'videos/test-video.mp4',
          fullUrl: 'http://example.com/videos/test-video.mp4',
          uploadTime: expect.any(Date),
          description: 'Test video description'
        }
      });
    });
  });

  describe('分页和排序功能', () => {
    it('应该支持分页查询', async () => {
      mockQueryBuilder.getMany.mockResolvedValue([]);
      mockQueryBuilder.getCount.mockResolvedValue(0);

      await orderService.getCompanyVideos(1, {
        page: 2,
        pageSize: 20
      });

      // 验证分页参数正确应用
      expect(mockQueryBuilder.skip).toHaveBeenCalledWith((2 - 1) * 20);
      expect(mockQueryBuilder.take).toHaveBeenCalledWith(20);
    });

    it('应该支持按不同字段排序', async () => {
      mockQueryBuilder.getMany.mockResolvedValue([]);
      mockQueryBuilder.getCount.mockResolvedValue(0);

      // 测试按 createTime 排序
      await orderService.getCompanyVideos(1, {
        sortBy: 'createTime',
        sortOrder: 'ASC',
        page: 1,
        pageSize: 10
      });

      expect(mockQueryBuilder.orderBy).toHaveBeenCalledWith(
        'asset.createTime',
        'ASC'
      );
    });

    it('默认应该按 relatedTime 降序排序', async () => {
      mockQueryBuilder.getMany.mockResolvedValue([]);
      mockQueryBuilder.getCount.mockResolvedValue(0);

      await orderService.getCompanyVideos(1, {
        page: 1,
        pageSize: 10
      });

      expect(mockQueryBuilder.orderBy).toHaveBeenCalledWith(
        'asset.relatedTime',
        'DESC'
      );
    });
  });
});
