import { Prisma } from '@prisma/client';
import { SdsProductDetail, SdsProductVariant } from './sds-client.service';

type TableCell = { content?: unknown; remark?: unknown };
type Table = TableCell[][];

export interface NormalizedVariant {
  sdsVariantId: string;
  sku: string;
  sizeId: string | null;
  sizeName: string | null;
  colorId: string | null;
  colorName: string | null;
  colorHex: string | null;
  imageUrl: string | null;
  price: Prisma.Decimal | null;
  originalPrice: Prisma.Decimal | null;
  weightG: Prisma.Decimal | null;
  boxLengthCm: Prisma.Decimal | null;
  boxWidthCm: Prisma.Decimal | null;
  boxHeightCm: Prisma.Decimal | null;
  enabled: boolean;
  sortOrder: number;
  designData: Prisma.InputJsonValue | null;
}

export interface NormalizedProductDetail {
  productCode: string | null;
  englishName: string | null;
  blankDesignUrl: string | null;
  detailsPageVideoUrl: string | null;
  textureName: string | null;
  productionCycleHours: number | null;
  minWeightG: Prisma.Decimal | null;
  reminder: string | null;
  productionProcess: string | null;
  materialDescription: string | null;
  productPerformance: string | null;
  applicableScenarios: string | null;
  washingInstructions: string | null;
  specialDescription: string | null;
  designExplanation: string | null;
  designArea: string | null;
  pictureRequest: string | null;
  sizeChart: Prisma.InputJsonValue | null;
  packageSpecs: Prisma.InputJsonValue | null;
  options: Prisma.InputJsonValue | null;
  media: Prisma.InputJsonValue | null;
  upstreamUpdatedAt: Date | null;
  variants: NormalizedVariant[];
}

const text = (value: unknown): string | null => {
  if (value === undefined || value === null) return null;
  const normalized = String(value).trim();
  return normalized.length > 0 ? normalized : null;
};

const decimal = (value: unknown): Prisma.Decimal | null => {
  if (value === undefined || value === null || value === '') return null;
  const n = Number(value);
  return Number.isFinite(n) ? new Prisma.Decimal(n) : null;
};

const integer = (value: unknown): number | null => {
  const n = Number(value);
  return Number.isInteger(n) ? n : null;
};

function parseTable(raw: unknown): Table | null {
  if (typeof raw !== 'string' || raw.trim() === '') return null;
  try {
    const parsed = JSON.parse(raw) as unknown;
    if (!Array.isArray(parsed) || parsed.length < 2) return null;
    const rows = parsed.filter(Array.isArray) as Table;
    return rows.length >= 2 ? rows : null;
  } catch {
    return null;
  }
}

const cell = (row: TableCell[], index: number): string =>
  text(row[index]?.content)?.replace(/\t/g, '').trim() ?? '';

const measurementKey = (header: string, index: number): string => {
  if (header.includes('衣长')) return 'bodyLength';
  if (header.includes('胸围')) return 'chest';
  if (header.includes('肩宽')) return 'shoulder';
  if (header.includes('袖长')) return 'sleeveLength';
  return `measurement${index}`;
};

export function parseSizeChart(raw: unknown): Prisma.InputJsonValue | null {
  const table = parseTable(raw);
  if (!table) return null;
  const [header, ...body] = table;
  const columns = header.slice(1).map((item, index) => {
    const name = text(item.content)?.replace(/\s*\(cm\/in\)\s*/i, '') ?? `规格${index + 1}`;
    return { key: measurementKey(name, index + 1), name };
  });
  const rows = body
    .map((row, rowIndex) => {
      const sizeName = cell(row, 0);
      if (!sizeName) return null;
      return {
        sizeId: `size_${rowIndex}`,
        sizeName,
        measurements: columns.map((column, index) => {
          const cm = cell(row, index + 1);
          const cmNumber = Number(cm);
          return {
            key: column.key,
            cm: cm || null,
            in: Number.isFinite(cmNumber) ? (cmNumber / 2.54).toFixed(2) : null,
          };
        }),
      };
    })
    .filter((row): row is NonNullable<typeof row> => row !== null);
  return { columns, rows } as Prisma.InputJsonValue;
}

function dimensions(value: string): { length: string; width: string; height: string } | null {
  const parts = value
    .replace(/[×x]/gi, '*')
    .split('*')
    .map((part) => part.trim());
  if (parts.length !== 3 || parts.some((part) => !Number.isFinite(Number(part)))) return null;
  return { length: parts[0], width: parts[1], height: parts[2] };
}

export function parsePackageSpecs(raw: unknown): Prisma.InputJsonValue | null {
  const table = parseTable(raw);
  if (!table) return null;
  const rows = table.slice(1)
    .map((row, rowIndex) => {
      const sizeName = cell(row, 0);
      if (!sizeName) return null;
      return {
        sizeId: `size_${rowIndex}`,
        sizeName,
        dimensionsCm: dimensions(cell(row, 1)),
        dimensionsIn: dimensions(cell(row, 2)),
        volumeCm3: cell(row, 3) || null,
        volumeIn3: cell(row, 4) || null,
        grossWeightG: cell(row, 5) || null,
        grossWeightLb: cell(row, 6) || null,
      };
    })
    .filter((row): row is NonNullable<typeof row> => row !== null);
  return { rows } as Prisma.InputJsonValue;
}

function normalizeOptions(detail: SdsProductDetail): Prisma.InputJsonValue | null {
  const attributers = detail.subproducts?.attributers;
  if (!Array.isArray(attributers)) return null;
  const sizeMap = new Map<string, { id: string; name: string; sortOrder: number; enabled: boolean }>();
  const colorMap = new Map<string, { id: string; name: string; hex: string | null; sortOrder: number; enabled: boolean }>();
  attributers.forEach((attribute, sizeIndex) => {
    const sizeName = text(attribute.size);
    const sizeId = text(attribute.sizeId) ?? `size_${sizeIndex}`;
    if (sizeName) sizeMap.set(sizeId, { id: sizeId, name: sizeName, sortOrder: sizeIndex, enabled: true });
    if (Array.isArray(attribute.colors)) {
      attribute.colors.forEach((color, colorIndex) => {
        const colorId = text(color.colorId) ?? `color_${colorIndex}`;
        if (!colorMap.has(colorId)) {
          colorMap.set(colorId, {
            id: colorId,
            name: text(color.chineseName) ?? text(color.color_name) ?? colorId,
            hex: text(color.color),
            sortOrder: integer(color.colorSort) ?? colorIndex,
            enabled: true,
          });
        }
      });
    }
  });
  return { sizes: [...sizeMap.values()], colors: [...colorMap.values()] } as Prisma.InputJsonValue;
}

function normalizeMedia(detail: SdsProductDetail, variants: SdsProductVariant[]): Prisma.InputJsonValue | null {
  const urls: string[] = [];
  const add = (value: unknown) => {
    const url = text(value);
    if (url && !urls.includes(url)) urls.push(url);
  };
  add(detail.blankDesignUrl);
  add(detail.psd_img_url);
  add(detail.img_url);
  for (const variant of variants) {
    add(variant.psd_img_url);
    add(variant.img_url);
    add(variant.blankDesignUrl);
    for (const image of variant.designPrototype?.detailImgUrls ?? []) add(image.imageUrl);
    for (const image of variant.designPrototype?.prototypeResultGroups ?? []) add(image.resultImage);
  }
  if (urls.length === 0) return null;
  return {
    primaryImageUrl: urls[0],
    images: urls.map((url, index) => ({ id: `image_${index}`, url, sortOrder: index })),
  } as Prisma.InputJsonValue;
}

function normalizeVariant(variant: SdsProductVariant, index: number): NormalizedVariant | null {
  const sdsVariantId = text(variant.id);
  const sku = text(variant.sku);
  if (!sdsVariantId || !sku) return null;
  return {
    sdsVariantId,
    sku,
    sizeId: text(variant.sizeId) ?? text(variant.sizeDto?.id),
    sizeName: text(variant.size) ?? text(variant.sizeDto?.sizeName),
    colorId: text(variant.colorId) ?? text(variant.color?.colorId),
    colorName: text(variant.color?.chineseName) ?? text(variant.color_name) ?? text(variant.color?.color_name),
    colorHex: text(variant.color?.color),
    imageUrl: text(variant.psd_img_url) ?? text(variant.img_url) ?? text(variant.blankDesignUrl),
    price: decimal(variant.currentPrice ?? variant.unit_price ?? variant.min_price),
    originalPrice: decimal(variant.originalPrice),
    weightG: decimal(variant.weight),
    boxLengthCm: decimal(variant.box_length),
    boxWidthCm: decimal(variant.box_width),
    boxHeightCm: decimal(variant.box_height),
    enabled: Number(variant.status ?? 1) === 1 && String(variant.delFlag ?? '0') === '0',
    sortOrder: integer(variant.attribute_sort?.split('-')[0]) ?? integer(variant.size_sort) ?? index,
    designData: variant.designPrototype ? (variant.designPrototype as Prisma.InputJsonValue) : null,
  };
}

export function normalizeProductDetail(detail: SdsProductDetail): NormalizedProductDetail {
  const productDetails = detail.product_details ?? {};
  const sourceVariants = Array.isArray(detail.subproducts?.items) ? detail.subproducts!.items! : [];
  const variants = sourceVariants
    .map(normalizeVariant)
    .filter((variant): variant is NormalizedVariant => variant !== null);
  const updatedAt = Number(detail.updateTime);
  return {
    productCode: text(detail.sku),
    englishName: text(detail.english_name),
    blankDesignUrl: text(detail.blankDesignUrl),
    detailsPageVideoUrl: text(detail.detailsPageVideoUrl),
    textureName: text(detail.texture?.name),
    productionCycleHours: integer(detail.productionCycle),
    minWeightG: decimal(detail.minWeight),
    reminder: text(productDetails.reminder),
    productionProcess: text(productDetails.production_process),
    materialDescription: text(productDetails.material_description),
    productPerformance: text(productDetails.product_performance),
    applicableScenarios: text(productDetails.applicable_scenarios),
    washingInstructions: text(productDetails.washing_instructions),
    specialDescription: text(productDetails.special_description),
    designExplanation: text(productDetails.design_explanation),
    designArea: text(productDetails.design_area),
    pictureRequest: text(productDetails.picture_request),
    sizeChart: parseSizeChart(productDetails.product_size),
    packageSpecs: parsePackageSpecs(productDetails.packaging_specification),
    options: normalizeOptions(detail),
    media: normalizeMedia(detail, sourceVariants),
    upstreamUpdatedAt: Number.isFinite(updatedAt) ? new Date(updatedAt) : null,
    variants,
  };
}
