/* global React, Icons */
// ============================================================
//  Section registry + shared section renderers
//  Every résumé section is data-driven so the builder can let the
//  user drag, drop, add and remove "segments" in any order, and the
//  unified template engine (ResumePreview.jsx) renders them.
// ============================================================

// key -> { label, icon, kind, blurb }
const SECTION_DEFS = {
  summary:        { label: "Summary",        icon: "pen",    kind: "text",    blurb: "A short professional intro" },
  experience:     { label: "Experience",     icon: "brief",  kind: "jobs",    blurb: "Roles, companies & impact" },
  projects:       { label: "Projects",       icon: "folder", kind: "projects",blurb: "Selected work & side projects" },
  education:      { label: "Education",       icon: "cap",    kind: "edu",     blurb: "Degrees & schools" },
  skills:         { label: "Skills",         icon: "star",   kind: "skills",  blurb: "Tools & competencies" },
  certifications: { label: "Certifications", icon: "cert",   kind: "named",   blurb: "Licenses & credentials" },
  awards:         { label: "Awards",         icon: "trophy", kind: "named",   blurb: "Honors & recognition" },
  languages:      { label: "Languages",      icon: "lang",   kind: "leveled", blurb: "Spoken & written languages" },
  publications:   { label: "Publications",   icon: "book",   kind: "pubs",    blurb: "Papers, articles & talks" },
  volunteer:      { label: "Volunteering",   icon: "heart",  kind: "jobs",    blurb: "Community & pro-bono work" },
  courses:        { label: "Coursework",     icon: "list",   kind: "chips",   blurb: "Relevant courses" },
  interests:      { label: "Interests",      icon: "spark",  kind: "chips",   blurb: "Hobbies & personal" },
  references:     { label: "References",     icon: "user",   kind: "refs",    blurb: "Referees or 'on request'" },
};

// natural fallback order if a résumé has no explicit sectionOrder
const DEFAULT_ORDER = [
  "summary", "experience", "projects", "skills", "certifications",
  "awards", "languages", "education", "volunteer", "publications",
  "courses", "interests", "references",
];

const sectionLabel = (k) => (SECTION_DEFS[k] ? SECTION_DEFS[k].label : k);

// does this résumé hold any content for section k?
function sectionHasData(k, d) {
  if (k === "summary") return !!(d.summary && d.summary.trim());
  if (k === "references") {
    const r = d.references;
    return d.referencesOnRequest || (Array.isArray(r) && r.length > 0);
  }
  const v = d[k];
  return Array.isArray(v) && v.length > 0;
}

// the effective, ordered, non-empty list of section keys to render.
// When an explicit sectionOrder exists it is authoritative (so removing a
// segment in the organizer truly removes it). Only fall back to DEFAULT_ORDER
// when no order has been set yet.
function activeSections(d) {
  if (d.sectionOrder && d.sectionOrder.length) {
    return d.sectionOrder.filter((k) => SECTION_DEFS[k] && sectionHasData(k, d));
  }
  return DEFAULT_ORDER.filter((k) => SECTION_DEFS[k] && sectionHasData(k, d));
}

// ---- contacts ----
const CICONS = { email: Icons.mail, phone: Icons.phone, website: Icons.globe, location: Icons.pin };
function contactList(d, keys) {
  return (keys || ["email", "phone", "website", "location"])
    .map((k) => ({ k, Ic: CICONS[k], val: d[k] }))
    .filter((x) => x.val);
}

const initials = (name) => (name || "?").trim().split(" ").map((w) => w[0]).slice(0, 2).join("");

// ---- photo with monogram fallback ----
//  When a real photo URL is set we layer it over the accent gradient, so a
//  slow / failed remote load still reads as an intentional colored avatar
//  rather than an empty white box.
function Photo({ d, shape = "circle", className = "" }) {
  if (d.photo) return (
    <div className={`rphoto has-photo ${shape} ${className}`}
      style={{ backgroundImage: `url("${d.photo}"), linear-gradient(140deg, var(--pa), var(--pa2))` }} />
  );
  return <div className={`rphoto mono ${shape} ${className}`}>{initials(d.name)}</div>;
}

// ---- proficiency scale ("vector") model ---------------------------------
//  A skill is stored as either a plain string (legacy) or { name, level }
//  where level is 1..SKILL_MAX. The scale is how much you've used a tool /
//  language — rendered as dots, segments or signal bars instead of plain text.
const SKILL_MAX = 5;
const LEVEL_WORDS = ["", "Novice", "Familiar", "Proficient", "Advanced", "Expert"];
// level -> fill % for the bar / ring variants (reads naturally, never 0/100)
const LVL_PCT = { 1: 50, 2: 65, 3: 78, 4: 90, 5: 98 };

const clampLvl = (n) => { n = Math.round(Number(n)); return Math.max(1, Math.min(SKILL_MAX, Number.isFinite(n) && n ? n : 4)); };
const SKILL_LVLS = [94, 88, 96, 82, 90, 85, 92, 80, 87, 83];
const lvl = (i) => SKILL_LVLS[i % SKILL_LVLS.length];
// legacy strings get a varied-but-plausible level so old résumés still look alive
const legacyTier = (i) => clampLvl(Math.round(lvl(i) / 20));
const skillName = (s) => (s && typeof s === "object" ? s.name : s);
const normSkill = (s, i = 0) =>
  s && typeof s === "object" ? { name: s.name || "", level: clampLvl(s.level) } : { name: String(s ?? ""), level: legacyTier(i) };

// spoken-language proficiency words -> tier on the same 1..5 scale
const LANG_TIER = {
  native: 5, bilingual: 5, mother: 5, fluent: 5, "full professional": 4, professional: 4,
  advanced: 4, "working professional": 4, conversational: 3, intermediate: 3, limited: 2,
  "limited working": 2, basic: 2, elementary: 2, beginner: 1,
};
const langTier = (level) => {
  if (level == null) return 3;
  const k = String(level).toLowerCase().trim();
  if (LANG_TIER[k] != null) return LANG_TIER[k];
  const hit = Object.keys(LANG_TIER).find((w) => k.includes(w));
  return hit ? LANG_TIER[hit] : 3;
};

// ---- the scale renderers (the "vectors") ----
function ScaleDots({ level, max = SKILL_MAX }) {
  return <span className="sdots" role="img" aria-label={`${level} of ${max}`}>{Array.from({ length: max }, (_, i) => <i key={i} className={i < level ? "on" : ""} />)}</span>;
}
function ScaleSegs({ level, max = SKILL_MAX }) {
  return <span className="sseg" role="img" aria-label={`${level} of ${max}`}>{Array.from({ length: max }, (_, i) => <i key={i} className={i < level ? "on" : ""} />)}</span>;
}
function ScaleSignal({ level, max = SKILL_MAX }) {
  return <span className="ssig" role="img" aria-label={`${level} of ${max}`}>{Array.from({ length: max }, (_, i) => <i key={i} className={i < level ? "on" : ""} style={{ height: 5 + i * 3 + "px" }} />)}</span>;
}
function Scale({ kind, level, max }) {
  if (kind === "segments") return <ScaleSegs level={level} max={max} />;
  if (kind === "signal") return <ScaleSignal level={level} max={max} />;
  return <ScaleDots level={level} max={max} />;
}

// one labelled scale row (name on the left, scale on the right)
function ScaleRow({ name, level, kind, showWord, editPath }) {
  return (
    <div className="srow">
      <span className="srow-n" data-edit={editPath}>{name}</span>
      <span className="srow-r">
        {showWord && <em className="srow-w">{LEVEL_WORDS[level]}</em>}
        <Scale kind={kind} level={level} />
      </span>
    </div>
  );
}

// ---- skill visualisations ----
function SkillBar({ name, pct, word, editPath }) {
  return (
    <div className="sbar">
      <div className="sbar-h"><span data-edit={editPath}>{name}</span><i>{word || pct + "%"}</i></div>
      <div className="sbar-track"><div className="sbar-fill" style={{ width: pct + "%" }} /></div>
    </div>
  );
}

function SkillRing({ name, pct, editPath }) {
  const R = 22, C = 2 * Math.PI * R;
  return (
    <div className="sring">
      <svg viewBox="0 0 56 56" width="56" height="56">
        <circle cx="28" cy="28" r={R} className="sring-bg" />
        <circle cx="28" cy="28" r={R} className="sring-fg"
          strokeDasharray={C} strokeDashoffset={C * (1 - pct / 100)} transform="rotate(-90 28 28)" />
        <text x="28" y="28" className="sring-t" dominantBaseline="central" textAnchor="middle">{pct}%</text>
      </svg>
      <span className="sring-l" data-edit={editPath}>{name}</span>
    </div>
  );
}

function RSkills({ items, variant, showWord }) {
  const norm = (items || []).map((s, i) => normSkill(s, i));
  if (variant === "bars")
    return <div className="rbars">{norm.slice(0, 9).map((s, i) => <SkillBar key={i} name={s.name} pct={LVL_PCT[s.level]} word={LEVEL_WORDS[s.level]} editPath={`skills:${i}:name`} />)}</div>;
  if (variant === "rings")
    return <div className="rrings">{norm.slice(0, 6).map((s, i) => <SkillRing key={i} name={s.name} pct={LVL_PCT[s.level]} editPath={`skills:${i}:name`} />)}</div>;
  if (variant === "dots" || variant === "segments" || variant === "signal")
    return <div className={`rscale rscale-${variant}`}>{norm.slice(0, 12).map((s, i) => <ScaleRow key={i} name={s.name} level={s.level} kind={variant} showWord={showWord} editPath={`skills:${i}:name`} />)}</div>;
  if (variant === "list")
    return <div className="rskill-list">{norm.map((s, i) => <span key={i} data-edit={`skills:${i}:name`}>{s.name}</span>)}</div>;
  return <div className={`rtags ${variant === "pills" ? "pills" : ""}`}>{norm.map((s, i) => <span key={i} data-edit={`skills:${i}:name`}>{s.name}</span>)}</div>;
}

// ---- experience / volunteer entry ----
function RJob({ j, secKey = "experience" }) {
  return (
    <div className="rjob">
      <div className="rjob-h">
        <span className="rjob-r" data-edit={`${secKey}:${j.id}:role`}>{j.role}</span>
        <span className="rjob-w when">{j.start}{j.end ? ` – ${j.end}` : ""}</span>
      </div>
      <div className="rjob-c">
        <span data-edit={`${secKey}:${j.id}:company`}>{j.company}</span>
        {j.location ? <> · <span data-edit={`${secKey}:${j.id}:location`}>{j.location}</span></> : ""}
      </div>
      {j.bullets && j.bullets.filter(Boolean).length > 0 && (
        <ul className="r-bullets">{j.bullets.map((b, i) => (b && b.trim() ? <li key={i} data-edit={`${secKey}:${j.id}:bullet:${i}`}>{b}</li> : null))}</ul>
      )}
    </div>
  );
}

function REdu({ e }) {
  return (
    <div className="redu">
      <div className="redu-h"><b data-edit={`education:${e.id}:school`}>{e.school}</b><span className="redu-w when">{e.start}{e.end ? ` – ${e.end}` : ""}</span></div>
      {e.degree && <div className="redu-d" data-edit={`education:${e.id}:degree`}>{e.degree}</div>}
    </div>
  );
}

// ---- the body of one section (heading is added by the engine) ----
function SectionBody({ k, d, variant, tpl }) {
  const def = SECTION_DEFS[k];
  if (!def) return null;
  // graphic templates show a proficiency scale; ATS / list templates stay text-only
  const graphic = tpl && tpl.skills && tpl.skills !== "list";
  const showWord = !!(tpl && tpl.scaleWord);
  switch (def.kind) {
    case "text":
      return <p className="rsum" data-edit="summary">{d.summary}</p>;
    case "jobs": {
      const arr = k === "experience" ? d.experience : d.volunteer;
      return <div className="rjobs">{(arr || []).map((j) => <RJob key={j.id} j={j} secKey={k} />)}</div>;
    }
    case "projects":
      return (
        <div className="rprojs">
          {d.projects.map((p) => (
            <div className="rproj" key={p.id}><b data-edit={`projects:${p.id}:name`}>{p.name}</b>{p.desc ? <span className="rproj-d"> — <span data-edit={`projects:${p.id}:desc`}>{p.desc}</span></span> : null}</div>
          ))}
        </div>
      );
    case "edu":
      return <div className="redus">{d.education.map((e) => <REdu key={e.id} e={e} />)}</div>;
    case "skills":
      return <RSkills items={d.skills} variant={variant} showWord={showWord} />;
    case "named": {
      const arr = d[k] || [];
      return (
        <div className="rnamed">
          {arr.map((x) => (
            <div className="rnamed-i" key={x.id}>
              <b data-edit={`${k}:${x.id}:name`}>{x.name}</b>
              {(x.issuer || x.year) && <span className="rnamed-m">{[x.issuer, x.year].filter(Boolean).join(" · ")}</span>}
            </div>
          ))}
        </div>
      );
    }
    case "leveled": {
      // languages get the same scale 'vector' on graphic templates; keep the
      // word too (CEFR-ish proficiency reads clearer than dots alone here)
      const lkind = (tpl && (tpl.skills === "segments" || tpl.skills === "signal")) ? tpl.skills : "dots";
      return (
        <div className={`rlangs ${graphic ? "rlangs-scale" : ""}`}>
          {d.languages.map((l) => (
            <div className="rlang" key={l.id}>
              <span className="rlang-n" data-edit={`languages:${l.id}:name`}>{l.name}</span>
              {graphic
                ? <span className="rlang-r">{l.level && <em className="rlang-l">{l.level}</em>}<Scale kind={lkind} level={langTier(l.level)} /></span>
                : (l.level && <span className="rlang-l">{l.level}</span>)}
            </div>
          ))}
        </div>
      );
    }
    case "pubs":
      return (
        <div className="rpubs">
          {d.publications.map((p) => (
            <div className="rpub" key={p.id}><span data-edit={`publications:${p.id}:title`}>{p.title}</span>{p.venue ? <>. <i>{p.venue}</i></> : null}{p.year ? `, ${p.year}` : ""}.</div>
          ))}
        </div>
      );
    case "chips": {
      const arr = d[k] || [];
      return <div className="rchips">{arr.map((s, i) => <span key={i} data-edit={`${k}:${i}`}>{s}</span>)}</div>;
    }
    case "refs":
      if (d.referencesOnRequest && !(d.references && d.references.length)) {
        return <p className="rrefs-note">Available upon request.</p>;
      }
      return (
        <div className="rrefs">
          {(d.references || []).map((r) => (
            <div className="rref" key={r.id}><b data-edit={`references:${r.id}:name`}>{r.name}</b>{r.title && <span>{r.title}</span>}{r.contact && <span className="rref-c">{r.contact}</span>}</div>
          ))}
        </div>
      );
    default:
      return null;
  }
}

Object.assign(window, {
  SECTION_DEFS, DEFAULT_ORDER, sectionLabel, sectionHasData, activeSections,
  contactList, initials, Photo, SkillBar, SkillRing, RSkills, RJob, REdu, SectionBody, lvl,
  SKILL_MAX, LEVEL_WORDS, LVL_PCT, clampLvl, skillName, normSkill, langTier,
  ScaleDots, ScaleSegs, ScaleSignal, Scale, ScaleRow,
});
