from __future__ import annotations

from html import escape
from pathlib import Path
from urllib.parse import quote

from openpyxl import load_workbook


INPUT = Path(
    "docs/final-results/20260813-yifangbao-phone-legal-enrichment/"
    "7月线索导入模板-0715-0812-电话法人已补全-可靠来源二次补查.xlsx"
)
OUTPUT = Path("docs/final-results/20260813-yifangbao-phone-legal-enrichment/index.html")


def text(value: object) -> str:
    return str(value or "").strip()


def linkify(value: object) -> str:
    url = text(value)
    if not url:
        return ""
    return f'<a href="{escape(url)}" target="_blank" rel="noreferrer">打开来源</a>'


def status_badge(value: str) -> str:
    klass = "ok" if value == "已补充" else "review" if value == "待复核" else "gap"
    return f'<span class="status {klass}">{escape(value)}</span>'


def main() -> None:
    wb = load_workbook(INPUT, data_only=True)
    ws = wb["企业补全汇总"]
    rows = [list(row) for row in ws.iter_rows(min_row=2, values_only=True)]
    unique_companies = len(rows)
    final_phone = sum(1 for row in rows if text(row[3]))
    added_phone = sum(1 for row in rows if text(row[4]))
    legal_count = sum(1 for row in rows if text(row[5]))
    legal_review = sum(1 for row in rows if text(row[6]) == "待复核")
    enriched = sum(1 for row in rows if text(row[4]) or text(row[5]))
    review_rows = sum(1 for row in rows if text(row[8]) == "medium" or "复核" in text(row[11]))
    phone_gap_companies = sum(1 for row in rows if text(row[1]) and not text(row[3]))
    second_round_names = {
        "青岛天高智慧科技有限公司",
        "中国农业银行股份有限公司潮州分行",
        "成都鑫金大数据有限责任公司",
        "中国银行股份有限公司福州闽都支行",
        "张家港市大数据科技有限公司",
        "广州市公用公交站场管理服务有限公司",
        "中国电信股份有限公司牡丹江分公司",
    }
    second_round_companies = sum(1 for row in rows if text(row[1]) in second_round_names)

    body = []
    for row in rows:
        legal_status = text(row[6])
        body.append(
            f"""<tr data-status="{escape(legal_status)}" data-phone-gap="{str(not bool(text(row[3]))).lower()}">
              <td class="num">{escape(text(row[0]))}</td>
              <td class="company">{escape(text(row[1]))}</td>
              <td>{escape(text(row[2]))}</td>
              <td>{escape(text(row[3]))}</td>
              <td>{escape(text(row[4]))}</td>
              <td>{escape(text(row[5]))}</td>
              <td>{status_badge(legal_status)}</td>
              <td>{escape(text(row[7]))}</td>
              <td>{escape(text(row[8]))}</td>
              <td>{linkify(row[9])}</td>
              <td class="evidence">{escape(text(row[10]))}</td>
              <td class="note">{escape(text(row[11]))}</td>
              <td>{escape(text(row[12]))}</td>
            </tr>"""
        )

    workbook_href = quote(INPUT.name)
    html = f"""<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>7月线索电话与法人补全结果</title>
  <style>
    :root {{
      color-scheme: light dark;
      --bg: #f4f7fa;
      --surface: #ffffff;
      --surface-2: #edf3f7;
      --text: #17212b;
      --muted: #566574;
      --line: #d6e0e8;
      --accent: #2f5d83;
      --accent-strong: #234864;
      --accent-text: #ffffff;
      --ok-bg: #e2f0d9;
      --ok-text: #315b2d;
      --review-bg: #f4cccc;
      --review-text: #7c2929;
      --gap-bg: #fce4d6;
      --gap-text: #7b492d;
      --radius: 10px;
    }}
    @media (prefers-color-scheme: dark) {{
      :root {{
        --bg: #111820;
        --surface: #17222c;
        --surface-2: #1d2a35;
        --text: #edf3f7;
        --muted: #afbcc7;
        --line: #334553;
        --accent: #75a8cf;
        --accent-strong: #9bc3e2;
        --accent-text: #10212d;
        --ok-bg: #234228;
        --ok-text: #c4e8c4;
        --review-bg: #552b2b;
        --review-text: #ffd0d0;
        --gap-bg: #4d382c;
        --gap-text: #f2d5c5;
      }}
    }}
    * {{ box-sizing: border-box; }}
    body {{
      margin: 0;
      background: var(--bg);
      color: var(--text);
      font-family: "Aptos", "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
      line-height: 1.55;
    }}
    a {{ color: var(--accent); text-underline-offset: 3px; }}
    a:hover {{ color: var(--accent-strong); }}
    a:focus-visible, button:focus-visible, input:focus-visible {{
      outline: 3px solid var(--accent);
      outline-offset: 2px;
    }}
    main {{ width: min(1480px, calc(100% - 40px)); margin: 0 auto; padding: 48px 0 72px; }}
    header {{ max-width: 940px; }}
    h1 {{ margin: 0; font-size: clamp(32px, 5vw, 58px); line-height: 1.08; }}
    .lead {{ margin: 18px 0 0; max-width: 760px; color: var(--muted); font-size: 17px; }}
    .actions {{ display: flex; flex-wrap: wrap; gap: 12px; margin-top: 26px; }}
    .button {{
      display: inline-flex;
      align-items: center;
      justify-content: center;
      min-height: 44px;
      padding: 0 18px;
      border: 1px solid var(--accent);
      border-radius: var(--radius);
      background: var(--accent);
      color: var(--accent-text);
      font-weight: 700;
      text-decoration: none;
      white-space: nowrap;
    }}
    .button:hover {{ background: var(--accent-strong); color: var(--accent-text); transform: translateY(-1px); }}
    .button:active {{ transform: translateY(1px); }}
    .metrics {{
      display: grid;
      grid-template-columns: 1.2fr repeat(4, 1fr);
      gap: 12px;
      margin: 36px 0 0;
    }}
    .metric {{ padding: 18px; border-radius: var(--radius); background: var(--surface); border: 1px solid var(--line); }}
    .metric:first-child {{ background: var(--accent); color: var(--accent-text); border-color: var(--accent); }}
    .metric strong {{ display: block; font-size: 30px; line-height: 1; font-variant-numeric: tabular-nums; }}
    .metric span {{ display: block; margin-top: 9px; color: var(--muted); font-size: 13px; }}
    .metric:first-child span {{ color: var(--accent-text); opacity: .86; }}
    .notice {{
      margin: 22px 0 0;
      padding: 18px 20px;
      border-left: 4px solid var(--accent);
      border-radius: 0 var(--radius) var(--radius) 0;
      background: var(--surface);
      color: var(--muted);
    }}
    .notice strong {{ color: var(--text); }}
    .controls {{
      display: grid;
      grid-template-columns: minmax(240px, 1fr) auto;
      gap: 12px;
      align-items: center;
      margin: 34px 0 14px;
    }}
    input[type="search"] {{
      width: 100%;
      min-height: 44px;
      border: 1px solid var(--line);
      border-radius: var(--radius);
      background: var(--surface);
      color: var(--text);
      padding: 0 14px;
      font: inherit;
    }}
    .filters {{ display: flex; flex-wrap: wrap; gap: 8px; }}
    .filters button {{
      min-height: 40px;
      border: 1px solid var(--line);
      border-radius: var(--radius);
      background: var(--surface);
      color: var(--text);
      padding: 0 13px;
      font: inherit;
      cursor: pointer;
      white-space: nowrap;
    }}
    .filters button[aria-pressed="true"] {{ border-color: var(--accent); background: var(--accent); color: var(--accent-text); }}
    .table-wrap {{ overflow: auto; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }}
    table {{ width: 100%; min-width: 1740px; border-collapse: collapse; font-size: 13px; }}
    th, td {{ padding: 11px 12px; text-align: left; vertical-align: top; border-bottom: 1px solid var(--line); }}
    th {{ position: sticky; top: 0; background: var(--accent); color: var(--accent-text); font-weight: 700; z-index: 1; }}
    tbody tr:hover {{ background: var(--surface-2); }}
    tbody tr:last-child td {{ border-bottom: 0; }}
    .num {{ text-align: right; font-variant-numeric: tabular-nums; }}
    .company {{ min-width: 220px; font-weight: 650; }}
    .evidence {{ min-width: 320px; }}
    .note {{ min-width: 260px; color: var(--muted); }}
    .status {{ display: inline-block; padding: 3px 8px; border-radius: 7px; white-space: nowrap; font-weight: 700; }}
    .status.ok {{ background: var(--ok-bg); color: var(--ok-text); }}
    .status.review {{ background: var(--review-bg); color: var(--review-text); }}
    .status.gap {{ background: var(--gap-bg); color: var(--gap-text); }}
    .empty {{ display: none; padding: 28px; text-align: center; color: var(--muted); }}
    footer {{ margin-top: 22px; color: var(--muted); font-size: 13px; }}
    @media (max-width: 900px) {{
      main {{ width: min(100% - 28px, 1480px); padding-top: 32px; }}
      .metrics {{ grid-template-columns: repeat(2, minmax(0, 1fr)); }}
      .metric:first-child {{ grid-column: 1 / -1; }}
      .controls {{ grid-template-columns: 1fr; }}
    }}
    @media (max-width: 560px) {{
      .metrics {{ grid-template-columns: 1fr; }}
      .metric:first-child {{ grid-column: auto; }}
      .actions, .filters {{ width: 100%; }}
      .button {{ width: 100%; }}
      .filters button {{ flex: 1 1 calc(50% - 8px); }}
    }}
    @media (prefers-reduced-motion: no-preference) {{
      .button {{ transition: background-color .18s ease, transform .18s ease; }}
    }}
  </style>
</head>
<body>
  <main>
    <header>
      <h1>7月线索电话与法人二次补查结果</h1>
      <p class="lead">已将二次公开检索中可核验的企业电话继续写回原表，并保留企业级汇总页，便于按公司复核和继续跟进。</p>
      <div class="actions">
        <a class="button" href="./{workbook_href}" download>下载二次补查版 Excel</a>
      </div>
    </header>
    <section class="metrics" aria-label="补全统计">
      <div class="metric"><strong>226</strong><span>原始线索记录</span></div>
      <div class="metric"><strong>{unique_companies}</strong><span>规范化唯一中标单位</span></div>
      <div class="metric"><strong>{final_phone}</strong><span>已有最终可用电话</span></div>
      <div class="metric"><strong>{legal_count}</strong><span>已补法定代表人</span></div>
      <div class="metric"><strong>{phone_gap_companies}</strong><span>仍缺电话的企业</span></div>
    </section>
    <section class="notice">
      <strong>数据边界：</strong>本次在上一版天眼查补查表上继续核对未找到可靠来源或最终电话为空的企业，二次新增可用电话覆盖 {second_round_companies} 家企业。官网、银行官方页、公共资源交易主体信息和主管部门公开名单标黄，第三方汇聚页或公告联系人电话标红提示复核。目前企业汇总中有 {added_phone} 家单位带新增或备用电话，{enriched} 家单位含电话或法人补充信息，{review_rows} 家需外呼前复核。
    </section>
    <section aria-labelledby="table-title">
      <div class="controls">
        <input id="search" type="search" aria-label="搜索企业、电话或法人" placeholder="搜索企业、电话、法人、来源">
        <div class="filters" role="group" aria-label="按法人状态筛选">
          <button type="button" data-filter="全部" aria-pressed="true">全部</button>
          <button type="button" data-filter="已补充" aria-pressed="false">已补充</button>
          <button type="button" data-filter="待复核" aria-pressed="false">待复核</button>
          <button type="button" data-filter="未找到可靠来源" aria-pressed="false">未找到可靠来源</button>
          <button type="button" data-filter="电话缺口" aria-pressed="false">电话缺口</button>
        </div>
      </div>
      <div class="table-wrap">
        <table id="results">
          <thead>
            <tr>
              <th>序号</th><th>中标单位</th><th>原表电话</th><th>最终可用电话</th><th>本次新增电话</th>
              <th>法定代表人</th><th>法人状态</th><th>来源类型</th><th>可信度</th><th>来源</th>
              <th>证据摘要</th><th>缺口/说明</th><th>原表行号</th>
            </tr>
          </thead>
          <tbody>
            {"".join(body)}
          </tbody>
        </table>
        <div id="empty" class="empty">没有匹配的企业记录。</div>
      </div>
    </section>
    <footer>生成时间：2026-08-18。黄色为新增确认信息，红色为需复核信息。</footer>
  </main>
  <script>
    const search = document.getElementById('search');
    const buttons = Array.from(document.querySelectorAll('[data-filter]'));
    const rows = Array.from(document.querySelectorAll('#results tbody tr'));
    const empty = document.getElementById('empty');
    let currentFilter = '全部';

    function applyFilters() {{
      const query = search.value.trim().toLowerCase();
      let shown = 0;
      for (const row of rows) {{
        const matchesText = !query || row.innerText.toLowerCase().includes(query);
        const matchesFilter = currentFilter === '全部' || row.dataset.status === currentFilter || (currentFilter === '电话缺口' && row.dataset.phoneGap === 'true');
        const visible = matchesText && matchesFilter;
        row.style.display = visible ? '' : 'none';
        if (visible) shown += 1;
      }}
      empty.style.display = shown ? 'none' : 'block';
    }}

    search.addEventListener('input', applyFilters);
    for (const button of buttons) {{
      button.addEventListener('click', () => {{
        currentFilter = button.dataset.filter;
        for (const item of buttons) item.setAttribute('aria-pressed', String(item === button));
        applyFilters();
      }});
    }}
  </script>
</body>
</html>
"""
    OUTPUT.write_text(html, encoding="utf-8")
    print({"output": str(OUTPUT), "rows": len(rows), "final_phone": final_phone})


if __name__ == "__main__":
    main()
