import assert from "node:assert/strict";
import test from "node:test";
import { strFromU8, strToU8, unzipSync, zipSync } from "fflate";
import { buildRecoveryWorkbook } from "../lib/recovery-workbook.ts";
import {
  PARTNER_BATCH_HEADERS,
  buildPartnerBatchWorkbookColumns,
  parsePartnerBatchWorkbook,
  resolvePartnerWorkbookOrigin,
} from "../lib/partner-batch-workbook.ts";
import { hasMp4FileSignature } from "../lib/video-file.ts";

const png = Uint8Array.from([
  0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
  0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
]);

test("round-trips hidden assignment IDs and embedded backfill screenshots", () => {
  const workbook = buildRecoveryWorkbook({
    sheetName: "批量回填",
    headers: [...PARTNER_BATCH_HEADERS],
    columnWidths: [10, 20, 40, 30, 40, 24, 28, 20, 20, 20],
    hiddenColumns: [7, 8, 9],
    rows: [
      {
        cells: [
          1,
          "测试笔记",
          "正文 #话题",
          "",
          "https://www.xiaohongshu.com/explore/1234567890abcdef",
          "",
          "",
          "distribution-1",
          "",
          "",
        ],
        images: [
          {
            column: 3,
            image: { bytes: png, contentType: "image/png", description: "原图" },
          },
          {
            column: 5,
            image: { bytes: png, contentType: "image/png", description: "笔记截图" },
          },
          {
            column: 6,
            image: { bytes: png, contentType: "image/png", description: "数据截图" },
          },
        ],
      },
    ],
  });
  const rows = parsePartnerBatchWorkbook(workbook);
  assert.equal(rows.length, 1);
  assert.equal(rows[0].distributionId, "distribution-1");
  assert.equal(rows[0].title, "测试笔记");
  assert.match(rows[0].publishUrl, /xiaohongshu\.com/);
  assert.equal(rows[0].publishScreenshot?.contentType, "image/png");
  assert.equal(rows[0].creatorScreenshot?.contentType, "image/png");

  const entries = unzipSync(workbook);
  const worksheet = strFromU8(entries["xl/worksheets/sheet1.xml"]);
  assert.match(worksheet, /序号（不能改）/);
  assert.doesNotMatch(worksheet, /张原图（见图）|已回填（见图）|请插入笔记截图/);
  assert.match(worksheet, /<drawing r:id="rId1"\/>/);
  assert.doesNotMatch(worksheet, /#VALUE!/);
  const drawing = strFromU8(entries["xl/drawings/drawing1.xml"]);
  assert.equal((drawing.match(/<xdr:twoCellAnchor editAs="twoCell">/g) ?? []).length, 3);
  assert.match(drawing, /<xdr:col>3<\/xdr:col>/);
  assert.match(drawing, /<xdr:col>5<\/xdr:col>/);
  assert.match(drawing, /<xdr:col>6<\/xdr:col>/);
  assert.match(worksheet, /min="8" max="8"[^>]*hidden="1"/);
});

test("accepts the legacy sequence header for previously exported workbooks", () => {
  const legacyHeaders = [...PARTNER_BATCH_HEADERS];
  legacyHeaders[0] = "序号";
  const workbook = buildRecoveryWorkbook({
    sheetName: "批量回填",
    headers: legacyHeaders,
    columnWidths: [10, 20, 40, 30, 40, 24, 28, 20, 20, 20],
    hiddenColumns: [7, 8, 9],
    rows: [
      {
        cells: [
          1,
          "旧模板笔记",
          "正文",
          "",
          "",
          "",
          "",
          "distribution-legacy",
          "",
          "",
        ],
        images: [],
      },
    ],
  });
  const rows = parsePartnerBatchWorkbook(workbook);
  assert.equal(rows[0].sequence, "1");
  assert.equal(rows[0].distributionId, "distribution-legacy");
});

test("imports dynamic source image columns without confusing screenshot columns", () => {
  const headers = [
    "序号（不能改）",
    "标题",
    "笔记内容（正文+话题）",
    "图片1",
    "图片2",
    "发布链接",
    "笔记截图",
    "数据分析截图（单篇笔记数据分析截图）",
    "_系统笔记ID",
    "_原笔记截图",
    "_原数据分析截图",
  ];
  const workbook = buildRecoveryWorkbook({
    sheetName: "批量回填",
    headers,
    columnWidths: headers.map(() => 20),
    hiddenColumns: [8, 9, 10],
    rows: [
      {
        cells: [
          1,
          "多图笔记",
          "正文",
          "",
          "",
          "https://www.xiaohongshu.com/explore/dynamic",
          "",
          "",
          "distribution-dynamic",
          "",
          "",
        ],
        images: [
          {
            column: 3,
            image: { bytes: png, contentType: "image/png", description: "原图1" },
          },
          {
            column: 4,
            image: { bytes: png, contentType: "image/png", description: "原图2" },
          },
          {
            column: 6,
            image: { bytes: png, contentType: "image/png", description: "笔记截图" },
          },
          {
            column: 7,
            image: { bytes: png, contentType: "image/png", description: "数据截图" },
          },
        ],
      },
    ],
  });
  const rows = parsePartnerBatchWorkbook(workbook);
  assert.equal(rows[0].distributionId, "distribution-dynamic");
  assert.equal(rows[0].publishScreenshot?.contentType, "image/png");
  assert.equal(rows[0].creatorScreenshot?.contentType, "image/png");
});

test("imports screenshots saved by WPS as DISPIMG cell images", () => {
  const sourceImage = Uint8Array.from([...png, 1]);
  const publishScreenshot = Uint8Array.from([...png, 2]);
  const creatorScreenshot = Uint8Array.from([...png, 3]);
  const worksheet = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
      <sheetData>
        <row r="1">
          <c r="A1" t="inlineStr"><is><t>序号（不能改）</t></is></c>
          <c r="B1" t="inlineStr"><is><t>标题</t></is></c>
          <c r="C1" t="inlineStr"><is><t>笔记内容（正文+话题）</t></is></c>
          <c r="D1" t="inlineStr"><is><t>图片1</t></is></c>
          <c r="E1" t="inlineStr"><is><t>发布链接</t></is></c>
          <c r="F1" t="inlineStr"><is><t>笔记截图</t></is></c>
          <c r="G1" t="inlineStr"><is><t>数据分析截图（单篇笔记数据分析截图）</t></is></c>
          <c r="H1" t="inlineStr"><is><t>_系统笔记ID</t></is></c>
          <c r="I1" t="inlineStr"><is><t>_原笔记截图</t></is></c>
          <c r="J1" t="inlineStr"><is><t>_原数据分析截图</t></is></c>
        </row>
        <row r="2">
          <c r="A2"><v>1</v></c>
          <c r="B2" t="inlineStr"><is><t>WPS 笔记</t></is></c>
          <c r="D2" t="str"><f>_xlfn.DISPIMG(&quot;SOURCE&quot;,1)</f><v>=DISPIMG(&quot;SOURCE&quot;,1)</v></c>
          <c r="E2" t="inlineStr"><is><t>https://www.xiaohongshu.com/explore/wps</t></is></c>
          <c r="F2" t="str"><f>_xlfn.DISPIMG(&quot;PUBLISH&quot;,1)</f><v>=DISPIMG(&quot;PUBLISH&quot;,1)</v></c>
          <c r="G2" t="str"><f>_xlfn.DISPIMG(&quot;CREATOR&quot;,1)</f><v>=DISPIMG(&quot;CREATOR&quot;,1)</v></c>
          <c r="H2" t="inlineStr"><is><t>distribution-wps</t></is></c>
        </row>
      </sheetData>
    </worksheet>`;
  const cellImages = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <etc:cellImages xmlns:etc="http://www.wps.cn/officeDocument/2017/etCustomData" xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
      <etc:cellImage><xdr:pic><xdr:nvPicPr><xdr:cNvPr id="1" name="SOURCE"/></xdr:nvPicPr><xdr:blipFill><a:blip r:embed="rId1"/></xdr:blipFill></xdr:pic></etc:cellImage>
      <etc:cellImage><xdr:pic><xdr:nvPicPr><xdr:cNvPr id="2" name="PUBLISH"/></xdr:nvPicPr><xdr:blipFill><a:blip r:embed="rId2"/></xdr:blipFill></xdr:pic></etc:cellImage>
      <etc:cellImage><xdr:pic><xdr:nvPicPr><xdr:cNvPr id="3" name="CREATOR"/></xdr:nvPicPr><xdr:blipFill><a:blip r:embed="rId3"/></xdr:blipFill></xdr:pic></etc:cellImage>
    </etc:cellImages>`;
  const relationships = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
      <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/source.png"/>
      <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/publish.png"/>
      <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/creator.png"/>
    </Relationships>`;
  const workbook = zipSync({
    "xl/worksheets/sheet1.xml": strToU8(worksheet),
    "xl/cellimages.xml": strToU8(cellImages),
    "xl/_rels/cellimages.xml.rels": strToU8(relationships),
    "xl/media/source.png": sourceImage,
    "xl/media/publish.png": publishScreenshot,
    "xl/media/creator.png": creatorScreenshot,
  });

  const rows = parsePartnerBatchWorkbook(workbook);
  assert.equal(rows.length, 1);
  assert.equal(rows[0].distributionId, "distribution-wps");
  assert.deepEqual(rows[0].publishScreenshot?.bytes, publishScreenshot);
  assert.deepEqual(rows[0].creatorScreenshot?.bytes, creatorScreenshot);
});

test("builds video-task workbooks with video columns and no source image columns", () => {
  const columns = buildPartnerBatchWorkbookColumns({
    contentFormat: "video",
    maxSourceImages: 3,
    maxSourceVideos: 2,
  });
  assert.deepEqual(columns.headers.slice(0, 8), [
    "序号（不能改）",
    "标题",
    "笔记内容（正文+话题）",
    "视频1",
    "视频2",
    "发布链接",
    "笔记截图",
    "数据分析截图（单篇笔记数据分析截图）",
  ]);
  assert.equal(columns.headers.some((header) => /^图片\d+$/.test(header)), false);

  const workbook = buildRecoveryWorkbook({
    sheetName: "视频批量回填",
    headers: columns.headers,
    columnWidths: columns.columnWidths,
    hiddenColumns: [
      columns.systemColumn,
      columns.systemColumn + 1,
      columns.systemColumn + 2,
    ],
    rows: [
      {
        cells: [
          1,
          "视频笔记",
          "视频正文 #测试",
          "下载视频1",
          "下载视频2",
          "",
          "",
          "",
          "distribution-video",
          "",
          "",
        ],
        images: [],
        hyperlinks: [
          {
            column: columns.sourceVideoStartColumn,
            url: "https://koc.example.com/api/partner-image?kind=video&download=1",
          },
        ],
      },
    ],
  });
  const entries = unzipSync(workbook);
  const worksheet = strFromU8(entries["xl/worksheets/sheet1.xml"]);
  const relationships = strFromU8(
    entries["xl/worksheets/_rels/sheet1.xml.rels"],
  );
  assert.match(worksheet, /视频1/);
  assert.doesNotMatch(worksheet, /图片1/);
  assert.match(relationships, /https:\/\/koc\.example\.com\/api\/partner-image/);
  assert.match(relationships, /download=1/);
});

test("uses the configured public origin before proxy or container addresses", () => {
  const request = new Request("http://app:3000/api/partner-batch-workbook", {
    headers: {
      host: "app:3000",
      "x-forwarded-host": "internal-proxy:8080",
      "x-forwarded-proto": "https",
    },
  });
  assert.equal(
    resolvePartnerWorkbookOrigin(request, "https://koc.example.com/koc/"),
    "https://koc.example.com",
  );
});

test("preserves a forwarded non-standard port when no origin is configured", () => {
  const request = new Request("http://app:3000/api/partner-batch-workbook", {
    headers: {
      host: "app:3000",
      "x-forwarded-host": "localhost:8080",
      "x-forwarded-proto": "http",
    },
  });
  assert.equal(resolvePartnerWorkbookOrigin(request), "http://localhost:8080");
});

test("recognizes MP4 bytes instead of trusting a response content type", () => {
  const mp4Header = Uint8Array.from([
    0x00, 0x00, 0x00, 0x18,
    0x66, 0x74, 0x79, 0x70,
    0x69, 0x73, 0x6f, 0x6d,
    0x00, 0x00, 0x02, 0x00,
    0x69, 0x73, 0x6f, 0x6d,
    0x6d, 0x70, 0x34, 0x32,
  ]);
  assert.equal(hasMp4FileSignature(mp4Header), true);
  assert.equal(hasMp4FileSignature(new TextEncoder().encode("not a video")), false);
});
