// OrgFields — 取引先の「社内担当」(K18-32・0114)と「契約状態・締結日」「確認済み」(K18-33・0115)の小さな部品。
// 管理画面(Admin.jsx の取引先/協力業者タブ)と取引先一覧・詳細が同じ部品を使う。
//
// 社内担当(organizations.owner_user_id)は「この取引先を担当する社内メンバー」(仕様 04:55・SF 営業担当者)。
// 担当固定(default_lead_user_id・受付で案件の担当を決める規則)とは別の列。書けるのは管理者だけ
// (rpc_admin_update_org の許可列)。一覧・詳細は rpc_list_partners の ownerUser {id, name} を読む。
//
// 契約状態(contract_status: 未締結/締結済・空は「不明」)と締結日(contract_signed_on・締結済のときだけ)も
// 管理者が管理画面で書く。確認済み(verified_at・verified_by)は取引先詳細から誰でも付け外しでき、
// 日時と人はサーバが決める(rpc_org_set_verified)。一覧・詳細は rpc_list_partners の
// contractStatus / contractSignedOn / verifiedAt / verifiedBy {id, name} を読む。

/* 取引先の行(rpc_list_partners)から社内担当の名前。無ければ ""。 */
const orgOwnerName = (p) => (p && p.ownerUser && p.ownerUser.name ? String(p.ownerUser.name) : "");

/* 社内担当の候補(有効な利用者)。管理画面の利用者一覧(rpc_admin_list_users・admin だけ)を 1 回だけ読む。
   読めなければ盤面の名簿(担当候補の人だけ)に落とす。{id, name} の配列を名前順で返す。 */
let _orgOwnerUsersP = null;
function orgOwnerCandidates() {
  if (_orgOwnerUsersP) return _orgOwnerUsersP;
  const fromSeed = () => ((window.MStore && window.MStore.masters) ? (window.MStore.masters().users || []) : [])
    .filter(u => u && u.id).map(u => ({ id: u.id, name: u.name || "" }));
  const cloud = window.MarurouCloud || null;
  if (!cloud || typeof cloud.adminListUsers !== "function") return Promise.resolve(fromSeed());
  _orgOwnerUsersP = cloud.adminListUsers()
    .then(rows => (rows || []).filter(u => u && u.id && u.isActive)
      .map(u => ({ id: u.id, name: u.displayName || u.email || "" }))
      .sort((a, b) => a.name.localeCompare(b.name, "ja")))
    .catch(() => { _orgOwnerUsersP = null; return fromSeed(); });
  return _orgOwnerUsersP;
}

/* 管理画面の編集欄。value は users.id(空は null)。今の値が候補に無い(無効になった人など)ときも
   黙って外さず「(名簿外)」として選択肢に残す(保存しても変わらない)。 */
function OrgOwnerSelect({ value, onChange, label }) {
  const [users, setUsers] = React.useState(null);
  React.useEffect(() => {
    let alive = true;
    orgOwnerCandidates().then(list => { if (alive) setUsers(list); });
    return () => { alive = false; };
  }, []);
  const list = users || [];
  const known = !value || list.some(u => u.id === value);
  return (
    <select value={value || ""} onChange={e => onChange && onChange(e.target.value || null)}
      aria-label={label || "社内担当"}
      style={{ height: 28, width: "100%", padding: "0 8px", borderRadius: "var(--radius-md)", borderWidth: 1, borderStyle: "solid",
        borderColor: "var(--input)", background: "var(--card)", color: "var(--foreground)", fontFamily: "var(--font-sans)", fontSize: "var(--text-body-md)" }}>
      <option value="">決めていない</option>
      {!known && <option value={value}>(名簿外)</option>}
      {list.map(u => <option key={u.id} value={u.id}>{u.name}</option>)}
    </select>
  );
}

/* ── 契約状態・締結日(K18-33) ── */
const ORG_CONTRACT_STATES = ["未締結", "締結済"];
/* "2024-02-01" → "2024/02/01"。形が違えばそのまま。 */
const orgYmd = (d) => (typeof d === "string" && /^\d{4}-\d{2}-\d{2}/.test(d) ? d.slice(0, 10).replace(/-/g, "/") : (d || ""));
/* 一覧・詳細に出す言葉。空(不明)は ""。締結済は締結日を添える。 */
const orgContractLabel = (p) => {
  const st = p && p.contractStatus;
  if (!st) return "";
  return st === "締結済" && p.contractSignedOn ? st + " " + orgYmd(p.contractSignedOn) : st;
};

/* 一覧の「契約」列のチップ。空(不明)は「—」。 */
function OrgContractChip({ p }) {
  const st = p && p.contractStatus;
  if (!st) return <span style={{ fontSize: "var(--text-body-sm)", color: "var(--foreground-subtle)" }}>—</span>;
  const on = st === "締結済";
  return <span title={orgContractLabel(p)} style={{ display: "inline-flex", alignItems: "center", height: 16, padding: "0 7px", borderRadius: "var(--radius-sm)",
    fontSize: "var(--text-caption)", fontWeight: 600, whiteSpace: "nowrap",
    color: on ? "var(--brand)" : "var(--muted-foreground)", background: on ? "var(--brand-muted)" : "var(--muted)",
    border: "1px solid " + (on ? "var(--brand-border)" : "var(--border)") }}>{st}</span>;
}

/* 管理画面の編集欄。値は draft の contractStatus / contractSignedOn をそのまま。
   onChange(key, value) を 1 項目ずつ呼ぶ(Admin.jsx の set と同じ形)。締結済でなくなったら締結日も空に戻す
   (サーバは「締結日は締結済のときだけ」を 400 で断る)。 */
function OrgContractFields({ status, signedOn, onChange }) {
  const pick = (v) => {
    onChange && onChange("contractStatus", v);
    if (v !== "締結済" && signedOn) onChange && onChange("contractSignedOn", null);
  };
  const opts = [["", "不明"], ...ORG_CONTRACT_STATES.map(x => [x, x])];
  return (
    <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
      <div role="radiogroup" aria-label="契約状態" style={{ display: "inline-flex", gap: 4 }}>
        {opts.map(([v, label]) => {
          const on = (status || "") === v;
          return <button key={label} type="button" role="radio" aria-checked={on} onClick={() => pick(v || null)} style={{
            all: "unset", cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: "var(--text-body-sm)", fontWeight: on ? 700 : 500,
            padding: "3px 10px", borderRadius: "var(--radius-full)", whiteSpace: "nowrap",
            border: "1px solid " + (on ? "var(--brand)" : "var(--border)"),
            background: on ? "var(--brand-muted)" : "var(--card)", color: on ? "var(--brand)" : "var(--muted-foreground)" }}>{label}</button>;
        })}
      </div>
      <input type="date" aria-label="締結日" value={signedOn || ""} disabled={status !== "締結済"}
        onChange={e => onChange && onChange("contractSignedOn", e.target.value || null)}
        style={{ height: 28, padding: "0 8px", borderRadius: "var(--radius-md)", border: "1px solid var(--input)",
          background: status === "締結済" ? "var(--card)" : "var(--muted)", color: "var(--foreground)",
          fontFamily: "var(--font-sans)", fontSize: "var(--text-body-md)" }} />
    </div>
  );
}

/* ── 確認済み(K18-33) ── */
/* 一覧の「確認」列の印。確認済みは ✓、未確認は —。 */
function OrgVerifiedMark({ p }) {
  const on = !!(p && p.verifiedAt);
  return <span title={on ? "確認済み " + (p.verifiedAt || "") : "未確認"} aria-label={on ? "確認済み" : "未確認"}
    style={{ fontSize: "var(--text-body-sm)", fontWeight: 700, color: on ? "var(--success)" : "var(--foreground-subtle)" }}>{on ? "✓" : "—"}</span>;
}

/* 取引先詳細の「確認済み」欄と付け外しのボタン。押した人と時刻はサーバが決める。
   partner は rpc_list_partners の行。onChanged({ verifiedAt, verifiedBy }) で呼び手の行を差し替えてもらう。 */
function OrgVerifyControl({ partner, onChanged, onToast }) {
  const p = partner || {};
  const [state, setState] = React.useState({ verifiedAt: p.verifiedAt || null, verifiedBy: p.verifiedBy || null });
  const [busy, setBusy] = React.useState(false);
  React.useEffect(() => { setState({ verifiedAt: p.verifiedAt || null, verifiedBy: p.verifiedBy || null }); }, [p.id, p.verifiedAt]);
  const cloud = window.MarurouCloud || null;
  const can = !!(p.id && cloud && typeof cloud.setOrgVerified === "function");
  const on = !!state.verifiedAt;
  const toggle = async () => {
    if (!can || busy) return;
    setBusy(true);
    try {
      const r = await cloud.setOrgVerified(p.id, !on);
      const next = { verifiedAt: (r && r.verifiedAt) || null, verifiedBy: (r && r.verifiedBy) || null };
      setState(next);
      onChanged && onChanged(next);
      onToast && onToast(next.verifiedAt ? "確認済みにしました" : "確認済みを外しました");
    } catch (e) {
      // 失敗は赤帯(㉝・atoms.jsx の showSaveFail)。理由はサーバの文言をそのまま出し、やり直せるようにする。
      const reason = (e && e.message) || String(e);
      if (typeof window.showSaveFail === "function") window.showSaveFail({ title: "確認済みを保存できませんでした", reason, onRetry: toggle });
      else onToast && onToast("確認済みを保存できませんでした: " + reason);
    } finally {
      setBusy(false);
    }
  };
  return (
    <span style={{ display: "inline-flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
      <span style={{ fontWeight: 600, color: on ? "var(--success)" : "var(--muted-foreground)" }}>{on ? "✓ 確認済み" : "未確認"}</span>
      {on && <span className="mono" style={{ fontSize: "var(--text-body-sm)", color: "var(--muted-foreground)" }}>
        {orgYmd(state.verifiedAt)}{state.verifiedBy && state.verifiedBy.name ? "・" + state.verifiedBy.name : ""}</span>}
      {can && <AfButton size="sm" disabled={busy} onClick={toggle}>{busy ? "保存中…" : (on ? "確認済みを外す" : "確認済みにする")}</AfButton>}
    </span>
  );
}

Object.assign(window, { orgOwnerName, orgOwnerCandidates, OrgOwnerSelect,
  ORG_CONTRACT_STATES, orgYmd, orgContractLabel, OrgContractChip, OrgContractFields, OrgVerifiedMark, OrgVerifyControl });
