"""Output formatting helpers for inquiry scraping."""

from __future__ import annotations

import json
import re
from collections import OrderedDict
from datetime import date, datetime, timedelta
from typing import Optional


DEFAULT_SCOPE_DESC = "今日新建询盘"


def format_display_date_window(
    start_time: Optional[str],
    end_time: Optional[str],
    separator: str = "至",
) -> str:
    if not start_time or not end_time:
        return ""

    try:
        start_date = datetime.strptime(start_time, "%Y-%m-%d").date()
        end_date = datetime.strptime(end_time, "%Y-%m-%d").date()
    except ValueError:
        if start_time == end_time:
            return start_time
        return f"{start_time}{separator}{end_time}"

    if start_date < end_date:
        end_date = end_date - timedelta(days=1)

    display_start = start_date.strftime("%Y-%m-%d")
    display_end = end_date.strftime("%Y-%m-%d")
    if display_start == display_end:
        return display_start
    return f"{display_start}{separator}{display_end}"


def scope_desc_for_mode(
    mode: str,
    start_time: Optional[str] = None,
    end_time: Optional[str] = None,
    buyer_name: Optional[str] = None,
) -> str:
    buyer_name = (buyer_name or "").strip()
    if start_time and end_time:
        date_window = format_display_date_window(start_time, end_time)
        desc = f"{date_window}新建询盘"
        return f"{desc}（买家名称={buyer_name}）" if buyer_name else desc
    if buyer_name:
        return f"最近一年买家名称={buyer_name} 的询盘"
    return DEFAULT_SCOPE_DESC if mode == "new" else mode


def _apply_optional_window_fields(
    payload: dict,
    *,
    start_time: Optional[str],
    end_time: Optional[str],
) -> None:
    if start_time is not None:
        payload["start_time"] = start_time
    if end_time is not None:
        payload["end_time"] = end_time


def _apply_window_meta(payload: dict, window_meta: Optional[dict]) -> None:
    for key, value in (window_meta or {}).items():
        if value is not None:
            payload[key] = value


def dedupe_preserve_order(values: list) -> list[str]:
    seen = set()
    result = []
    for value in values:
        text = str(value or "").strip()
        if not text or text in seen:
            continue
        seen.add(text)
        result.append(text)
    return result


def buyer_key(value: str) -> str:
    return re.sub(r"\s+", " ", str(value or "").strip()).casefold()


def is_tm_item(item: dict) -> bool:
    product = str(item.get("product") or "")
    category = str(item.get("category") or "")
    return product == "Inquiry from TM" or "TM" in category


def is_inquiry_item(item: dict) -> bool:
    if is_tm_item(item):
        return False
    product = str(item.get("product") or "")
    category = str(item.get("category") or "")
    return (
        product == "Inquiry from Product Details Page"
        or "Product Details Page" in category
        or ("询盘" in category and "TM" not in category)
    )


def product_ids_from_detail(item: dict, *, buyer_only: bool = False) -> list[str]:
    ids = []
    detail = item.get("detail") or {}
    for card in detail.get("product_cards") or []:
        if buyer_only and card.get("speaker") != "buyer":
            continue
        ids.append(card.get("product_id"))
    return dedupe_preserve_order(ids)


def ensure_product_summary_fields(item: dict) -> None:
    product_ids = item.get("product_ids")
    buyer_product_ids = item.get("buyer_product_ids")
    detail = item.get("detail") or {}
    has_product_cards = "product_cards" in detail

    if product_ids is None or has_product_cards:
        product_ids = product_ids_from_detail(item)
        item["product_ids"] = product_ids
    if buyer_product_ids is None or has_product_cards:
        buyer_product_ids = product_ids_from_detail(item, buyer_only=True)
        item["buyer_product_ids"] = buyer_product_ids

    item["business_opportunity_count"] = len(dedupe_preserve_order(buyer_product_ids or []))


def build_metrics(inquiries: list[dict]) -> dict:
    for item in inquiries:
        ensure_product_summary_fields(item)

    inquiry_items = [item for item in inquiries if is_inquiry_item(item)]
    tm_items = [item for item in inquiries if is_tm_item(item)]

    inquiry_buyers = {
        buyer_key(item.get("buyer_name"))
        for item in inquiry_items
        if buyer_key(item.get("buyer_name"))
    }
    tm_buyers = {
        buyer_key(item.get("buyer_name"))
        for item in tm_items
        if buyer_key(item.get("buyer_name"))
    }
    buyer_names_by_key = {}
    for item in inquiries:
        b_key = buyer_key(item.get("buyer_name"))
        if b_key and b_key not in buyer_names_by_key:
            buyer_names_by_key[b_key] = str(item.get("buyer_name") or "").strip()
    tm_buyers_covered_by_inquiry = tm_buyers & inquiry_buyers
    tm_buyers_deduped = tm_buyers - inquiry_buyers

    opportunity_keys = OrderedDict()
    for item in inquiries:
        b_key = buyer_key(item.get("buyer_name"))
        if not b_key:
            continue
        for product_id in dedupe_preserve_order(item.get("buyer_product_ids") or []):
            key = f"{b_key}|{product_id}"
            if key not in opportunity_keys:
                opportunity_keys[key] = {
                    "buyer_name": item.get("buyer_name"),
                    "buyer_key": b_key,
                    "product_id": product_id,
                    "inquiry_id": item.get("inquiry_id"),
                    "detail_url": item.get("detail_url"),
                    "source": item.get("product") or item.get("category") or "",
                }

    return {
        "inquiry_buyer_count": len(inquiry_buyers),
        "inquiry_buyer_deduped_count": len(inquiry_buyers),
        "inquiry_count": len(inquiry_items),
        "tm_buyer_count": len(tm_buyers),
        "tm_buyer_deduped_count": len(tm_buyers_deduped),
        "tm_buyer_deduped_removed_count": len(tm_buyers_covered_by_inquiry),
        "tm_buyer_deduped_removed_buyers": [
            {
                "buyer_name": buyer_names_by_key.get(b_key, b_key),
                "buyer_key": b_key,
            }
            for b_key in sorted(tm_buyers_covered_by_inquiry)
        ],
        "deduped_buyer_count": len(inquiry_buyers | tm_buyers),
        "business_opportunity_count": len(opportunity_keys),
        "business_opportunity_keys": list(opportunity_keys.values()),
        "business_opportunity_note": "按已抓到的买家侧产品卡 product_id 计算；缺少产品 ID 的沟通不虚构商机量，未应用 1 分钟/单日点击上限。",
    }


def build_output_data(
    inquiries: list,
    skipped_stats: dict,
    mode: str,
    depth: Optional[str],
    max_detail: int,
    start_time: Optional[str] = None,
    end_time: Optional[str] = None,
    category: str = "",
    assignee: str = "",
    buyer_name: str = "",
    list_metadata: Optional[dict] = None,
    scope_desc: Optional[str] = None,
    batch_meta: Optional[dict] = None,
    window_meta: Optional[dict] = None,
) -> dict:
    for item in inquiries:
        ensure_product_summary_fields(item)

    detail_count = sum(1 for item in inquiries if not item.get("detail_skipped"))
    detail_skipped = sum(1 for item in inquiries if item.get("detail_skipped"))
    failed = [
        item.get("inquiry_id")
        for item in inquiries
        if str(item.get("scrape_status", "")).startswith("error")
    ]
    unreplied_count = sum(
        1
        for item in inquiries
        if item.get("detail") and item["detail"].get("stats", {}).get("unreplied")
    )

    output = {
        "status": "ok",
        "date": str(date.today()),
        "generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "mode": mode,
        "scope_desc": scope_desc
        if scope_desc is not None
        else scope_desc_for_mode(
            mode,
            start_time,
            end_time,
            buyer_name=buyer_name,
        ),
        "filter_category": category or None,
        "filter_assignee": assignee or None,
        "filter_buyer_name": buyer_name or None,
        "depth": depth,
        "max_detail": max_detail,
        "total": len(inquiries),
        "detail_count": detail_count,
        "detail_skipped": detail_skipped,
        "unreplied_count": unreplied_count if depth else 0,
        "metrics": build_metrics(inquiries),
        "skipped": skipped_stats,
        "failed": failed,
        "inquiries": inquiries,
    }
    _apply_optional_window_fields(output, start_time=start_time, end_time=end_time)
    _apply_window_meta(output, window_meta)

    if list_metadata:
        if list_metadata.get("partial"):
            output["partial"] = True
        if list_metadata.get("warnings"):
            output["warnings"] = list_metadata["warnings"]
        if list_metadata.get("pages_loaded"):
            output["pages_loaded"] = list_metadata["pages_loaded"]
        if list_metadata.get("pages_checked"):
            output["pages_checked"] = list_metadata["pages_checked"]

    if batch_meta:
        output["batch"] = batch_meta

    return output


def format_output(
    inquiries: list,
    skipped_stats: dict,
    mode: str,
    depth: Optional[str],
    max_detail: int,
    start_time: Optional[str] = None,
    end_time: Optional[str] = None,
    category: str = "",
    assignee: str = "",
    buyer_name: str = "",
    list_metadata: Optional[dict] = None,
    scope_desc: Optional[str] = None,
    batch_meta: Optional[dict] = None,
    window_meta: Optional[dict] = None,
) -> str:
    return json.dumps(
        build_output_data(
            inquiries=inquiries,
            skipped_stats=skipped_stats,
            mode=mode,
            depth=depth,
            max_detail=max_detail,
            start_time=start_time,
            end_time=end_time,
            category=category,
            assignee=assignee,
            buyer_name=buyer_name,
            list_metadata=list_metadata,
            scope_desc=scope_desc,
            batch_meta=batch_meta,
            window_meta=window_meta,
        ),
        ensure_ascii=False,
        indent=2,
    )


def format_empty_output(
    skipped_stats: dict,
    list_metadata: Optional[dict] = None,
    start_time: Optional[str] = None,
    end_time: Optional[str] = None,
    category: str = "",
    assignee: str = "",
    buyer_name: str = "",
    mode: str = "new",
    scope_desc: Optional[str] = None,
    batch_meta: Optional[dict] = None,
    window_meta: Optional[dict] = None,
) -> str:
    filters = []
    if start_time and end_time:
        filters.append(f"日期>={start_time} 且 <{end_time}")
    else:
        filters.append("最近一年" if buyer_name else "今日")
    if category:
        filters.append(f"分类={category}")
    if assignee:
        filters.append(f"负责人={assignee}")
    if buyer_name:
        filters.append(f"买家名称={buyer_name}")

    filter_desc = "，".join(filters) if filters else "今日"
    output = {
        "status": "ok",
        "date": str(date.today()),
        "generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "mode": mode,
        "scope_desc": scope_desc
        if scope_desc is not None
        else scope_desc_for_mode(
            mode,
            start_time,
            end_time,
            buyer_name=buyer_name,
        ),
        "filter_buyer_name": buyer_name or None,
        "total": 0,
        "metrics": build_metrics([]),
        "skipped": skipped_stats,
        "inquiries": [],
        "message": f"{filter_desc}暂无符合条件的新建询盘",
    }
    _apply_optional_window_fields(output, start_time=start_time, end_time=end_time)
    _apply_window_meta(output, window_meta)

    if list_metadata:
        if list_metadata.get("partial"):
            output["partial"] = True
            output["message"] = f"部分分页加载失败，{filter_desc}无匹配数据"
        if list_metadata.get("warnings"):
            output["warnings"] = list_metadata["warnings"]
        if list_metadata.get("pages_loaded"):
            output["pages_loaded"] = list_metadata["pages_loaded"]
        if list_metadata.get("pages_checked"):
            output["pages_checked"] = list_metadata["pages_checked"]

    if batch_meta:
        output["batch"] = batch_meta

    return json.dumps(output, ensure_ascii=False)


def format_error_output(error: str, hint: Optional[str] = None) -> str:
    result = {
        "status": "error",
        "error": error,
    }
    if hint:
        result["hint"] = hint
    return json.dumps(result, ensure_ascii=False)
