import type { AgentTask, TaskRunRequest, TaskRunResult } from "../../shared/types";
import type { SmqjhClient } from "../cloud/smqjhClient";
import type { ConfigStore } from "../config/appConfig";
import type { ReadOnlyDatabaseClient } from "../db/readOnlyDatabase";

interface TaskContext {
  client: SmqjhClient;
  database: ReadOnlyDatabaseClient;
  configStore: ConfigStore;
  openExternal: (url: string) => Promise<void>;
  log: (message: string, context?: Record<string, unknown>) => void;
}

type TaskHandler = (request: TaskRunRequest, context: TaskContext) => Promise<Omit<TaskRunResult, "taskId" | "startedAt" | "finishedAt">>;

interface PriceComparisonRow {
  platform: string;
  productName: string;
  price: string;
  unitPrice?: string;
  status: string;
  source: string;
  note: string;
  sourceUrl?: string;
  evidence?: string;
}

export const TASKS: AgentTask[] = [
  {
    id: "cloud.health",
    title: "网关连通检查",
    category: "system",
    description: "调用认证验证码接口，验证网关地址与认证服务是否可达。",
    dangerLevel: "safe",
    endpoint: "/smqjh-auth/api/v1/auth/captcha",
    method: "GET",
    params: [],
    requiresAuth: false
  },
  {
    id: "auth.login",
    title: "后台账号登录",
    category: "system",
    description: "通过 mall-admin OAuth 客户端换取后台访问 token。",
    dangerLevel: "safe",
    endpoint: "/smqjh-auth/oauth2/token",
    method: "POST",
    params: [
      { key: "username", label: "账号", type: "text", required: true, placeholder: "admin" },
      { key: "password", label: "密码", type: "password", required: true },
      { key: "tenantCode", label: "租户编码", type: "text", required: true, placeholder: "zswl" },
      { key: "captchaId", label: "验证码 ID", type: "text", required: true },
      { key: "captchaCode", label: "验证码", type: "text", required: true }
    ],
    requiresAuth: false
  },
  {
    id: "user.current",
    title: "当前管理员信息",
    category: "system",
    description: "读取当前登录管理员资料，用于确认权限与菜单。",
    dangerLevel: "safe",
    endpoint: "/smqjh-system/api/v1/users/me",
    method: "GET",
    params: [],
    requiresAuth: true
  },
  {
    id: "member.export.start",
    title: "会员导出任务",
    category: "member",
    description: "创建会员中心导出任务，后续可接下载和轮询状态。",
    dangerLevel: "review",
    endpoint: "/smqjh-system/api/v1/member/export/task/start",
    method: "POST",
    params: [
      { key: "dateRange", label: "日期范围", type: "text", placeholder: "2026-06-01,2026-06-30" },
      { key: "mobile", label: "手机号", type: "text" }
    ],
    requiresAuth: true
  },
  {
    id: "enterprise.member.search",
    title: "企业会员查询",
    category: "member",
    description: "按手机号查询企业会员记录，适合做客服核验入口。",
    dangerLevel: "safe",
    endpoint: "/smqjh-system/api/v1/members/enterprise/getByMobile",
    method: "GET",
    params: [{ key: "mobile", label: "手机号", type: "text", required: true }],
    requiresAuth: true
  },
  {
    id: "points.template",
    title: "积分充值模板",
    category: "points",
    description: "下载积分充值导入模板，后续可扩展为本地校验后上传。",
    dangerLevel: "safe",
    endpoint: "/smqjh-system/api/v1/pointsRecharge/exportTemplate",
    method: "GET",
    params: [],
    requiresAuth: true
  },
  {
    id: "order.count.query",
    title: "订单经营汇总",
    category: "order",
    description: "通过数据库只读 SELECT 查询订单数量和销售额，支持按手机号、日期范围过滤，并自动排除逻辑删除订单。",
    dangerLevel: "safe",
    params: [
      { key: "mobile", label: "手机号", type: "text", placeholder: "15165016065，可选" },
      { key: "dateFrom", label: "开始日期", type: "text", placeholder: "2025-06-02，可选" },
      { key: "dateTo", label: "结束日期", type: "text", placeholder: "2026-06-03，左闭右开，可选" }
    ],
    requiresAuth: true
  },
  {
    id: "database.readonly.query",
    title: "数据库智能只读查询",
    category: "system",
    description: "由 DeepSeek 根据管理员问题生成安全 SELECT，再经本地白名单、只读校验和超时限制执行，返回表格结果。",
    dangerLevel: "safe",
    params: [
      { key: "question", label: "原始问题", type: "textarea", placeholder: "例如：查看某个订单物流状态，或统计当前销售额" },
      { key: "sql", label: "只读 SQL", type: "textarea", required: true, placeholder: "仅允许 SELECT，自动限制访问 smqjh 白名单表" }
    ],
    requiresAuth: true
  },
  {
    id: "product.lookup.summary",
    title: "商品资料查询",
    category: "product",
    description: "通过数据库只读 SELECT 查询 smqjh 商品资料，包括商品名、品牌、价格、状态、描述和依据。",
    dangerLevel: "safe",
    params: [
      { key: "productKeyword", label: "商品关键词", type: "text", required: true, placeholder: "例如：百事可乐 500ml" }
    ],
    requiresAuth: true
  },
  {
    id: "product.price.compare",
    title: "商品价格对比调研",
    category: "product",
    description: "通过数据库只读查询 smqjh 系统内商品定价，并在后台采集苏宁易购和慢慢买公开价格线索，输出带依据的对比表格。",
    dangerLevel: "safe",
    params: [
      { key: "productKeyword", label: "商品关键词", type: "text", required: true, placeholder: "例如：爱奇艺月卡 / 体检套餐 / 商品名称" },
      { key: "productType", label: "系统商品类型", type: "text", placeholder: "auto / djk / spu / sku" },
      { key: "productId", label: "商品ID或SKU ID", type: "text", placeholder: "已知 ID 时填写，查询更准" },
      { key: "goodsCode", label: "商品编码", type: "text", placeholder: "大健康商品编码，可选" },
      { key: "systemPrice", label: "系统价手动补充", type: "text", placeholder: "接口暂不可用时可填写" },
      { key: "openBrowser", label: "打开浏览器复核", type: "text", placeholder: "默认 false；只有需要人工复核时填 true" },
      { key: "notes", label: "比价口径", type: "textarea", placeholder: "规格、地区、会员券、到手价口径等" }
    ],
    requiresAuth: true
  },
  {
    id: "coupon.batch.plan",
    title: "券批量发放预案",
    category: "coupon",
    description: "离线生成操作预案，不直接请求后端，等管理员流程确认后再接上传接口。",
    dangerLevel: "review",
    params: [
      { key: "couponType", label: "券类型", type: "text", required: true },
      { key: "audience", label: "发放对象", type: "textarea", required: true }
    ],
    requiresAuth: false
  },
  {
    id: "ops.requirement.note",
    title: "管理员需求记录",
    category: "ops",
    description: "把管理员操作诉求先沉淀为本地任务草稿，便于后续转成自动化动作。",
    dangerLevel: "safe",
    params: [
      { key: "title", label: "需求标题", type: "text", required: true },
      { key: "detail", label: "操作步骤", type: "textarea", required: true }
    ],
    requiresAuth: false
  }
];

const ACTIVE_TASK_IDS = [
  "product.price.compare",
  "product.lookup.summary",
  "order.count.query",
  "database.readonly.query",
  "user.current",
  "cloud.health"
];

const handlers: Record<string, TaskHandler> = {
  "cloud.health": async (_request, context) => {
    const result = await context.client.ping();
    return {
      success: result.ok,
      message: result.ok ? "网关连接正常" : `网关连接失败：${result.message}`,
      statusCode: result.status,
      data: result.data ?? result.raw
    };
  },

  "auth.login": async (request, context) => {
    const result = await context.client.login({
      username: request.params.username,
      password: request.params.password,
      tenantCode: request.params.tenantCode,
      captchaId: request.params.captchaId,
      captchaCode: request.params.captchaCode
    });

    return {
      success: result.ok,
      message: result.ok ? "后台登录成功" : `后台登录失败：${result.message}`,
      statusCode: result.status,
      data: result.data
    };
  },

  "coupon.batch.plan": async (request) => ({
    success: true,
    message: "券批量发放预案已生成，当前框架未执行真实发放",
    data: {
      couponType: request.params.couponType,
      audience: request.params.audience,
      nextStep: "确认管理员审批、名单校验和后端上传接口后接入执行器"
    }
  }),

  "order.count.query": async (request, context) => {
    const result = await context.database.countOrders({
      mobile: request.params.mobile,
      dateFrom: request.params.dateFrom,
      dateTo: request.params.dateTo
    });

    return {
      success: result.ok,
      message: result.ok ? "订单数量查询完成" : `订单数量查询失败：${result.message}`,
      data: result.record ?? {
        mobile: request.params.mobile,
        dateFrom: request.params.dateFrom,
        dateTo: request.params.dateTo,
        reason: result.message
      }
    };
  },

  "database.readonly.query": async (request, context) => {
    const sql = request.params.sql?.trim();
    if (!sql) {
      return {
        success: false,
        message: "缺少可执行 SQL",
        data: {
          question: request.params.question
        }
      };
    }

    const result = await context.database.runReadOnlyQuery(sql);
    return {
      success: result.ok,
      message: result.ok ? "数据库智能只读查询完成" : `数据库智能只读查询失败：${result.message}`,
      data: result.result ?? {
        question: request.params.question,
        sql,
        reason: result.message
      }
    };
  },

  "product.lookup.summary": async (request, context) => {
    const keyword = request.params.productKeyword.trim();
    const result = await context.database.findProductSummary(keyword);

    return {
      success: result.ok,
      message: result.ok ? "商品资料查询完成" : `商品资料查询失败：${result.message}`,
      data: result.record ?? {
        productKeyword: keyword,
        reason: result.message
      }
    };
  },

  "product.price.compare": async (request, context) => {
    const keyword = request.params.productKeyword.trim();
    const priceToolUrls = buildPriceToolUrls(keyword);
    const browserResults: Array<{ platform: string; url: string; opened: boolean; message: string }> = [];

    if (shouldOpenBrowser(request.params.openBrowser)) {
      for (const item of priceToolUrls) {
        try {
          await context.openExternal(item.url);
          browserResults.push({ platform: item.platform, url: item.url, opened: true, message: "已打开浏览器查询页" });
        } catch (error) {
          browserResults.push({
            platform: item.platform,
            url: item.url,
            opened: false,
            message: error instanceof Error ? error.message : String(error)
          });
        }
      }
    }

    const [systemRow, externalRows] = await Promise.all([
      buildSystemPriceRow(request, context),
      collectPriceToolRows(keyword)
    ]);
    const rows: PriceComparisonRow[] = [
      systemRow,
      ...externalRows
    ];

    return {
      success: true,
      message: "商品价格对比调研已生成",
      data: {
        productKeyword: keyword,
        searchedAt: new Date().toISOString(),
        comparisonRows: rows,
        comparisonMarkdown: toMarkdownTable(rows),
        searchUrls: priceToolUrls,
        browserResults,
        notes: request.params.notes || "外部价格只请求苏宁易购公开页/价格接口和慢慢买公开比价页；价格受地区、券、会员价、运费和活动影响，只能作为线索。",
        nextStep: "如需更多平台官方实时价，建议后续接入平台官方接口或授权采集账号；当前版本不再请求易触发风控的电商页面。"
      }
    };
  },

  "ops.requirement.note": async (request) => ({
    success: true,
    message: "管理员需求已进入本地草稿",
    data: {
      title: request.params.title,
      detail: request.params.detail,
      nextStep: "补充接口、权限、异常处理和回滚策略"
    }
  })
};

function shouldOpenBrowser(value?: string): boolean {
  const normalized = (value ?? "").trim().toLowerCase();
  return ["true", "1", "yes", "y", "是", "打开"].includes(normalized);
}

interface PriceToolUrl {
  platform: string;
  url: string;
}

function buildPriceToolUrls(keyword: string): PriceToolUrl[] {
  return [
    { platform: "苏宁", url: buildSuningSearchUrl(keyword) },
    { platform: "慢慢买", url: buildManmanbuySearchUrl(keyword) }
  ];
}

function buildManmanbuySearchUrl(keyword: string): string {
  const url = new URL("https://s.manmanbuy.com/pc/search/result");
  url.searchParams.set("keyword", keyword);
  url.searchParams.set("btnSearch", "搜索");
  return url.toString();
}

function buildSuningSearchUrl(keyword: string): string {
  return `https://search.suning.com/${encodeURIComponent(keyword)}/`;
}

async function collectPriceToolRows(keyword: string): Promise<PriceComparisonRow[]> {
  const [suning, manmanbuyRows] = await Promise.all([
    collectSuningRow(keyword),
    collectManmanbuyRows(keyword)
  ]);
  return [
    ...(suning ? [suning] : []),
    ...manmanbuyRows
  ];
}

interface ManmanbuyDeal {
  title: string;
  price: string;
  unitPrice?: string;
  priceNote?: string;
  mall: string;
  time: string;
  detailUrl: string;
  score: number;
  sortPrice: number;
}

async function collectManmanbuyRows(keyword: string): Promise<PriceComparisonRow[]> {
  const searchUrl = buildManmanbuySearchUrl(keyword);
  const page = await fetchMarketPage(searchUrl);
  if (!page.ok || page.blocked || !page.text) {
    return [];
  }

  return selectManmanbuyDeals(extractManmanbuyDeals(page.text, keyword), 6)
    .map((deal) => {
      const price = normalizePriceText(deal.price);
      const packMeta = buildPackMeta(deal.title, price, "慢慢买公开比价页");
      const unitPrice = deal.unitPrice || packMeta.unitPrice;
      const note = [
        "聚合来源：慢慢买",
        deal.mall ? `原平台/商城：${deal.mall}` : "",
        deal.time ? `时间：${deal.time}` : "",
        deal.priceNote ? `页面价格口径：${deal.priceNote}` : "",
        packMeta.note
      ].filter(Boolean).join("；");

      return {
        platform: deal.mall ? `${deal.mall}（慢慢买）` : "慢慢买",
        productName: deal.title,
        price,
        unitPrice,
        status: "价格线索",
        source: "慢慢买公开比价页",
        note,
        sourceUrl: deal.detailUrl,
        evidence: [`慢慢买聚合搜索请求：${searchUrl}`, deal.mall ? `慢慢买标注原平台/商城：${deal.mall}` : "", `线索详情：${deal.detailUrl}`].filter(Boolean).join("\n")
      };
    });
}

function extractManmanbuyDeals(html: string, keyword: string): ManmanbuyDeal[] {
  const blocks = html
    .split(/<div\s+class="DiscountItemPC_box__/i)
    .slice(1)
    .map((block) => `<div class="DiscountItemPC_box__${block}`);

  const deals = blocks
    .map((block): ManmanbuyDeal | undefined => {
      const titleHtml = block.match(/<div\s+class="DiscountItemPC_itemTitle[^"]*">([\s\S]*?)<\/div>/i)?.[1] ?? "";
      const titleAnchor = titleHtml.match(/<a\b([^>]*)>([\s\S]*?)<\/a>/i);
      const titleAttrs = titleAnchor?.[1] ?? "";
      const href = getHtmlAttribute(titleAttrs, "href") || block.match(/<a\b[^>]*href=["']([^"']+)["']/i)?.[1] || "";
      const rawTitle =
        getHtmlAttribute(titleAttrs, "title") ||
        titleAnchor?.[2] ||
        block.match(/<img\b[^>]*alt=["']([^"']+)["']/i)?.[1] ||
        "";
      const title = cleanMarkdownText(htmlToText(rawTitle));

      const rawPrice = cleanMarkdownText(
        htmlToText(block.match(/<div\s+class="DiscountItemPC_itemSubTitle[^"]*">([\s\S]*?)<\/div>/i)?.[1] ?? "")
      );
      const parsedPrice = parseManmanbuyPrice(rawPrice, title);
      const mall = cleanMarkdownText(
        htmlToText(block.match(/<span\s+class="DiscountItemPC_itemMall[^"]*">([\s\S]*?)<\/span>/i)?.[1] ?? "")
      );
      const time = cleanMarkdownText(
        htmlToText(block.match(/<span\s+class="DiscountItemPC_itemTime[^"]*">([\s\S]*?)<\/span>/i)?.[1] ?? "")
      );

      if (!title || !parsedPrice.price || !href) {
        return undefined;
      }

      return {
        title,
        price: parsedPrice.price,
        unitPrice: parsedPrice.unitPrice,
        priceNote: parsedPrice.note,
        mall,
        time,
        detailUrl: absoluteUrl(href, "https://s.manmanbuy.com/"),
        score: scoreProductTitle(title, keyword, [mall, "慢慢买"].filter(Boolean)),
        sortPrice: parsedPrice.sortPrice
      };
    })
    .filter((deal): deal is ManmanbuyDeal => Boolean(deal))
    .filter((deal) => deal.score > 0)
    .sort((a, b) => b.score - a.score || a.sortPrice - b.sortPrice);

  return dedupeBy(deals, (deal) => deal.detailUrl || deal.title);
}

function selectManmanbuyDeals(deals: ManmanbuyDeal[], limit: number): ManmanbuyDeal[] {
  const selected: ManmanbuyDeal[] = [];
  const seenMalls = new Set<string>();

  for (const deal of deals) {
    const mallKey = normalizeMallName(deal.mall);
    if (!mallKey || seenMalls.has(mallKey)) {
      continue;
    }
    selected.push(deal);
    seenMalls.add(mallKey);
    if (selected.length >= limit) {
      return selected;
    }
  }

  return selected;
}

function parseManmanbuyPrice(rawPrice: string, title: string): { price: string; unitPrice?: string; note: string; sortPrice: number } {
  const text = rawPrice.replace(/\s+/g, " ").trim();
  const matches = Array.from(
    text.matchAll(/(?:[¥￥]\s*)?(\d{1,6}(?:\.\d{1,2})?)\s*元?(?:\s*\/\s*(瓶|桶|罐|支|盒|件|包|个))?/g)
  )
    .map((match) => ({
      value: Number(match[1]),
      unit: match[2] ?? "",
      raw: match[0].trim()
    }))
    .filter((match) => Number.isFinite(match.value) && match.value > 0);

  if (matches.length === 0) {
    return { price: "", note: "", sortPrice: Number.POSITIVE_INFINITY };
  }

  const unitMatch = matches.find((match) => match.unit);
  const totalMatch = matches.find((match) => !match.unit);
  const packageCount = parsePackageCount(title);
  const estimatedTotal = unitMatch && packageCount > 1 ? unitMatch.value * packageCount : undefined;
  const total = totalMatch?.value ?? estimatedTotal ?? unitMatch?.value ?? matches[0].value;
  const price = formatMoney(total);
  const unitPrice = unitMatch
    ? `${formatMoney(unitMatch.value)}/${unitMatch.unit}`
    : packageCount > 1 && totalMatch
      ? `${formatMoney(totalMatch.value / packageCount)}/件`
      : undefined;

  const noteParts = [
    text ? `原文：${text}` : "",
    unitMatch ? `页面单价 ${formatMoney(unitMatch.value)}/${unitMatch.unit}` : "",
    totalMatch ? `页面总价 ${formatMoney(totalMatch.value)}` : estimatedTotal ? `按规格估算总价 ${formatMoney(estimatedTotal)}` : ""
  ].filter(Boolean);

  return {
    price,
    unitPrice,
    note: noteParts.join("，"),
    sortPrice: total
  };
}

function normalizeMallName(value: string): string {
  const cleaned = value
    .replace(/商城|旗舰店|官方|自营|超市/g, "")
    .replace(/\s+/g, "")
    .trim()
    .toLowerCase();

  if (cleaned.includes("京东")) return "京东";
  if (cleaned.includes("淘宝")) return "淘宝";
  if (cleaned.includes("天猫")) return "天猫";
  if (cleaned.includes("拼多多")) return "拼多多";
  if (cleaned.includes("苏宁")) return "苏宁";
  if (cleaned.includes("抖音")) return "抖音";
  if (cleaned.includes("快手")) return "快手";
  if (cleaned.includes("唯品会")) return "唯品会";
  return cleaned;
}

interface SuningProduct {
  productId: string;
  shopId: string;
  title: string;
  productUrl: string;
  score: number;
}

async function collectSuningRow(keyword: string): Promise<PriceComparisonRow | undefined> {
  const searchUrl = buildSuningSearchUrl(keyword);
  const searchPage = await fetchMarketPage(searchUrl);
  if (!searchPage.ok || !searchPage.text) {
    return undefined;
  }

  const products = extractSuningProducts(searchPage.text, keyword);
  if (products.length === 0) {
    return undefined;
  }

  const priceResult = await fetchSuningPrices(products.slice(0, 8));
  const priced = products
    .map((product) => ({ product, price: priceResult.prices.get(product.productId) }))
    .filter((item): item is { product: SuningProduct; price: string } => Boolean(item.price))
    .sort((a, b) => b.product.score - a.product.score)[0];

  if (!priced) {
    return {
      platform: "苏宁",
      productName: products[0].title,
      price: "未获取",
      status: "未采集到",
      source: "苏宁公开搜索页",
      note: "搜索页找到商品但价格接口未返回可用价格",
      sourceUrl: products[0].productUrl,
      evidence: [`苏宁搜索请求：${searchUrl}`, `苏宁价格接口：${priceResult.url || "未生成"}`].filter(Boolean).join("\n")
    };
  }

  const packMeta = buildPackMeta(priced.product.title, priced.price, "苏宁公开搜索页");
  return {
    platform: "苏宁",
    productName: priced.product.title,
    price: priced.price,
    unitPrice: packMeta.unitPrice,
    status: "价格线索",
    source: "苏宁公开页+价格接口",
    note: packMeta.note,
    sourceUrl: priced.product.productUrl,
    evidence: [`苏宁搜索请求：${searchUrl}`, `苏宁价格接口：${priceResult.url}`, `商品页：${priced.product.productUrl}`].join("\n")
  };
}

function extractSuningProducts(html: string, keyword: string): SuningProduct[] {
  const blocks = html.split(/<div\s+class="product-box[^"]*"/i).slice(1);
  const products = blocks
    .map((block): SuningProduct | undefined => {
      const hrefMatch = block.match(/href="\/\/product\.suning\.com\/([^"/]+)\/(\d+)\.html"/i);
      const productId = hrefMatch?.[2] ?? block.match(/dataPro="[^"]*\|\|(\d+)\|\|/i)?.[1] ?? "";
      const shopId = hrefMatch?.[1] ?? block.match(/vendor="([^"]+)"/i)?.[1] ?? "0000000000";
      const rawTitle =
        block.match(/<img[^>]+alt="([^"]+)"/i)?.[1] ??
        block.match(/<div\s+class="title-selling-point">[\s\S]*?<a[^>]*>([\s\S]*?)<\/a>/i)?.[1] ??
        "";
      const title = cleanMarkdownText(htmlToText(rawTitle));
      if (!productId || !title) {
        return undefined;
      }
      const productUrl = `https://product.suning.com/${shopId}/${productId}.html`;
      return {
        productId,
        shopId,
        title,
        productUrl,
        score: scoreProductTitle(title, keyword, ["苏宁", "suning"])
      };
    })
    .filter((product): product is SuningProduct => Boolean(product))
    .filter((product) => product.score > 0)
    .sort((a, b) => b.score - a.score);

  return dedupeBy(products, (product) => product.productId).slice(0, 12);
}

async function fetchSuningPrices(products: SuningProduct[]): Promise<{ prices: Map<string, string>; url: string }> {
  if (products.length === 0) {
    return { prices: new Map(), url: "" };
  }

  const ids = products.map((product) => `${product.productId.padStart(18, "0")}_`).join(",");
  const url = `https://ds.suning.com/ds/generalForTile/${ids}-025-2-0000000000-1--ds0000000002391.json`;
  const page = await fetchMarketPage(url);
  const prices = new Map<string, string>();
  if (!page.ok || !page.text) {
    return { prices, url };
  }

  const parsed = parseJsonObject(page.text);
  const records = Array.isArray(parsed.rs) ? parsed.rs : [];
  for (const item of records) {
    if (!item || typeof item !== "object") {
      continue;
    }
    const record = item as Record<string, unknown>;
    const productId = String(record.cmmdtyCode ?? "").replace(/^0+/, "");
    const rawPrice = String(record.price || record.snPrice || record.promotionPrice || "");
    const price = normalizePriceText(rawPrice);
    if (productId && price) {
      prices.set(productId, price);
    }
  }

  return { prices, url };
}

function scoreProductTitle(title: string, keyword: string, aliases: string[]): number {
  const normalizedTitle = normalizeSearchText(title);
  const normalizedKeyword = normalizeSearchText(keyword);
  const keywordTokens = productSearchTokens(normalizedKeyword);
  let score = 0;

  if (normalizedTitle.includes(normalizedKeyword)) {
    score += 18;
  }

  for (const token of keywordTokens) {
    if (normalizedTitle.includes(token)) {
      score += token.length >= 4 ? 6 : 3;
    }
  }
  if (aliases.some((alias) => normalizedTitle.includes(normalizeSearchText(alias)))) {
    score += 4;
  }
  const keywordSpec = inferPackageSpec(keyword);
  if (keywordSpec && normalizedTitle.includes(normalizeSearchText(keywordSpec))) {
    score += 8;
  }
  if (hasVolumeMismatch(title, keyword)) {
    score -= 16;
  }
  if (/料酒|黄酒|调料|调味/.test(title) && /料酒|黄酒|调料|调味/.test(keyword)) {
    score += 8;
  }
  if (/白酒|浓香型|酱香型|52度|53度/.test(title) && /料酒|调料|调味/.test(keyword)) {
    score -= 18;
  }
  if (/百事可乐/.test(keyword) && /美年达|七喜|可口可乐|雪碧|芬达|苹果味|橙味/.test(title)) {
    score -= 18;
  }
  if (!/无糖/.test(keyword) && /无糖/.test(title)) {
    score -= 4;
  }
  return score;
}

function productSearchTokens(value: string): string[] {
  return value
    .replace(/[^\p{Script=Han}a-zA-Z0-9.]+/gu, " ")
    .split(/\s+/)
    .flatMap((part) => {
      const tokens = [part];
      const chineseWords = part.match(/[\p{Script=Han}]{2,}/gu) ?? [];
      tokens.push(...chineseWords);
      return tokens;
    })
    .map((part) => part.trim())
    .filter((part) => part.length >= 2 && !/^(价格|商品|产品|系统|业务|平台|电商|单瓶|一瓶)$/.test(part));
}

function normalizeSearchText(value: string): string {
  return value
    .toLowerCase()
    .replace(/×/g, "*")
    .replace(/\s+/g, "")
    .trim();
}

function inferPackageSpec(value: string): string {
  return value.match(/\d+(?:\.\d+)?\s*(?:ml|mL|ML|毫升|l|L|升)(?:\s*[xX*×]\s*\d+\s*(?:瓶|桶|罐|支|盒)?)?/u)?.[0].replace(/\s+/g, "") ?? "";
}

function buildPackMeta(title: string, price: string, sourceLabel = "公开页面"): { note: string; unitPrice: string } {
  const total = Number(price.replace(/[¥￥元,\s]/g, ""));
  const count = parsePackageCount(title);
  if (Number.isFinite(total) && total > 0 && Number.isFinite(count) && count > 1) {
    return {
      unitPrice: `¥${(total / count).toFixed(2)}/件`,
      note: `公开页规格约 ${count} 件，总价 ${price}；以平台实时页面为准`
    };
  }
  return {
    unitPrice: "",
    note: `${sourceLabel}匹配到商品并返回价格线索；以平台实时页面为准`
  };
}

function buildPackNote(title: string, price: string, sourceLabel = "公开页面"): string {
  return buildPackMeta(title, price, sourceLabel).note;
}

function parsePackageCount(title: string): number {
  const multiplierCounts = Array.from(title.matchAll(/[xX*×]\s*(\d+)\s*(?:瓶|桶|罐|支|盒|件|包)?/g))
    .map((match) => Number(match[1]))
    .filter((value) => Number.isFinite(value) && value > 1 && value < 1000);
  if (multiplierCounts.length > 0) {
    return multiplierCounts.reduce((sum, value) => sum + value, 0);
  }

  const countMatch = title.match(/(?:共|整箱|一箱|箱装|每箱|规格|净含量)[^\d]{0,8}(\d+)\s*(?:瓶|桶|罐|支|盒|件|包)/);
  const count = Number(countMatch?.[1] ?? "");
  return Number.isFinite(count) && count > 1 && count < 1000 ? count : 0;
}

function hasVolumeMismatch(title: string, keyword: string): boolean {
  const expected = extractVolumeMl(keyword);
  const actual = extractVolumeMl(title);
  return Boolean(expected && actual && Math.abs(expected - actual) / expected > 0.08);
}

function extractVolumeMl(value: string): number {
  const match = value.match(/(\d+(?:\.\d+)?)\s*(ml|mL|ML|毫升|l|L|升)/);
  if (!match) {
    return 0;
  }
  const amount = Number(match[1]);
  if (!Number.isFinite(amount) || amount <= 0) {
    return 0;
  }
  return /^(l|L|升)$/.test(match[2]) ? amount * 1000 : amount;
}

function dedupeBy<T>(items: T[], keyOf: (item: T) => string): T[] {
  const seen = new Set<string>();
  return items.filter((item) => {
    const key = keyOf(item);
    if (seen.has(key)) {
      return false;
    }
    seen.add(key);
    return true;
  });
}

function parseJsonObject(value: string): Record<string, unknown> {
  try {
    return JSON.parse(value) as Record<string, unknown>;
  } catch {
    const start = value.indexOf("{");
    const end = value.lastIndexOf("}");
    if (start >= 0 && end > start) {
      return JSON.parse(value.slice(start, end + 1)) as Record<string, unknown>;
    }
    return {};
  }
}

function getHtmlAttribute(attrs: string, name: string): string {
  const match = attrs.match(new RegExp(`${name}\\s*=\\s*["']([^"']+)["']`, "i"));
  return match?.[1] ? decodeHtml(match[1]) : "";
}

function absoluteUrl(url: string, base: string): string {
  try {
    return new URL(url, base).toString();
  } catch {
    return url;
  }
}

function parsePriceNumber(value: string): number {
  const match = value.match(/\d{1,6}(?:\.\d{1,2})?/);
  return match ? Number(match[0]) : Number.POSITIVE_INFINITY;
}

function formatMoney(value: number): string {
  return `¥${value.toFixed(2)}`;
}

function cleanMarkdownText(value: string): string {
  return decodeHtml(value)
    .replace(/!\[[^\]]*]\([^)]+\)/g, " ")
    .replace(/\[[^\]]*]\([^)]+\)/g, " ")
    .replace(/[_#>`]/g, " ")
    .replace(/\s+/g, " ")
    .trim();
}

async function fetchMarketPage(url: string): Promise<{ ok: boolean; blocked: boolean; text: string; status?: number; message?: string }> {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 12000);

  try {
    const response = await fetch(url, {
      signal: controller.signal,
      headers: {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125 Safari/537.36",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.7"
      }
    });
    const text = await response.text();
    return {
      ok: response.ok,
      blocked: isBlockedMarketPage(response.url, text),
      text,
      status: response.status
    };
  } catch (error) {
    return {
      ok: false,
      blocked: false,
      text: "",
      message: error instanceof Error ? error.message : String(error)
    };
  } finally {
    clearTimeout(timeout);
  }
}

function isBlockedMarketPage(url: string, html: string): boolean {
  const lowerUrl = url.toLowerCase();
  const lowerHtml = html.toLowerCase();
  const hasExplicitRisk =
    html.includes("京东验证") ||
    html.includes("请重新登录") ||
    html.includes("请输入登录密码") ||
    html.includes("账号名/邮箱/手机号") ||
    html.includes("滑动验证") ||
    html.includes("扫码登录更安全") ||
    lowerHtml.includes("captcha") ||
    lowerHtml.includes("risk_handler");

  return (
    lowerUrl.includes("login") ||
    lowerUrl.includes("risk") ||
    lowerUrl.includes("passport") ||
    hasExplicitRisk
  );
}

function extractPriceEvidence(html: string): { price: string; title: string; note: string } | undefined {
  const text = htmlToText(html);
  const priceRegex = /(?:¥|￥)\s*\d{1,6}(?:\.\d{1,2})?|\d{1,6}(?:\.\d{1,2})?\s*元/g;
  const matches = Array.from(text.matchAll(priceRegex))
    .map((match) => ({
      raw: match[0],
      value: Number(match[0].replace(/[¥￥元,\s]/g, ""))
    }))
    .filter((item) => Number.isFinite(item.value) && item.value > 0);

  const unique = Array.from(new Map(matches.map((item) => [item.value.toFixed(2), item])).values()).slice(0, 6);
  if (unique.length === 0) {
    return undefined;
  }

  const values = unique.map((item) => item.value).sort((a, b) => a - b);
  const price = values.length === 1 ? `¥${values[0].toFixed(2)}` : `¥${values[0].toFixed(2)} - ¥${values[values.length - 1].toFixed(2)}`;
  const title = pickPageTitle(html);

  return {
    price,
    title,
    note: values.length === 1 ? "后台从公开页面提取到价格" : `后台从公开页面提取到 ${values.length} 个价格线索，展示区间`
  };
}

function htmlToText(html: string): string {
  return decodeHtml(
    html
      .replace(/<script[\s\S]*?<\/script>/gi, " ")
      .replace(/<style[\s\S]*?<\/style>/gi, " ")
      .replace(/<[^>]+>/g, " ")
      .replace(/\s+/g, " ")
      .trim()
  );
}

function pickPageTitle(html: string): string {
  const title = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i)?.[1] ?? "";
  return decodeHtml(title).replace(/\s+/g, " ").trim().slice(0, 80);
}

function decodeHtml(value: string): string {
  return value
    .replace(/&nbsp;/g, " ")
    .replace(/&amp;/g, "&")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">")
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'");
}

async function buildSystemPriceRow(request: TaskRunRequest, context: TaskContext): Promise<PriceComparisonRow> {
  const manualPrice = normalizePriceText(request.params.systemPrice);
  if (manualPrice) {
    return {
      platform: "smqjh系统",
      productName: request.params.productKeyword,
      price: manualPrice,
      status: "已手动补充",
      source: "任务参数",
      note: "系统价由管理员手动录入",
      evidence: "任务参数 systemPrice"
    };
  }

  const type = (request.params.productType || "auto").trim().toLowerCase();
  const lookupOrder = type === "auto" ? ["djk", "spu", "sku"] : [type];
  const errors: string[] = [];
  const dbResult = await context.database.findProductPrice(request.params.productKeyword);
  if (dbResult.ok && dbResult.record) {
    return {
      platform: "smqjh系统",
      productName: dbResult.record.productName,
      price: dbResult.record.price,
      status: "已匹配",
      source: dbResult.record.source,
      note: dbResult.record.note,
      evidence: dbResult.record.evidence
    };
  }
  if (dbResult.message) {
    errors.push(`mysql: ${dbResult.message}`);
  }

  for (const sourceType of lookupOrder) {
    const row = await trySystemLookup(sourceType, request, context);
    if (row.status === "已匹配") {
      return row;
    }
    if (row.note) {
      errors.push(`${sourceType}: ${row.note}`);
    }
  }

  return {
    platform: "smqjh系统",
    productName: request.params.productKeyword,
    price: "未获取",
    status: "待确认",
    source: "smqjh-pms",
    note: errors[0] || "未匹配到商品；可填写商品ID、SKU ID、商品编码或系统价后重试",
    evidence: errors.join("\n")
  };
}

async function trySystemLookup(sourceType: string, request: TaskRunRequest, context: TaskContext): Promise<PriceComparisonRow> {
  if (sourceType === "djk") {
    return lookupDjkGoods(request, context);
  }
  if (sourceType === "spu") {
    return lookupSpu(request, context);
  }
  if (sourceType === "sku") {
    return lookupSku(request, context);
  }
  return {
    platform: "smqjh系统",
    productName: request.params.productKeyword,
    price: "未获取",
    status: "待确认",
    source: "smqjh-pms",
    note: "系统商品类型仅支持 auto、djk、spu、sku"
  };
}

async function lookupDjkGoods(request: TaskRunRequest, context: TaskContext): Promise<PriceComparisonRow> {
  const productId = request.params.productId?.trim();
  const result = productId
    ? await context.client.request<Record<string, unknown>>("GET", "/smqjh-pms/api/v1/goods/info", {
        auth: true,
        query: { id: productId }
      })
    : await firstSuccessfulProductPage(keywordCandidates(request.params.productKeyword), async (keyword) =>
        context.client.request<Record<string, unknown>>("GET", "/smqjh-pms/api/v1/goods/goodsList", {
          auth: true,
          query: compactQuery({
            pageNum: "1",
            pageSize: "5",
            goodsName: keyword,
            goodsCode: request.params.goodsCode
          })
        })
      );

  if (!result.ok) {
    return systemLookupMiss(request, "大健康商品", result.message);
  }

  const record = (productId ? result.data : firstPageRecord(result.data)) as Record<string, unknown> | undefined;
  if (!record) {
    return systemLookupMiss(request, "大健康商品", "未查询到匹配商品");
  }

  return {
    platform: "smqjh系统",
    productName: stringValue(record.goodsName) || request.params.productKeyword,
    price: formatYuan(record.price),
    status: "已匹配",
    source: productId ? "/smqjh-pms/api/v1/goods/info" : "/smqjh-pms/api/v1/goods/goodsList",
    note: `类型：大健康商品${stringValue(record.goodsCode) ? `，编码：${stringValue(record.goodsCode)}` : ""}`,
    evidence: productId
      ? `接口请求：GET /smqjh-pms/api/v1/goods/info?id=${encodeURIComponent(productId)}`
      : `接口请求：GET /smqjh-pms/api/v1/goods/goodsList?goodsName=${encodeURIComponent(request.params.productKeyword)}`
  };
}

async function lookupSpu(request: TaskRunRequest, context: TaskContext): Promise<PriceComparisonRow> {
  const productId = request.params.productId?.trim();
  const result = productId
    ? await context.client.request<Record<string, unknown>>("GET", `/smqjh-pms/api/v1/spu/${encodeURIComponent(productId)}/detail`, { auth: true })
    : await firstSuccessfulProductPage(keywordCandidates(request.params.productKeyword), async (keyword) =>
        context.client.request<Record<string, unknown>>("GET", "/smqjh-pms/api/v1/spu/page", {
          auth: true,
          query: {
            pageNum: "1",
            pageSize: "5",
            keywords: keyword
          }
        })
      );

  if (!result.ok) {
    return systemLookupMiss(request, "SPU", result.message);
  }

  const record = (productId ? result.data : firstPageRecord(result.data)) as Record<string, unknown> | undefined;
  if (!record) {
    return systemLookupMiss(request, "SPU", "未查询到匹配商品");
  }

  return {
    platform: "smqjh系统",
    productName: stringValue(record.name) || request.params.productKeyword,
    price: formatCent(record.price),
    status: "已匹配",
    source: productId ? "/smqjh-pms/api/v1/spu/{id}/detail" : "/smqjh-pms/api/v1/spu/page",
    note: `类型：SPU${stringValue(record.id) ? `，ID：${stringValue(record.id)}` : ""}`,
    evidence: productId
      ? `接口请求：GET /smqjh-pms/api/v1/spu/${encodeURIComponent(productId)}/detail`
      : `接口请求：GET /smqjh-pms/api/v1/spu/page?keywords=${encodeURIComponent(request.params.productKeyword)}`
  };
}

async function lookupSku(request: TaskRunRequest, context: TaskContext): Promise<PriceComparisonRow> {
  const productId = request.params.productId?.trim();
  if (!productId) {
    return systemLookupMiss(request, "SKU", "SKU 查询需要填写商品ID或SKU ID");
  }

  const result = await context.client.request<Record<string, unknown>>("GET", `/smqjh-pms/api/v1/sku/${encodeURIComponent(productId)}`, { auth: true });
  if (!result.ok) {
    return systemLookupMiss(request, "SKU", result.message);
  }
  if (!result.data) {
    return systemLookupMiss(request, "SKU", "未查询到匹配商品");
  }

  return {
    platform: "smqjh系统",
    productName: stringValue(result.data.name) || request.params.productKeyword,
    price: formatCent(result.data.price),
    status: "已匹配",
    source: "/smqjh-pms/api/v1/sku/{skuId}",
    note: `类型：SKU${stringValue(result.data.skuSn) ? `，SKU：${stringValue(result.data.skuSn)}` : ""}`,
    evidence: `接口请求：GET /smqjh-pms/api/v1/sku/${encodeURIComponent(productId)}`
  };
}

function systemLookupMiss(request: TaskRunRequest, source: string, note: string): PriceComparisonRow {
  return {
    platform: "smqjh系统",
    productName: request.params.productKeyword,
    price: "未获取",
    status: "待确认",
    source,
    note,
    evidence: note
  };
}

async function firstSuccessfulProductPage(
  keywords: string[],
  fetcher: (keyword: string) => Promise<{ ok: boolean; message: string; data?: unknown; raw?: unknown; status?: number }>
): Promise<{ ok: boolean; message: string; data?: unknown; raw?: unknown; status?: number }> {
  let lastResult: { ok: boolean; message: string; data?: unknown; raw?: unknown; status?: number } | undefined;

  for (const keyword of keywords) {
    const result = await fetcher(keyword);
    lastResult = result;
    if (result.ok && firstPageRecord(result.data)) {
      return result;
    }
  }

  return lastResult ?? { ok: false, message: "未发起商品查询" };
}

function keywordCandidates(keyword: string): string[] {
  const compact = keyword.replace(/\s+/g, " ").trim();
  const withoutSpec = compact
    .replace(/单瓶|一瓶|1瓶|单件|一件|单个/g, " ")
    .replace(/\d+(?:\.\d+)?\s*(?:ml|mL|ML|毫升|l|L|升)(?:\s*[xX*×]\s*\d+\s*(?:瓶|桶|罐|支|盒)?)?/g, " ")
    .replace(/\s+/g, " ")
    .trim();
  return Array.from(new Set([compact, withoutSpec].filter(Boolean)));
}

function firstPageRecord(data: unknown): Record<string, unknown> | undefined {
  if (!data || typeof data !== "object") {
    return undefined;
  }
  const value = data as Record<string, unknown>;
  const list = Array.isArray(value.list) ? value.list : Array.isArray(value.records) ? value.records : undefined;
  const first = list?.[0];
  return first && typeof first === "object" ? (first as Record<string, unknown>) : undefined;
}

function compactQuery(query: Record<string, string | undefined>): Record<string, string> {
  return Object.fromEntries(Object.entries(query).filter(([, value]) => value !== undefined && value !== "")) as Record<string, string>;
}

function normalizePriceText(value?: string): string {
  const text = (value ?? "").trim();
  if (!text) {
    return "";
  }
  return text.startsWith("¥") || text.includes("元") ? text : `¥${text}`;
}

function formatYuan(value: unknown): string {
  const number = Number(value);
  return Number.isFinite(number) ? `¥${number.toFixed(2)}` : "未获取";
}

function formatCent(value: unknown): string {
  const number = Number(value);
  return Number.isFinite(number) ? `¥${(number / 100).toFixed(2)}` : "未获取";
}

function stringValue(value: unknown): string {
  return value === undefined || value === null ? "" : String(value);
}

function toMarkdownTable(rows: PriceComparisonRow[]): string {
  const header = "| 平台 | 商品/规格 | 价格 | 折算单价 | 状态 | 来源 | 备注 | 依据 |";
  const divider = "| --- | --- | --- | --- | --- | --- | --- | --- |";
  const body = rows.map((row) =>
    [
      row.platform,
      row.productName,
      row.price,
      row.unitPrice ?? "",
      row.status,
      row.sourceUrl ? `${row.source}：${row.sourceUrl}` : row.source,
      row.note,
      row.evidence ?? ""
    ]
      .map(escapeMarkdownCell)
      .join(" | ")
  );
  return [header, divider, ...body.map((line) => `| ${line} |`)].join("\n");
}

function escapeMarkdownCell(value: string): string {
  return value.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
}

export function listTasks(): AgentTask[] {
  return ACTIVE_TASK_IDS
    .map((id) => TASKS.find((task) => task.id === id))
    .filter((task): task is AgentTask => Boolean(task));
}

export async function executeTask(
  request: TaskRunRequest,
  context: TaskContext
): Promise<Omit<TaskRunResult, "taskId" | "startedAt" | "finishedAt">> {
  const task = TASKS.find((item) => item.id === request.taskId);
  if (!task) {
    return {
      success: false,
      message: `未知任务：${request.taskId}`
    };
  }

  const missing = task.params.filter((param) => param.required && !request.params[param.key]);
  if (missing.length > 0) {
    return {
      success: false,
      message: `缺少必填参数：${missing.map((param) => param.label).join("、")}`
    };
  }

  context.log(`开始执行：${task.title}`, { taskId: task.id });

  const customHandler = handlers[task.id];
  if (customHandler) {
    return customHandler(request, context);
  }

  if (!task.endpoint || !task.method) {
    return {
      success: false,
      message: "任务尚未绑定后端接口"
    };
  }

  if (context.configStore.get().dryRun && task.dangerLevel !== "safe") {
    return {
      success: true,
      message: "当前为预演模式，已拦截真实后端写入",
      data: {
        endpoint: task.endpoint,
        method: task.method,
        params: request.params
      }
    };
  }

  const result = await context.client.request(task.method, task.endpoint, {
    auth: task.requiresAuth,
    query: task.method === "GET" ? request.params : undefined,
    body: task.method !== "GET" ? request.params : undefined
  });

  return {
    success: result.ok,
    message: result.ok ? "任务执行完成" : result.message,
    statusCode: result.status,
    data: result.data ?? result.raw
  };
}
