import { getRuntimeEnv } from "../../../lib/runtime-env";
import type { StoredObjectBody } from "../../../lib/object-store";
import { adminForbidden, isAdminRequest } from "../../../lib/admin-auth";
import { parseStoredDate } from "../../../lib/date-utils";
import {
  downloadFeishuMedia,
  type FeishuBindings,
} from "../../../lib/feishu-client";
import {
  ensureSchema,
  getRawDb,
  getUploadBucket,
} from "../../../lib/mvp-db";
import {
  buildRecoveryWorkbook,
  type RecoveryWorkbookImage,
  type RecoveryWorkbookRow,
} from "../../../lib/recovery-workbook";
import { consumeMcpExportToken } from "../../../lib/mcp-export-token";
import { normalizeWorkbookImage } from "../../../lib/workbook-image";

const env = getRuntimeEnv();

type StoredAsset = {
  index: number;
  key: string;
  fileToken?: string;
  width?: number | null;
  height?: number | null;
};

type ExportRow = {
  content_id: string;
  source_row: number | null;
  title: string;
  body: string;
  image_assets: string;
  distribution_id: string | null;
  publish_url: string | null;
  publish_time: string | null;
  publish_screenshot_key: string | null;
  screenshot_key: string | null;
  exposure: number | null;
  views: number | null;
  d2_likes: number | null;
  d2_comments: number | null;
  d2_collects: number | null;
  d5_likes: number | null;
  d5_comments: number | null;
  d5_collects: number | null;
  d7_likes: number | null;
  d7_comments: number | null;
  d7_collects: number | null;
  latest_likes: number | null;
  latest_comments: number | null;
  latest_collects: number | null;
  latest_shares: number | null;
  collection_status: string | null;
  collection_status_description: string | null;
  collection_updated_at: string | null;
  updated_at: string | null;
  account_nickname: string | null;
  partner_name: string | null;
  claimant_name: string | null;
};

type ImageReference = StoredAsset & {
  description: string;
};

function parseAssets(value: string) {
  try {
    const parsed = JSON.parse(value || "[]") as StoredAsset[];
    if (!Array.isArray(parsed)) return [];
    return parsed
      .filter(
        (asset) =>
          Number.isInteger(Number(asset.index)) &&
          Number(asset.index) > 0 &&
          typeof asset.key === "string" &&
          asset.key.startsWith("content-assets/"),
      )
      .map((asset) => ({ ...asset, index: Number(asset.index) }))
      .sort((left, right) => left.index - right.index);
  } catch {
    return [];
  }
}

function latestMetrics(row: ExportRow) {
  const legacyDay =
    row.d7_likes !== null
      ? 7
      : row.d5_likes !== null
        ? 5
        : row.d2_likes !== null
          ? 2
          : null;
  const legacyLikes =
    legacyDay === 7
      ? row.d7_likes
      : legacyDay === 5
        ? row.d5_likes
        : legacyDay === 2
          ? row.d2_likes
          : null;
  const legacyComments =
    legacyDay === 7
      ? row.d7_comments
      : legacyDay === 5
        ? row.d5_comments
        : legacyDay === 2
          ? row.d2_comments
          : null;
  const legacyCollects =
    legacyDay === 7
      ? row.d7_collects
      : legacyDay === 5
        ? row.d5_collects
        : legacyDay === 2
          ? row.d2_collects
          : null;
  const likes = row.latest_likes ?? legacyLikes;
  const comments = row.latest_comments ?? legacyComments;
  const collects = row.latest_collects ?? legacyCollects;
  const shares = row.latest_shares;
  return {
    likes,
    comments,
    collects,
    shares,
    total:
      likes === null
        ? null
        : likes + (comments ?? 0) + (collects ?? 0) + (shares ?? 0),
  };
}

function formatExportDate(value: string | null) {
  if (!value) return "";
  const date = parseStoredDate(value);
  if (Number.isNaN(date.getTime())) return value;
  return new Intl.DateTimeFormat("zh-CN", {
    timeZone: "Asia/Shanghai",
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    hour12: false,
  }).format(date);
}

function collectionLabel(row: ExportRow) {
  const labels: Record<string, string> = {
    pending: "待设置",
    scheduled: "已计划",
    collecting: "采集中",
    success: "采集成功",
    failed: "采集失败",
  };
  const status = row.collection_status || (row.latest_likes !== null ? "success" : "pending");
  const label = labels[status] || status;
  return row.collection_status_description
    ? `${label}：${row.collection_status_description}`
    : label;
}

function contentTypeFromObject(object: StoredObjectBody) {
  const headers = new Headers();
  object.writeHttpMetadata(headers);
  return headers.get("Content-Type") || "application/octet-stream";
}

async function loadImage(reference: ImageReference) {
  const bucket = getUploadBucket();
  let object = await bucket.get(reference.key);
  if (!object && reference.fileToken) {
    const media = await downloadFeishuMedia(
      reference.fileToken,
      env as unknown as FeishuBindings,
    );
    await bucket.put(reference.key, media.bytes, {
      httpMetadata: { contentType: media.contentType },
      customMetadata: { source: "feishu-api" },
    });
    object = await bucket.get(reference.key);
  }
  if (!object) return null;
  return normalizeWorkbookImage({
    bytes: new Uint8Array(await object.arrayBuffer()),
    contentType: contentTypeFromObject(object),
    width: reference.width,
    height: reference.height,
    description: reference.description,
  } satisfies RecoveryWorkbookImage);
}

async function loadImages(references: ImageReference[]) {
  const unique = new Map(references.map((reference) => [reference.key, reference]));
  const queue = [...unique.values()];
  const loaded = new Map<string, RecoveryWorkbookImage | null>();
  const workers = Array.from({ length: Math.min(6, queue.length) }, async () => {
    while (queue.length > 0) {
      const reference = queue.shift();
      if (!reference) return;
      try {
        loaded.set(reference.key, await loadImage(reference));
      } catch {
        loaded.set(reference.key, null);
      }
    }
  });
  await Promise.all(workers);
  return loaded;
}

function safeFileName(value: string) {
  return value.replace(/[\\/:*?"<>|]/g, " ").trim().slice(0, 60) || "KOC数据回收";
}

function exactArrayBuffer(bytes: Uint8Array) {
  const copy = new Uint8Array(bytes.byteLength);
  copy.set(bytes);
  return copy.buffer;
}

export async function GET(request: Request) {
  try {
    const url = new URL(request.url);
    const token = url.searchParams.get("token")?.trim() || "";
    const tokenPayload = token
      ? await consumeMcpExportToken(token, "recovery")
      : null;
    if (!tokenPayload && !(await isAdminRequest(request))) return adminForbidden();
    await ensureSchema();
    const taskId = tokenPayload
      ? String(tokenPayload.taskId ?? "").trim()
      : url.searchParams.get("task")?.trim() || "";
    if (!taskId) {
      return Response.json({ error: "缺少导出任务" }, { status: 400 });
    }
    const db = getRawDb();
    const task = await db
      .prepare("SELECT id, name, brand, platform FROM tasks WHERE id = ?")
      .bind(taskId)
      .first<{ id: string; name: string; brand: string; platform: string }>();
    if (!task) {
      return Response.json({ error: "没有找到这个任务" }, { status: 404 });
    }
    const result = await db
      .prepare(
        `SELECT
          c.id AS content_id,
          c.source_row,
          c.title,
          c.body,
          c.image_assets,
          d.id AS distribution_id,
          d.publish_url,
          d.publish_time,
          d.publish_screenshot_key,
          d.screenshot_key,
          d.exposure,
          d.views,
          d.d2_likes,
          d.d2_comments,
          d.d2_collects,
          d.d5_likes,
          d.d5_comments,
          d.d5_collects,
          d.d7_likes,
          d.d7_comments,
          d.d7_collects,
          d.latest_likes,
          d.latest_comments,
          d.latest_collects,
          d.latest_shares,
          d.collection_status,
          d.collection_status_description,
          d.collection_updated_at,
          d.updated_at,
          a.nickname AS account_nickname,
          p.name AS partner_name,
          cl.claimant_name
         FROM contents c
         LEFT JOIN distributions d ON d.content_id = c.id
         LEFT JOIN accounts a ON a.id = d.account_id
         LEFT JOIN partners p ON p.id = d.partner_id
         LEFT JOIN claims cl ON cl.id = d.claim_id
         WHERE c.task_id = ?
         ORDER BY COALESCE(c.source_row, 999999), c.created_at, d.claimed_at`,
      )
      .bind(taskId)
      .all<ExportRow>();
    const rows = result.results;
    const assetsByContent = new Map(
      rows.map((row) => [row.content_id, parseAssets(row.image_assets)]),
    );
    const maxContentImages = Math.max(
      0,
      ...[...assetsByContent.values()].flat().map((asset) => asset.index),
    );
    const references: ImageReference[] = [];
    rows.forEach((row) => {
      for (const asset of assetsByContent.get(row.content_id) ?? []) {
        references.push({
          ...asset,
          description: `${row.title} 原图${asset.index}`,
        });
      }
      if (row.screenshot_key) {
        references.push({
          index: 0,
          key: row.screenshot_key,
          description: `${row.title} 创作者中心截图`,
        });
      }
      if (row.publish_screenshot_key) {
        references.push({
          index: 0,
          key: row.publish_screenshot_key,
          description: `${row.title} 发布截图`,
        });
      }
    });
    const loadedImages = await loadImages(references);
    const isDouyin = task.platform === "抖音";
    const metricHeaders = isDouyin
      ? ["点赞", "收藏", "转发", "评论", "总互动"]
      : ["点赞", "收藏", "评论", "总互动"];
    const headers = [
      "序号（不能改）",
      "标题",
      "笔记内容（正文+话题）",
      ...Array.from({ length: maxContentImages }, (_, index) => `图片${index + 1}`),
      `${task.platform}昵称`,
      "发布链接",
      "发布时间",
      ...metricHeaders,
      "曝光量-实际（第7天）",
      "阅读量-实际（第7天）",
      "数据分析截图（单篇笔记数据分析截图）",
      "笔记截图",
      "数据更新时间",
      "采集状态",
      "合作方",
      "领取人微信号/手机号",
    ];
    const originalImageStart = 3;
    const accountColumn = originalImageStart + maxContentImages;
    const creatorScreenshotColumn = accountColumn + (isDouyin ? 10 : 9);
    const publishScreenshotColumn = creatorScreenshotColumn + 1;
    const workbookRows: RecoveryWorkbookRow[] = rows.map((row, rowIndex) => {
      const metrics = latestMetrics(row);
      const contentAssets = assetsByContent.get(row.content_id) ?? [];
      const originalImageCells = Array.from(
        { length: maxContentImages },
        (_, index) => {
          const asset = contentAssets.find((item) => item.index === index + 1);
          return "";
        },
      );
      const creatorImage = row.screenshot_key
        ? loadedImages.get(row.screenshot_key) ?? null
        : null;
      const publishImage = row.publish_screenshot_key
        ? loadedImages.get(row.publish_screenshot_key) ?? null
        : null;
      const cells: Array<string | number | null> = [
        row.source_row ?? rowIndex + 1,
        row.title,
        row.body,
        ...originalImageCells,
        row.account_nickname || "",
        row.publish_url || "",
        formatExportDate(row.publish_time),
        metrics.likes,
        metrics.collects,
        ...(isDouyin ? [metrics.shares] : []),
        metrics.comments,
        metrics.total,
        row.exposure,
        row.views,
        "",
        "",
        formatExportDate(row.collection_updated_at || row.updated_at),
        row.distribution_id ? collectionLabel(row) : "未领取",
        row.partner_name || "",
        row.claimant_name || "",
      ];
      const images: RecoveryWorkbookRow["images"] = [];
      for (const asset of contentAssets) {
        const image = loadedImages.get(asset.key);
        if (image) images.push({ column: originalImageStart + asset.index - 1, image });
      }
      if (creatorImage) {
        images.push({ column: creatorScreenshotColumn, image: creatorImage });
      }
      if (publishImage) {
        images.push({ column: publishScreenshotColumn, image: publishImage });
      }
      return { cells, images };
    });
    const columnWidths = [
      12,
      28,
      56,
      ...Array.from({ length: maxContentImages }, () => 24),
      18,
      40,
      20,
      11,
      11,
      ...(isDouyin ? [11] : []),
      11,
      11,
      18,
      18,
      26,
      24,
      20,
      24,
      18,
      22,
    ];
    const workbook = buildRecoveryWorkbook({
      sheetName: `${task.name}-数据回收`,
      headers,
      columnWidths,
      rows: workbookRows,
    });
    const fileName = `${safeFileName(task.name)}-数据回收.xlsx`;
    return new Response(exactArrayBuffer(workbook), {
      headers: {
        "Cache-Control": "private, no-store",
        "Content-Type":
          "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        "Content-Disposition": `attachment; filename="koc-recovery.xlsx"; filename*=UTF-8''${encodeURIComponent(fileName)}`,
        "X-Content-Type-Options": "nosniff",
      },
    });
  } catch (error) {
    return Response.json(
      { error: error instanceof Error ? error.message : "导出失败" },
      { status: 500 },
    );
  }
}
