import { Category } from './entities/category.entity';
import { CatalogSyncJob } from './entities/catalog-sync-job.entity';
import { Course } from './entities/course.entity';
import { OrderItem } from '../orders/entities/order-item.entity';
import { ThirdPartyCatalogService } from './third-party-catalog.service';

function createRepositoryMock<T extends object>() {
  return {
    create: jest.fn((input: Partial<T>) => input),
    findOne: jest.fn(),
    find: jest.fn(),
    exist: jest.fn(),
    remove: jest.fn(async (input: Partial<T>) => input),
    save: jest.fn(async (input: Partial<T>) => input),
    createQueryBuilder: jest.fn(),
  };
}

describe('ThirdPartyCatalogService', () => {
  const client = {
    call: jest.fn(),
  };
  const catalogSyncJobsRepository = createRepositoryMock<CatalogSyncJob>();
  const categoriesRepository = createRepositoryMock<Category>();
  const coursesRepository = createRepositoryMock<Course>();
  const orderItemsRepository = createRepositoryMock<OrderItem>();

  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('syncs remote categories into local cache', async () => {
    client.call.mockResolvedValue({
      code: 1,
      data: [
        { id: '100', name: '大学课程', sort: 2 },
        { cid: '200', title: '职业培训' },
      ],
    });
    categoriesRepository.findOne.mockResolvedValue(null);
    const service = new ThirdPartyCatalogService(
      client as never,
      catalogSyncJobsRepository as never,
      categoriesRepository as never,
      coursesRepository as never,
      orderItemsRepository as never,
    );

    const result = await service.syncCategories();

    expect(client.call).toHaveBeenCalledWith('getcate');
    expect(categoriesRepository.save).toHaveBeenCalledTimes(2);
    expect(categoriesRepository.save).toHaveBeenCalledWith(
      expect.objectContaining({
        sourceType: 'third_party',
        provider: 'biedawo',
        externalId: '100',
        remoteCategoryId: '100',
        name: '大学课程',
      }),
    );
    expect(result).toEqual(
      expect.objectContaining({
        total: 2,
        created: 2,
        updated: 0,
      }),
    );
  });

  it('syncs remote courses and links cached categories', async () => {
    client.call.mockResolvedValue({
      code: 1,
      data: [{ kcid: 'kc-1', kcname: '形势与政策', fenlei: '100', price: '12.5' }],
    });
    categoriesRepository.findOne.mockResolvedValue({ id: 'category-id' });
    coursesRepository.findOne.mockResolvedValue(null);
    const service = new ThirdPartyCatalogService(
      client as never,
      catalogSyncJobsRepository as never,
      categoriesRepository as never,
      coursesRepository as never,
      orderItemsRepository as never,
    );

    const result = await service.syncCourses('100');

    expect(client.call).toHaveBeenCalledWith('getclass', { fenlei: '100' });
    expect(coursesRepository.save).toHaveBeenCalledWith(
      expect.objectContaining({
        sourceType: 'third_party',
        provider: 'biedawo',
        externalId: 'kc-1',
        remoteCourseId: 'kc-1',
        remoteCategoryId: '100',
        categoryId: 'category-id',
        name: '形势与政策',
        price: '12.50',
      }),
    );
    expect(result).toEqual(
      expect.objectContaining({
        remoteCategoryId: '100',
        total: 1,
        created: 1,
        updated: 0,
      }),
    );
  });

  it('marks missing third-party products as removed when orders already exist', async () => {
    client.call
      .mockResolvedValueOnce({
        code: 1,
        data: [{ id: '100', name: '大学课程' }],
      })
      .mockResolvedValueOnce({
        code: 1,
        data: [],
      });
    categoriesRepository.findOne.mockResolvedValue(null);
    coursesRepository.find.mockResolvedValue([
      {
        id: 'product-id',
        sourceType: 'third_party',
        provider: 'biedawo',
        externalId: 'kc-removed',
        remoteCategoryId: '100',
        enabled: true,
        syncStatus: 'active',
      },
    ]);
    orderItemsRepository.exist.mockResolvedValue(true);
    const service = new ThirdPartyCatalogService(
      client as never,
      catalogSyncJobsRepository as never,
      categoriesRepository as never,
      coursesRepository as never,
      orderItemsRepository as never,
    );

    const result = await service.syncThirdPartyCatalog({
      triggerType: 'manual',
      remoteCategoryId: '100',
    });

    expect(orderItemsRepository.exist).toHaveBeenCalledWith({
      where: { productId: 'product-id' },
    });
    expect(coursesRepository.remove).not.toHaveBeenCalled();
    expect(coursesRepository.save).toHaveBeenCalledWith(
      expect.objectContaining({
        id: 'product-id',
        enabled: false,
        syncStatus: 'removed',
      }),
    );
    expect(result).toEqual(
      expect.objectContaining({
        categoryTotal: 1,
        productRemoved: 1,
      }),
    );
  });
});
