const DATA_BASE = "./data/";

const state = {
  china: [],
  mappings: [],
  details: [],
  categories: {},
  stats: {},
  mappingStats: {},
  profileStats: {},
  sources: {},
  selectedMajor: null,
  query: "",
  mappingFilter: "all",
  aiFilter: "all",
  visible: 40,
  profileQuery: "",
  profileVisible: 48,
};

const majorShort = {
  "1": "组织负责人",
  "2": "专业技术",
  "3": "办事与公共安全",
  "4": "生产与生活服务",
  "5": "农林牧渔",
  "6": "生产制造",
  "7": "军队人员",
  "8": "其他从业人员",
};

const socNames = {
  "11": "管理",
  "13": "商业与金融",
  "15": "计算机与数学",
  "17": "建筑与工程",
  "19": "科学",
  "21": "社区与社会服务",
  "23": "法律",
  "25": "教育与图书",
  "27": "艺术与传媒",
  "29": "医疗专业",
  "31": "医疗支持",
  "33": "公共安全",
  "35": "餐饮",
  "37": "清洁维护",
  "39": "个人服务",
  "41": "销售",
  "43": "行政支持",
  "45": "农林渔业",
  "47": "建筑采掘",
  "49": "安装维修",
  "51": "生产制造",
  "53": "运输物流",
  "NA": "暂无直接对应",
};

const $ = (selector) => document.querySelector(selector);
const escapeHTML = (value = "") => String(value).replace(/[&<>"']/g, ch => ({
  "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#039;"
}[ch]));

async function loadData() {
  if (window.OCCUPATION_ATLAS_DATA) {
    const { china, categories, mappings, details, stats, mappingStats, profileStats, sources } = window.OCCUPATION_ATLAS_DATA;
    Object.assign(state, { china, categories, mappings, details, stats, mappingStats, profileStats, sources });
    state.mappingByCode = new Map(mappings.map(row => [row.chinaCode, row]));
    state.detailByCode = new Map(details.map(row => [row.chinaCode, row]));
    return;
  }
  const [china, categories, mappings, details, stats, mappingStats, profileStats, sources] = await Promise.all([
    fetch(DATA_BASE + "china_occupations.json").then(r => r.json()),
    fetch(DATA_BASE + "china_categories.json").then(r => r.json()),
    fetch(DATA_BASE + "full_mappings.json").then(r => r.json()),
    fetch(DATA_BASE + "all_profiles.json").then(r => r.json()),
    fetch(DATA_BASE + "build_stats.json").then(r => r.json()),
    fetch(DATA_BASE + "mapping_stats.json").then(r => r.json()),
    fetch(DATA_BASE + "profile_stats.json").then(r => r.json()),
    fetch(DATA_BASE + "source_registry.json").then(r => r.json()),
  ]);
  Object.assign(state, { china, categories, mappings, details, stats, mappingStats, profileStats, sources });
  state.mappingByCode = new Map(mappings.map(row => [row.chinaCode, row]));
  state.detailByCode = new Map(details.map(row => [row.chinaCode, row]));
}

function renderStats() {
  $("#stat-middle").textContent = state.stats.chinaMiddleCategories.toLocaleString();
  $("#stat-minor").textContent = state.stats.chinaMinorCategories.toLocaleString();
  $("#stat-tasks").textContent = state.stats.chinaWithTasks.toLocaleString();
  $("#stat-mapped").textContent = state.mappingStats.matched.toLocaleString();
  $("#stat-unmatched").textContent = state.mappingStats.unmatched.toLocaleString();
}

function majorCounts() {
  const counts = {};
  for (const row of state.china) counts[row.majorCode] = (counts[row.majorCode] || 0) + 1;
  return counts;
}

function renderMajorMap() {
  const counts = majorCounts();
  const max = Math.max(...Object.values(counts));
  $("#major-map").innerHTML = Object.entries(majorShort).map(([code, label]) => {
    const count = counts[code] || 0;
    return `<button class="major-card" data-major="${code}">
      <span class="num">0${code}</span>
      <h3>${escapeHTML(state.china.find(x => x.majorCode === code)?.major || label)}</h3>
      <span class="count"><b>${count.toLocaleString()}</b> 个职业</span>
      <i class="bar" style="width:${Math.max(12, count / max * 65)}%"></i>
    </button>`;
  }).join("");
  document.querySelectorAll(".major-card").forEach(card => card.addEventListener("click", () => {
    selectMajor(card.dataset.major);
    $("#browser").scrollIntoView({ behavior: "smooth" });
  }));
}

function renderMajorRail() {
  const counts = majorCounts();
  $("#major-list").innerHTML = Object.entries(majorShort).map(([code, label]) =>
    `<button class="major-filter ${state.selectedMajor === code ? "active" : ""}" data-major="${code}" title="${escapeHTML(label)}">
      <span class="index">0${code}</span><span class="label">${escapeHTML(label)}</span><span class="total">${counts[code]}</span>
    </button>`
  ).join("");
  document.querySelectorAll(".major-filter").forEach(btn => btn.addEventListener("click", () => selectMajor(btn.dataset.major)));
}

function selectMajor(code) {
  state.selectedMajor = code;
  state.visible = 40;
  renderMajorRail();
  renderOccupations();
}

function renderCrosswalk() {
  const aggregate = {};
  const leftCounts = {};
  const rightCounts = {};
  for (const row of state.mappings) {
    const left = row.majorCode;
    const right = row.onetCode ? row.onetCode.slice(0, 2) : "NA";
    const key = `${left}-${right}`;
    aggregate[key] = (aggregate[key] || 0) + 1;
    leftCounts[left] = (leftCounts[left] || 0) + 1;
    rightCounts[right] = (rightCounts[right] || 0) + 1;
  }
  const left = Object.keys(leftCounts).sort();
  const right = Object.keys(rightCounts).sort((a, b) => b === "NA" ? -1 : a.localeCompare(b));
  const width = 900, height = 580, top = 35, bottom = 35;
  const leftGap = (height - top - bottom) / Math.max(1, left.length - 1);
  const rightGap = (height - top - bottom) / Math.max(1, right.length - 1);
  const leftY = Object.fromEntries(left.map((code, i) => [code, top + i * leftGap]));
  const rightY = Object.fromEntries(right.map((code, i) => [code, top + i * rightGap]));
  const flows = Object.entries(aggregate).map(([key, count]) => {
    const [l, r] = key.split("-");
    const y1 = leftY[l], y2 = rightY[r];
    return `<path class="flow" d="M 175 ${y1} C 360 ${y1}, 540 ${y2}, 725 ${y2}" stroke-width="${Math.min(28, Math.max(1.2, Math.sqrt(count) * 1.7))}"><title>${majorShort[l]} → ${socNames[r]}：${count} 个职业</title></path>`;
  }).join("");
  const leftNodes = left.map(code => `<g transform="translate(24 ${leftY[code] - 16})">
    <rect width="151" height="32" rx="3" fill="#29433f"/>
    <text x="10" y="14" class="node-label">${escapeHTML(majorShort[code])}</text>
    <text x="10" y="25" class="node-count">${leftCounts[code]} 个试配</text>
  </g>`).join("");
  const rightNodes = right.map(code => `<g transform="translate(725 ${rightY[code] - 16})">
    <rect width="151" height="32" rx="3" fill="${code === "NA" ? "#554b45" : "#31584f"}"/>
    <text x="10" y="14" class="node-label">${escapeHTML(socNames[code] || `SOC ${code}`)}</text>
    <text x="10" y="25" class="node-count">${rightCounts[code]} 个试配</text>
  </g>`).join("");
  $("#crosswalk-viz").innerHTML = `<svg viewBox="0 0 ${width} ${height}" role="img" aria-label="中美职业大类对应流向图">${flows}${leftNodes}${rightNodes}</svg>`;

  const gradeCounts = {};
  state.mappings.forEach(row => gradeCounts[row.grade] = (gradeCounts[row.grade] || 0) + 1);
  $("#grade-summary").innerHTML = ["A", "B", "C", "D"].map(g => `<div><b>${gradeCounts[g] || 0}</b><span>${g} 级</span></div>`).join("");
}

function getFilteredOccupations() {
  const query = state.query.trim().toLowerCase();
  return state.china.filter(row => {
    if (state.selectedMajor && row.majorCode !== state.selectedMajor) return false;
    const mapping = state.mappingByCode.get(row.code);
    if (state.mappingFilter === "mapped" && mapping?.status !== "matched") return false;
    if (state.mappingFilter === "unmapped" && mapping?.status !== "unmatched") return false;
    if (state.aiFilter !== "all" && (mapping?.status !== "matched" || mapping.ai.level !== state.aiFilter)) return false;
    if (query) {
      const haystack = [row.code, row.title, row.middle, row.minor, mapping?.onetTitle].filter(Boolean).join(" ").toLowerCase();
      if (!haystack.includes(query)) return false;
    }
    return true;
  });
}

function renderOccupations() {
  const rows = getFilteredOccupations();
  const visibleRows = rows.slice(0, state.visible);
  $("#result-count").textContent = `${rows.length.toLocaleString()} 个结果`;
  $("#breadcrumb").textContent = state.selectedMajor ? `第 ${state.selectedMajor} 大类 / ${majorShort[state.selectedMajor]}` : "全部职业";
  $("#occupation-list").innerHTML = visibleRows.map(row => {
    const mapping = state.mappingByCode.get(row.code);
    const hasDetail = true;
    const isMatched = mapping?.status === "matched";
    const aiClass = isMatched ? { "高": "high", "中": "medium", "低": "low" }[mapping.ai.level] : "";
    return `<article class="occupation-row" data-code="${row.code}">
      <div class="occ-code">${row.code}</div>
      <div><div class="occ-title">${escapeHTML(row.title)}</div><div class="occ-path">${escapeHTML(row.middle)} · ${escapeHTML(row.minor)}</div></div>
      <div class="us-match">${isMatched ? `<b>${escapeHTML(mapping.onetTitle)}</b>${mapping.onetCode}` : `<b class="unmatched-text">未找到适配</b>未达到可靠匹配阈值`}</div>
      <div class="badges">
        ${isMatched ? `<span class="pill"><span class="grade grade-${mapping.grade}" style="width:15px;height:15px;font-size:8px">${mapping.grade}</span>${mapping.confidence}%</span><span class="pill ai-${aiClass}">AI ${mapping.ai.level}</span>` : `<span class="pill unmatched-pill">未找到适配</span>`}
        ${hasDetail ? `<span class="pill">完整画像</span>` : ""}
      </div>
    </article>`;
  }).join("") || `<div class="empty-detail"><h3>没有找到匹配结果</h3><p>试试更短的关键词，或者清除筛选条件。</p></div>`;
  document.querySelectorAll(".occupation-row").forEach(row => row.addEventListener("click", () => openOccupation(row.dataset.code)));
  $("#load-more").hidden = visibleRows.length >= rows.length;
}

function profileSearchText(row) {
  return [
    row.chinaCode,
    row.chinaTitle,
    row.classification.major,
    row.classification.middle,
    row.classification.minor,
    row.summary,
    row.description,
    ...(row.tasks || []),
    ...Object.values(row.ksav || {}).flat(),
    row.mapping.onetCode,
    row.mapping.onetTitle,
  ].filter(Boolean).join(" ").toLowerCase();
}

function getProfileMatches(query) {
  const value = query.trim().toLowerCase();
  if (!value) return [];
  const codeLike = /^\d(?:-\d{1,2}){0,3}$/.test(value);
  if (codeLike) {
    return state.details.filter(row => row.chinaCode.toLowerCase().startsWith(value));
  }
  const terms = value.split(/\s+/).filter(Boolean);
  return state.details.map(row => {
    const title = row.chinaTitle.toLowerCase();
    const code = row.chinaCode.toLowerCase();
    const classification = Object.values(row.classification).join(" ").toLowerCase();
    const onet = `${row.mapping.onetCode || ""} ${row.mapping.onetTitle || ""}`.toLowerCase();
    const full = profileSearchText(row);
    if (!terms.every(term => full.includes(term))) return null;
    let score = 0;
    for (const term of terms) {
      if (title === term) score += 120;
      else if (title.startsWith(term)) score += 90;
      else if (title.includes(term)) score += 70;
      if (code.includes(term)) score += 65;
      if (classification.includes(term)) score += 30;
      if (onet.includes(term)) score += 25;
      if (row.summary.toLowerCase().includes(term)) score += 15;
    }
    return { row, score };
  }).filter(Boolean).sort((a, b) => b.score - a.score || a.row.chinaCode.localeCompare(b.row.chinaCode)).map(item => item.row);
}

function profileCardHTML(row) {
  return `<article class="profile-card" data-open="${row.chinaCode}">
    <span class="profile-code">${row.chinaCode}</span><h3>${escapeHTML(row.chinaTitle)}</h3><p>${escapeHTML(row.summary)}</p>
    <div class="profile-path">${escapeHTML(row.classification.middle)} · ${escapeHTML(row.classification.minor)}</div>
    <div class="card-foot"><span class="pill ai-${{高:"high",中:"medium",低:"low"}[row.ai.level]}">AI ${row.ai.level}</span><span class="pill">${row.mapping.status === "matched" ? `${row.mapping.grade} 级对应` : "未找到适配"}</span></div>
  </article>`;
}

function bindProfileCards() {
  document.querySelectorAll("#profile-grid [data-open]").forEach(el => el.addEventListener("click", () => openOccupation(el.dataset.open)));
}

function renderProfileResults() {
  const showcaseCodes = ["1-06-01-02","2-02-38-01","2-02-30-09","2-06-03-00","2-07-04-00","2-08-02-01","2-05-01-01","2-05-08-01","2-10-01-01","2-10-05-00","4-07-02-03","4-02-07-08","3-02-03-01","6-18-02-04","6-31-01-03","4-10-01-05","5-01-02-01","7-01-00-00","8-00-00-00"];
  const matches = state.profileQuery ? getProfileMatches(state.profileQuery) : showcaseCodes.map(code => state.detailByCode.get(code)).filter(Boolean);
  const visibleMatches = matches.slice(0, state.profileVisible);
  $("#profile-result-label").textContent = state.profileQuery ? `“${state.profileQuery}”的匹配结果` : "推荐岗位";
  $("#profile-result-count").textContent = state.profileQuery
    ? `找到 ${matches.length.toLocaleString()} 个岗位${matches.length > state.profileVisible ? ` · 当前显示前 ${state.profileVisible} 个` : " · 已全部显示"}`
    : `精选示例 · 全库 ${state.details.length.toLocaleString()} 个岗位画像`;
  $("#profile-grid").innerHTML = visibleMatches.length
    ? visibleMatches.map(profileCardHTML).join("")
    : `<div class="profile-empty"><h3>暂未找到相关岗位</h3><p>可尝试职业名称中的核心词，例如“软件”“护士”“电工”或职业编码。</p></div>`;
  bindProfileCards();
  $("#profile-load-more").hidden = !state.profileQuery || visibleMatches.length >= matches.length;
}

function renderProfiles() {
  const featured = state.details.find(x => x.chinaCode === "2-02-10-03") || state.details[0];
  const featuredMapping = featured.mapping;
  $("#profile-feature").innerHTML = `<div class="feature-copy">
    <span class="profile-code">${featured.chinaCode}</span>
    <h3>${escapeHTML(featured.chinaTitle)}</h3>
    <p>${escapeHTML(featured.summary)}</p>
    <div class="feature-meta"><span class="pill">${featuredMapping.status === "matched" ? `对应 ${escapeHTML(featuredMapping.onetTitle)}` : "未找到适配"}</span><span class="pill">AI 影响 ${featured.ai.level}</span></div>
    <button class="button primary" style="margin-top:28px;background:var(--lime);color:var(--ink);border:0;cursor:pointer" data-open="${featured.chinaCode}">查看完整画像</button>
  </div><div class="feature-day"><h4>岗位的一天 · 典型示意</h4>${featured.day.map(item => `<div class="day-item"><time>${escapeHTML(item[0])}</time><span>${escapeHTML(item[1])}</span></div>`).join("")}</div>`;
  $("#profile-feature [data-open]").addEventListener("click", () => openOccupation(featured.chinaCode));
  renderProfileResults();
}

function ksavHTML(ksav) {
  const labels = { K: "Knowledge 知识", S: "Skills 技能", A: "Abilities 能力", V: "Values 价值倾向" };
  return ["K", "S", "A", "V"].map(key => `<div class="ksav-card"><span class="ksav-letter">${key}</span><h4>${labels[key]}</h4><ul>${(ksav[key] || []).map(x => `<li>${escapeHTML(x)}</li>`).join("") || "<li>待补充本土化数据</li>"}</ul></div>`).join("");
}

function sourceTag(field) {
  if (!field) return "";
  const cls = field.type.includes("官方") ? "official" : field.type.includes("网络") ? "web" : "generated";
  return `<span class="source-tag ${cls}" title="${escapeHTML(field.note || "")}">${escapeHTML(field.type)}</span>`;
}

function sourcesHTML(detail) {
  const sources = detail.sourceIds.map(id => state.sources[id]).filter(Boolean);
  return sources.map(source => source.url
    ? `<a class="source-link" href="${escapeHTML(source.url)}" target="_blank" rel="noreferrer"><b>${escapeHTML(source.title)}</b><span>${escapeHTML(source.type)}</span></a>`
    : `<div class="source-link"><b>${escapeHTML(source.title)}</b><span>${escapeHTML(source.type)} · ${escapeHTML(source.location || "")}</span></div>`
  ).join("");
}

function openOccupation(code) {
  const row = state.china.find(x => x.code === code);
  const mapping = state.mappingByCode.get(code);
  const detail = state.detailByCode.get(code);
  if (!row || !detail) return;
  const dialog = $("#detail-dialog");
  const detailMapping = detail.mapping;
  const isMatched = detailMapping.status === "matched";
  $("#dialog-content").innerHTML = `<section class="detail-hero">
      <span class="profile-code">${detail.chinaCode} · ${escapeHTML(row.middle)}</span>
      <h2>${escapeHTML(detail.chinaTitle)}</h2>
      <p class="detail-summary">${escapeHTML(detail.summary)} ${sourceTag(detail.fieldSources.summary)}</p>
      <div class="detail-match ${isMatched ? "" : "detail-unmatched"}"><div><small>中国职业</small><br><b>${escapeHTML(detail.chinaTitle)}</b></div><span class="arrow">→</span><div><small>美国职业适配</small><br><b>${isMatched ? escapeHTML(detailMapping.onetTitle) : "未找到适配"}</b> ${isMatched ? `(${detailMapping.onetCode})` : ""}</div><span class="grade grade-${detailMapping.grade}">${detailMapping.grade}</span></div>
    </section><section class="detail-body">
      <h3>岗位的一天 ${sourceTag(detail.fieldSources.day)} <small style="font-family:inherit;color:var(--muted);font-size:11px">典型示意，非固定日程</small></h3>
      <div class="detail-day">${detail.day.map(item => `<div><time>${escapeHTML(item[0])}</time><span>${escapeHTML(item[1])}</span></div>`).join("")}</div>
      <h3>KSAV 胜任力 ${sourceTag(detail.fieldSources.ksav)}</h3><div class="ksav-grid">${ksavHTML(detail.ksav)}</div>
      <h3>AI 影响 ${sourceTag(detail.fieldSources.ai)}</h3><div class="ai-meter">
        <div class="meter"><div class="meter-head"><span>替代暴露</span><b>${detail.ai.exposure}%</b></div><div class="meter-track"><i style="width:${detail.ai.exposure}%"></i></div></div>
        <div class="meter"><div class="meter-head"><span>增强潜力</span><b>${detail.ai.augmentation}%</b></div><div class="meter-track"><i style="width:${detail.ai.augmentation}%"></i></div></div>
      </div>
      <h3>主要工作任务 ${sourceTag(detail.fieldSources.tasks)}</h3><ul class="task-list">${detail.tasks.map(x => `<li>${escapeHTML(x)}</li>`).join("")}</ul>
      <h3>适配说明</h3><p style="color:var(--muted);font-size:13px">${escapeHTML(detailMapping.rationale)}${isMatched ? `。置信度 ${detailMapping.confidence}%，属于 ${detailMapping.grade} 级对应。` : "。不使用低质量候选强行替代。"}</p>
      <h3>资料来源与生成标注</h3><div class="source-list">${sourcesHTML(detail)}</div>
    </section>`;
  dialog.showModal();
}

function bindEvents() {
  $("#search-input").addEventListener("input", event => { state.query = event.target.value; state.visible = 40; renderOccupations(); });
  $("#mapping-filter").addEventListener("change", event => { state.mappingFilter = event.target.value; state.visible = 40; renderOccupations(); });
  $("#ai-filter").addEventListener("change", event => { state.aiFilter = event.target.value; state.visible = 40; renderOccupations(); });
  $("#clear-major").addEventListener("click", () => selectMajor(null));
  $("#load-more").addEventListener("click", () => { state.visible += 40; renderOccupations(); });
  $("#profile-search-input").addEventListener("input", event => {
    state.profileQuery = event.target.value.trim();
    state.profileVisible = 48;
    renderProfileResults();
  });
  $("#profile-search-clear").addEventListener("click", () => {
    state.profileQuery = "";
    state.profileVisible = 48;
    $("#profile-search-input").value = "";
    renderProfileResults();
    $("#profile-search-input").focus();
  });
  document.querySelectorAll("[data-profile-term]").forEach(button => button.addEventListener("click", () => {
    state.profileQuery = button.dataset.profileTerm;
    state.profileVisible = 48;
    $("#profile-search-input").value = state.profileQuery;
    renderProfileResults();
  }));
  $("#profile-load-more").addEventListener("click", () => {
    state.profileVisible += 48;
    renderProfileResults();
  });
  $("#dialog-close").addEventListener("click", () => $("#detail-dialog").close());
  $("#detail-dialog").addEventListener("click", event => {
    if (event.target === $("#detail-dialog")) $("#detail-dialog").close();
  });
}

async function init() {
  try {
    await loadData();
    renderStats();
    renderMajorMap();
    renderCrosswalk();
    renderMajorRail();
    renderOccupations();
    renderProfiles();
    bindEvents();
  } catch (error) {
    console.error(error);
    document.body.innerHTML = `<main class="empty-detail"><h1>数据未能载入</h1><p>请通过本地 HTTP 服务打开本目录，而不是直接双击 HTML 文件。</p><pre>${escapeHTML(error.message)}</pre></main>`;
  }
}

init();
