import { Test } from '@nestjs/testing';
import { Prisma } from '@prisma/client';
import { FamilyRecomputeService } from './family-recompute.service';
import { ProductFamiliesService } from './product-families.service';
import { OrganizeService } from './organize.service';
import { PrismaService } from '../prisma/prisma.service';

/**
 * 集成测试（连真实库，与 goods.service.spec.ts 同模式）：
 * 验证并集尺码表/包装规则裁决、五维价格矩阵推导（最低价/来源累积/维度过滤）、
 * 覆盖合并、autoManaged 锁定语义与 canonical detail 初始化。
 */
describe('FamilyRecomputeService', () => {
  let service: FamilyRecomputeService;
  let organize!: OrganizeService;
  let prisma: PrismaService;
  const stamp = Date.now();
  const createdOriginGoodIds: bigint[] = [];
  const createdFamilyIds: bigint[] = [];
  const createdTagIds: bigint[] = [];
  const createdTagGroupIds: bigint[] = [];

  const mkOriginGood = async (over: {
    goodName?: string;
    source?: 'SDS' | 'CUSTOM';
    craftLabel?: string | null;
    logisticsLabel?: string | null;
    variants?: Array<{
      sdsVariantId: string;
      sku: string;
      sizeId?: string;
      sizeName?: string;
      colorId?: string;
      colorName?: string;
      price: number;
      enabled?: boolean;
    }>;
    sizeChart?: object;
    packageSpecs?: object;
  }) => {
    const og = await prisma.originGood.create({
      data: {
        sdsGoodId: `recompute-${stamp}-${createdOriginGoodIds.length}-${Math.random().toString(36).slice(2, 7)}`,
        goodName: over.goodName ?? `测试链接-${stamp}`,
        source: over.source ?? 'CUSTOM',
        craftLabel: over.craftLabel ?? null,
        logisticsLabel: over.logisticsLabel ?? null,
      },
    });
    createdOriginGoodIds.push(og.id);
    if (over.variants?.length) {
      await prisma.originGoodVariant.createMany({
        data: over.variants.map((v) => ({
          originGoodId: og.id,
          sdsVariantId: v.sdsVariantId,
          sku: v.sku,
          sizeId: v.sizeId ?? null,
          sizeName: v.sizeName ?? null,
          colorId: v.colorId ?? null,
          colorName: v.colorName ?? null,
          price: new Prisma.Decimal(v.price),
          enabled: v.enabled ?? true,
        })),
      });
    }
    if (over.sizeChart || over.packageSpecs) {
      await prisma.originGoodDetail.create({
        data: {
          originGoodId: og.id,
          sizeChart: (over.sizeChart ?? undefined) as Prisma.InputJsonValue,
          packageSpecs: (over.packageSpecs ?? undefined) as Prisma.InputJsonValue,
        },
      });
    }
    return og;
  };

  const mkFamily = async (over: {
    primaryOriginGoodId?: bigint | null;
    autoManaged?: boolean;
    memberIds?: bigint[];
  }) => {
    const family = await prisma.productFamily.create({
      data: {
        familyName: `测试族-${stamp}-${createdFamilyIds.length}`,
        autoManaged: over.autoManaged ?? true,
        primaryOriginGoodId: over.primaryOriginGoodId ?? null,
      },
    });
    createdFamilyIds.push(family.id);
    if (over.memberIds?.length) {
      await prisma.originGood.updateMany({
        where: { id: { in: over.memberIds } },
        data: { familyId: family.id },
      });
    }
    return family;
  };

  beforeAll(async () => {
    const moduleRef = await Test.createTestingModule({
      providers: [FamilyRecomputeService, PrismaService],
    }).compile();
    service = moduleRef.get(FamilyRecomputeService);
    organize = new OrganizeService(
      moduleRef.get(PrismaService),
      service,
      new ProductFamiliesService(moduleRef.get(PrismaService), service),
    );
    prisma = moduleRef.get(PrismaService);
    await prisma.onModuleInit();
  });

  afterAll(async () => {
    await prisma.originGood.deleteMany({ where: { id: { in: createdOriginGoodIds } } });
    await prisma.productFamily.deleteMany({ where: { id: { in: createdFamilyIds } } });
    await prisma.tag.deleteMany({ where: { id: { in: createdTagIds } } });
    await prisma.tagGroup.deleteMany({ where: { id: { in: createdTagGroupIds } } });
    await prisma.$disconnect();
  });

  it('并集尺码表：覆盖最全的成员优先、主链接次之、按首现顺序合并', async () => {
    // A：主链接，2 行；C：非主链接，3 行 → C 的 S 行胜出
    const a = await mkOriginGood({
      craftLabel: '单面印花',
      logisticsLabel: '包邮',
      sizeChart: { columns: [{ key: 'chest', name: '胸围' }], rows: [
        { sizeId: 'size_S', sizeName: 'S', chest: 100 },
        { sizeId: 'size_XL', sizeName: 'XL', chest: 108 },
      ] },
    });
    const c = await mkOriginGood({
      craftLabel: '单面印花',
      logisticsLabel: '包邮',
      sizeChart: { columns: [{ key: 'chest', name: '胸围' }], rows: [
        { sizeId: 'size_S', sizeName: 'S', chest: 98 },
        { sizeId: 'size_XXL', sizeName: 'XXL', chest: 112 },
        { sizeId: 'size_XXXL', sizeName: 'XXXL', chest: 116 },
      ] },
    });
    const family = await mkFamily({ primaryOriginGoodId: a.id, memberIds: [a.id, c.id] });

    await service.recomputeFamily(family.id);

    const after = await prisma.productFamily.findUniqueOrThrow({ where: { id: family.id } });
    const chart = after.sizeChart as { rows: Array<{ sizeName: string; chest: number }> };
    const names = chart.rows.map((r) => r.sizeName);
    expect(names).toEqual(['S', 'XXL', 'XXXL', 'XL']); // C(3行) 优先遍历，A 补充 XL
    expect(chart.rows.find((r) => r.sizeName === 'S')?.chest).toBe(98); // 冲突取覆盖最全成员
    expect(after.stale).toBe(false);
  });

  it('价格矩阵：五维格子取最低价并累积来源；维度取整理派生的链接标签；停用变体不参与', async () => {
    const a = await mkOriginGood({
      source: 'SDS',
      goodName: '美国（包邮）测试A-PA1-单面印花',
      variants: [
        { sdsVariantId: 'v1', sku: 'A-S-BLK', sizeId: 'size_S', sizeName: 'S', colorId: 'color_blk', colorName: '黑色', price: 25 },
        { sdsVariantId: 'v2', sku: 'A-XL-WHT', sizeId: 'size_XL', sizeName: 'XL', colorId: 'color_wht', colorName: '白色', price: 27 },
        { sdsVariantId: 'v3', sku: 'A-S-BLK-OFF', sizeId: 'size_S', sizeName: 'S', colorId: 'color_blk', colorName: '黑色', price: 20, enabled: false },
      ],
    });
    const b = await mkOriginGood({
      source: 'SDS',
      goodName: '美国（包邮）测试B-PB1-单面印花', // 同格子（不同仓库）
      variants: [
        { sdsVariantId: 'v4', sku: 'B-S-BLK', sizeId: 'size_S', sizeName: 'S', colorId: 'color_blk', colorName: '黑色', price: 24.5 },
        { sdsVariantId: 'v5', sku: 'B-XXXL-BLK', sizeId: 'size_XXXL', sizeName: 'XXXL', colorId: 'color_blk', colorName: '黑色', price: 29 },
      ],
    });
    const d = await mkOriginGood({
      source: 'SDS',
      goodName: '美国（包邮）测试D-PD1-直喷双面',
      variants: [
        { sdsVariantId: 'v6', sku: 'D-S-BLK', sizeId: 'size_S', sizeName: 'S', colorId: 'color_blk', colorName: '黑色', price: 30 },
      ],
    });
    const e = await mkOriginGood({
      source: 'SDS',
      goodName: '美国（不包邮）测试E-PE1-光板', // 整理派生：不打印 + 不包邮（无印花数量标签 → 结构化占位单面）
      variants: [
        { sdsVariantId: 'v7', sku: 'E-S-BLK', sizeId: 'size_S', sizeName: 'S', colorId: 'color_blk', colorName: '黑色', price: 22 },
      ],
    });
    const family = await mkFamily({ primaryOriginGoodId: a.id, memberIds: [a.id, b.id, d.id, e.id] });

    // 整理（显式派生标签）→ 重算（纯聚合）。运行时不解析名称，未派生的成员不进矩阵
    await organize.deriveFamilyTags(family.id);
    await service.recomputeFamily(family.id);

    const after = await prisma.productFamily.findUniqueOrThrow({ where: { id: family.id } });
    const matrix = after.priceMatrix as any;
    const cell = matrix.rows.find(
      (r: any) => r.sizeId === 'size_S' && r.colorId === 'color_blk'
        && r.printCount === '单面印花' && r.craft === '烫画' && r.logistics === '包邮',
    );
    expect(cell.price).toBe('24.5');
    expect(cell.manual).toBe(false);
    expect(cell.sources).toHaveLength(2); // a.v1 + b.v4；停用 v3 排除
    // 直喷双面 → 印花数量/工艺两维正确拆分
    const dg = matrix.rows.find((r: any) => r.printCount === '双面印花' && r.craft === '直喷' && r.price === '30');
    expect(dg).toBeTruthy();
    // 光板 → 工艺=不打印、印花数量占位单面、物流=不包邮
    const blank = matrix.rows.find((r: any) => r.craft === '不打印' && r.logistics === '不包邮');
    expect(blank).toBeTruthy();
    expect(blank.printCount).toBe('单面印花');
    expect(matrix.printCounts.sort()).toEqual(['单面印花', '双面印花']);
    expect(matrix.crafts.sort()).toEqual(['不打印', '烫画', '直喷']);
    expect(matrix.logistics.sort()).toEqual(['不包邮', '包邮']);
    expect(matrix.sizes.map((s: any) => s.name).sort()).toEqual(['S', 'XL', 'XXXL']);
  });

  it('价格矩阵：未整理（无标签）的 SDS 成员不进矩阵', async () => {
    const a = await mkOriginGood({
      source: 'SDS',
      goodName: '美国（包邮）测试U-PU1-单面印花',
      variants: [
        { sdsVariantId: 'v1', sku: 'U-S-BLK', sizeId: 'size_S', sizeName: 'S', colorId: 'color_blk', colorName: '黑色', price: 25 },
      ],
    });
    const family = await mkFamily({ primaryOriginGoodId: a.id, memberIds: [a.id] });
    // 只重算、不整理 → 无标签 → 空矩阵
    await service.recomputeFamily(family.id);
    let after = await prisma.productFamily.findUniqueOrThrow({ where: { id: family.id } });
    expect((after.priceMatrix as any).rows).toHaveLength(0);
    // 整理后进入矩阵
    await organize.deriveFamilyTags(family.id);
    await service.recomputeFamily(family.id);
    after = await prisma.productFamily.findUniqueOrThrow({ where: { id: family.id } });
    expect((after.priceMatrix as any).rows).toHaveLength(1);
  });

  it('价格矩阵：人工接管标签即权威维度（人工标签 > 一切）', async () => {
    const a = await mkOriginGood({
      goodName: '美国（包邮）测试M-PM1-单面印花', // 名称派生是 单面/烫画/包邮
      variants: [
        { sdsVariantId: 'v1', sku: 'M-S-BLK', sizeId: 'size_S', sizeName: 'S', colorId: 'color_blk', colorName: '黑色', price: 25 },
      ],
    });
    // 人工把该链接标签改成 双面印花/直喷/不包邮（手工造标签行，模拟 updateTags 结果）
    const tagIds: bigint[] = [];
    for (const groupName of ['印花数量', '印刷工艺', '物流渠道']) {
      const group = await prisma.tagGroup.findFirst({ where: { groupName } });
      const g = group ?? (await prisma.tagGroup.create({ data: { groupName } }));
      if (!group) createdTagGroupIds.push(g.id);
      for (const tagName of groupName === '印花数量' ? ['双面印花'] : groupName === '印刷工艺' ? ['直喷'] : ['不包邮']) {
        const existing = await prisma.tag.findFirst({ where: { tagName } });
        const t = existing ?? (await prisma.tag.create({ data: { tagName, tagGroupId: g.id } }));
        if (!existing) createdTagIds.push(t.id);
        tagIds.push(t.id);
      }
    }
    await prisma.originGoodTag.createMany({
      data: tagIds.map((tagId) => ({ originGoodId: a.id, tagId, manual: true })),
    });
    const family = await mkFamily({ primaryOriginGoodId: a.id, memberIds: [a.id] });

    await service.recomputeFamily(family.id);

    const after = await prisma.productFamily.findUniqueOrThrow({ where: { id: family.id } });
    const matrix = after.priceMatrix as any;
    expect(matrix.rows).toHaveLength(1);
    expect(matrix.rows[0].printCount).toBe('双面印花');
    expect(matrix.rows[0].craft).toBe('直喷');
    expect(matrix.rows[0].logistics).toBe('不包邮');
  });

  it('覆盖：命中改价 manual=true，未命中新增行并扩充选项', async () => {
    const a = await mkOriginGood({
      goodName: '美国（包邮）测试O-PO1-单面印花',
      variants: [
        { sdsVariantId: 'v1', sku: 'A-S-BLK', sizeId: 'size_S', sizeName: 'S', colorId: 'color_blk', colorName: '黑色', price: 25 },
      ],
    });
    const family = await mkFamily({ primaryOriginGoodId: a.id, memberIds: [a.id] });
    await prisma.familyPriceOverride.createMany({
      data: [
        { familyId: family.id, sizeId: 'size_S', colorId: 'color_blk', printCount: '单面印花', craft: '烫画', logistics: '包邮', price: new Prisma.Decimal(23) },
        { familyId: family.id, sizeId: 'size_M', colorId: 'color_red', printCount: '四面印花', craft: '丝印', logistics: '海运', price: new Prisma.Decimal(40) },
      ],
    });

    await service.recomputeFamily(family.id);

    const after = await prisma.productFamily.findUniqueOrThrow({ where: { id: family.id } });
    const matrix = after.priceMatrix as any;
    const hit = matrix.rows.find(
      (r: any) => r.sizeId === 'size_S' && r.colorId === 'color_blk' && r.printCount === '单面印花' && r.craft === '烫画' && r.logistics === '包邮',
    );
    expect(hit.price).toBe('23');
    expect(hit.manual).toBe(true);
    const added = matrix.rows.find((r: any) => r.sizeId === 'size_M' && r.craft === '丝印');
    expect(added).toBeTruthy();
    expect(added.manual).toBe(true);
    expect(added.printCount).toBe('四面印花');
    expect(added.sources).toEqual([]);
    expect(matrix.crafts).toContain('丝印');
    expect(matrix.printCounts).toContain('四面印花');
    expect(matrix.logistics).toContain('海运');
    expect(matrix.sizes.some((s: any) => s.key === 'size_M')).toBe(true);
  });

  it('autoManaged=false：只置 stale，不覆盖物化字段', async () => {
    const a = await mkOriginGood({
      craftLabel: '单面印花',
      logisticsLabel: '包邮',
      variants: [
        { sdsVariantId: 'v1', sku: 'A-S-BLK', sizeId: 'size_S', sizeName: 'S', colorId: 'color_blk', colorName: '黑色', price: 25 },
      ],
    });
    const family = await mkFamily({ primaryOriginGoodId: a.id, memberIds: [a.id], autoManaged: false });
    await prisma.productFamily.update({
      where: { id: family.id },
      data: { priceMatrix: { version: 'locked' } as Prisma.InputJsonValue },
    });

    await service.recomputeFamily(family.id);

    const after = await prisma.productFamily.findUniqueOrThrow({ where: { id: family.id } });
    expect(after.stale).toBe(true);
    expect((after.priceMatrix as any).version).toBe('locked'); // 未被覆盖
  });

  it('canonical detail：为空时从主链接初始化一次，非空不动', async () => {
    const a = await mkOriginGood({ craftLabel: '单面印花', logisticsLabel: '包邮' });
    await prisma.originGoodDetail.create({
      data: { originGoodId: a.id, englishName: 'Cotton Tee', materialDescription: '100% 棉' },
    });
    const b = await mkOriginGood({ craftLabel: '单面印花', logisticsLabel: '包邮' });
    await prisma.originGoodDetail.create({
      data: { originGoodId: b.id, englishName: 'Should Not Win' },
    });
    const family = await mkFamily({ primaryOriginGoodId: a.id, memberIds: [a.id, b.id] });

    await service.recomputeFamily(family.id);
    let after = await prisma.productFamily.findUniqueOrThrow({ where: { id: family.id } });
    expect((after.detail as any).englishName).toBe('Cotton Tee');
    expect((after.detail as any).materialDescription).toBe('100% 棉');

    await prisma.productFamily.update({
      where: { id: family.id },
      data: { detail: { englishName: '人工已改' } as Prisma.InputJsonValue },
    });
    await service.recomputeFamily(family.id);
    after = await prisma.productFamily.findUniqueOrThrow({ where: { id: family.id } });
    expect((after.detail as any).englishName).toBe('人工已改');
  });

  it('族不存在时 no-op 不抛错', async () => {
    await expect(service.recomputeFamily(999999n)).resolves.toBeUndefined();
  });

  it('enqueue 去重且串行执行', async () => {
    const a = await mkOriginGood({
      craftLabel: '单面印花',
      logisticsLabel: '包邮',
      variants: [{ sdsVariantId: 'v1', sku: 'A-S-BLK', sizeId: 'size_S', sizeName: 'S', colorId: 'color_blk', colorName: '黑色', price: 25 }],
    });
    const family = await mkFamily({ primaryOriginGoodId: a.id, memberIds: [a.id] });
    const spy = jest.spyOn(service, 'recomputeFamily').mockResolvedValue();

    service.enqueue(family.id);
    service.enqueue(family.id);
    service.enqueue(family.id);
    await new Promise((r) => setTimeout(r, 50));

    expect(spy).toHaveBeenCalledTimes(1);
    spy.mockRestore();
  });
});
