import { Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import { QueryOriginGoodDto } from './dto/query-origin-good.dto';

export interface PaginatedOriginGoods {
  items: Array<{
    id: string;
    sdsGoodId: string;
    goodName: string | null;
    goodImage: string | null;
    goodPrice: string | null;
    sdsCategoryId: string | null;
    createdAt: string;
    updatedAt: string;
  }>;
  total: number;
  page: number;
  pageSize: number;
}

/**
 * A single origin-good node inside the tree, augmented with configuration status
 * (how many `goods` rows reference it and which countries it has been configured for).
 */
export interface OriginGoodsTreeNode {
  id: string;
  goodName: string;
  goodImage: string | null;
  goodPrice: string | null;
  sdsGoodId: string;
  configuredCount: number;
  configuredCountries: string[];
  configuredTags: { tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: string | null; tagGroupName: string | null; sortOrder: number }[];
}

/** A category node in the hierarchical tree, with origin goods as leaves. */
export interface OriginGoodsTreeCategoryNode {
  categoryId: string;
  categoryName: string;
  sdsCategoryId: string | null;
  configuredCount: number;
  totalCount: number;
  children: OriginGoodsTreeCategoryNode[];
  originGoods: OriginGoodsTreeNode[];
}

/** Top-level tree response returned by `OriginGoodsService.getTree()`. */
export interface OriginGoodsTreeResponse {
  tree: OriginGoodsTreeCategoryNode[];
  totalOriginGoods: number;
  configuredCount: number;
}

@Injectable()
export class OriginGoodsService {
  constructor(private readonly prisma: PrismaService) {}

  async findAll(query: QueryOriginGoodDto): Promise<PaginatedOriginGoods> {
    const { page, pageSize, keyword } = query;
    const where: Prisma.OriginGoodWhereInput = keyword
      ? { goodName: { contains: keyword, mode: 'insensitive' } }
      : {};

    const [total, rows] = await this.prisma.$transaction([
      this.prisma.originGood.count({ where }),
      this.prisma.originGood.findMany({
        where,
        orderBy: { id: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
    ]);

    return {
      items: rows.map((r) => ({
        id: r.id.toString(),
        sdsGoodId: r.sdsGoodId,
        goodName: r.goodName,
        goodImage: r.goodImage,
        goodPrice: r.goodPrice === null || r.goodPrice === undefined ? null : r.goodPrice.toString(),
        sdsCategoryId: r.sdsCategoryId,
        createdAt: r.createdAt.toISOString(),
        updatedAt: r.updatedAt.toISOString(),
      })),
      total,
      page,
      pageSize,
    };
  }

  /**
   * Builds a hierarchical tree using the `categories` table parent-child
   * structure, placing each origin-good as a leaf under the category whose
   * `sdsCategoryId` matches the origin-good's `sdsCategoryId`.
   *
   * Origin-goods whose `sdsCategoryId` doesn't map to any category are placed
   * under a synthetic "未分类" root node.
   */
  async getTree(): Promise<OriginGoodsTreeResponse> {
    const [allCategories, allOriginGoods, configCounts, goodsWithCountries, goodsWithTags] =
      await Promise.all([
        this.prisma.category.findMany({
          where: { sdsCategoryId: { not: null } },
          orderBy: { categoryName: 'asc' },
          select: {
            id: true,
            categoryName: true,
            sdsCategoryId: true,
            parentCategoryId: true,
          },
        }),
        this.prisma.originGood.findMany({ orderBy: { goodName: 'asc' } }),
        this.prisma.good.groupBy({
          by: ['originGoodId'],
          _count: { _all: true },
        }),
        this.prisma.good.findMany({
          select: {
            originGoodId: true,
            country: { select: { countryName: true } },
          },
          distinct: ['originGoodId', 'countryId'],
        }),
        this.prisma.goodTag.findMany({
          select: {
            good: { select: { originGoodId: true } },
            tag: {
              select: {
                tagName: true,
                tagColor: true,
                tagFontColor: true,
                tagGroupId: true,
                sortOrder: true,
                tagGroup: { select: { groupName: true } },
              },
            },
          },
        }),
      ]);

    const countMap = new Map<string, number>();
    configCounts.forEach((c) =>
      countMap.set(c.originGoodId.toString(), c._count._all),
    );

    const countryMap = new Map<string, string[]>();
    goodsWithCountries.forEach((g) => {
      const key = g.originGoodId.toString();
      const name = g.country?.countryName;
      if (!name) return;
      const arr = countryMap.get(key);
      if (arr) arr.push(name);
      else countryMap.set(key, [name]);
    });

    const tagMap = new Map<string, { tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: string | null; tagGroupName: string | null; sortOrder: number }[]>();
    goodsWithTags.forEach((gt) => {
      const key = gt.good.originGoodId.toString();
      const tagInfo = {
        tagName: gt.tag.tagName,
        tagColor: gt.tag.tagColor,
        tagFontColor: gt.tag.tagFontColor,
        tagGroupId: gt.tag.tagGroupId?.toString() ?? null,
        tagGroupName: gt.tag.tagGroup?.groupName ?? null,
        sortOrder: gt.tag.sortOrder,
      };
      const arr = tagMap.get(key);
      if (arr) {
        if (!arr.some((t) => t.tagName === tagInfo.tagName)) arr.push(tagInfo);
      } else {
        tagMap.set(key, [tagInfo]);
      }
    });

    const sdsToCategory = new Map<
      string,
      (typeof allCategories)[number]
    >();
    for (const c of allCategories) {
      if (c.sdsCategoryId) sdsToCategory.set(c.sdsCategoryId, c);
    }

    const ogToCategory = new Map<string, string>();
    for (const og of allOriginGoods) {
      if (og.sdsCategoryId && sdsToCategory.has(og.sdsCategoryId)) {
        ogToCategory.set(og.id.toString(), sdsToCategory.get(og.sdsCategoryId)!.id.toString());
      }
    }

    const buildNode = (
      cat: (typeof allCategories)[number],
    ): OriginGoodsTreeCategoryNode => {
      const childrenCats = allCategories.filter(
        (c) => c.parentCategoryId !== null && c.parentCategoryId === cat.id,
      );
      const childNodes = childrenCats.map(buildNode);

      const ogsForThisCat = allOriginGoods.filter(
        (og) => ogToCategory.get(og.id.toString()) === cat.id.toString(),
      );
      const ogNodes: OriginGoodsTreeNode[] = ogsForThisCat.map((og) => ({
        id: og.id.toString(),
        goodName: og.goodName ?? `SDS-${og.sdsGoodId}`,
        goodImage: og.goodImage,
        goodPrice: og.goodPrice?.toString() ?? null,
        sdsGoodId: og.sdsGoodId,
        configuredCount: countMap.get(og.id.toString()) ?? 0,
        configuredCountries: countryMap.get(og.id.toString()) ?? [],
        configuredTags: tagMap.get(og.id.toString()) ?? [],
      }));

      const childTotal = childNodes.reduce((s, n) => s + n.totalCount, 0);
      const childConfigured = childNodes.reduce(
        (s, n) => s + n.configuredCount,
        0,
      );
      const ogConfigured = ogNodes.filter((o) => o.configuredCount > 0).length;

      return {
        categoryId: cat.id.toString(),
        categoryName: cat.categoryName,
        sdsCategoryId: cat.sdsCategoryId,
        configuredCount: childConfigured + ogConfigured,
        totalCount: childTotal + ogNodes.length,
        children: childNodes,
        originGoods: ogNodes,
      };
    };

    const roots = allCategories.filter((c) => c.parentCategoryId === null);
    const tree = roots.map(buildNode);

    const unmapped = allOriginGoods.filter(
      (og) => !ogToCategory.has(og.id.toString()),
    );
    if (unmapped.length > 0) {
      tree.push({
        categoryId: 'uncategorized',
        categoryName: '未分类',
        sdsCategoryId: null,
        configuredCount: unmapped.filter(
          (og) => (countMap.get(og.id.toString()) ?? 0) > 0,
        ).length,
        totalCount: unmapped.length,
        children: [],
        originGoods: unmapped.map((og) => ({
          id: og.id.toString(),
          goodName: og.goodName ?? `SDS-${og.sdsGoodId}`,
          goodImage: og.goodImage,
          goodPrice: og.goodPrice?.toString() ?? null,
          sdsGoodId: og.sdsGoodId,
          configuredCount: countMap.get(og.id.toString()) ?? 0,
          configuredCountries: countryMap.get(og.id.toString()) ?? [],
          configuredTags: tagMap.get(og.id.toString()) ?? [],
        })),
      });
    }

    tree.sort((a, b) => a.categoryName.localeCompare(b.categoryName, 'zh'));

    const totalConfigured = allOriginGoods.filter(
      (og) => (countMap.get(og.id.toString()) ?? 0) > 0,
    ).length;

    return {
      tree,
      totalOriginGoods: allOriginGoods.length,
      configuredCount: totalConfigured,
    };
  }
}
