// ---------------------------------------------------------------------------
// connectors/OdooConfig.tsx — WAVE 32 items 6/11, contract C2, rulings R9/R10/R11.
//
// ⛔ WHAT THE OWNER FOUND WHEN THEY CLICKED "Manage keys" ON ODOO: a list of
// credentials. Not which server database this workspace reads, not which of the
// ten mirrored grids it wants, not how often they sync, and no way to stop. This
// panel is those four questions, on the card the question is asked from.
//
// ⛔⛔ THE PANEL IS A PURE COMPONENT AND THE FETCHING LIVES ABOVE IT, which is not
// a style preference — it is what makes the ticket's `done-when` provable. The
// page fetches on mount and `renderToStaticMarkup` does not run effects, so a
// component that loads its own data is a spinner forever in a shot, and a ticket
// whose done-when is "an admin SEES the server database and a checklist" cannot
// be evidenced by a picture of a spinner. `ConnectorCard` owns the request;
// `OdooConfigPanel` renders whatever it is handed. Same reason `ConnectorCard`
// itself was lifted out of the page in wave 30.
//
// ⚠ NO CLIENT UNION OVER `syncEvery` OR THE GRID KEYS. Both are the server's
// vocabulary (`odoo_relational.SYNC_PRESETS`, `odoo_relational.TABLES`) and
// arrive as strings — a client union turns "the server added a preset" into "the
// client drops the option" (the wave-9 law).
// ---------------------------------------------------------------------------

/** One materialisable grid, exactly as `GET /admin/connectors/odoo/config` sends it. */
export interface OdooGrid {
  key: string;
  label: string;
  enabled: boolean;
}

/** The config payload. Everything optional but `grids` is genuinely optional on the wire —
 *  an older server, or a tenant with no Odoo at all, simply says less. */
export interface OdooConfig {
  applicable?: boolean;
  /** `keychain` | `env` | `none` — WHERE the credential lives, read as a string. */
  source?: string;
  label?: string;
  /** The masked hint the keychain computed at write. Never the secret. */
  preview?: string;
  serverDb?: string;
  serverUrl?: string;
  apiUser?: string;
  /** False for the deployment ENVIRONMENT: editing it here would be editing the container. */
  serverDbEditable?: boolean;
  grids?: OdooGrid[];
  syncEvery?: string;
  syncOptions?: string[];
  syncFloorSeconds?: number;
  frozen?: boolean;
  frozenAt?: string;
  canDisconnect?: boolean;
  /** What the server did with what it was sent — a clamp, a refusal, a rewrite (W30/R6). */
  notes?: string[];
}

/**
 * The cadence words, in the two places a person reads them. ⚠ The MAP is presentation; the
 * OPTIONS come from the server (`syncOptions`), so a preset we have no phrase for still renders
 * — as its own key rather than as nothing.
 */
const EVERY_LABEL: Record<string, string> = {
  "30m": "Every 30 minutes",
  "1h": "Every hour",
  "4h": "Every 4 hours",
  daily: "Once a day",
  manual: "Only when I ask",
};
export const everyLabel = (k: string): string => EVERY_LABEL[k] || k;

/** What the connection is, in one line, for the header of the panel. */
export function sourceLine(cfg: OdooConfig): string {
  if (cfg.frozen) return "Disconnected — these databases are frozen as static data";
  if (cfg.source === "env") return "Connected using this deployment's own credentials";
  if (cfg.source === "keychain") return "Connected using a key stored in this workspace";
  return "Not connected";
}

export default function OdooConfigPanel({
  cfg,
  busy,
  onToggleGrid,
  onSyncEvery,
  onServerDb,
  onDisconnect,
  onReconnect,
}: {
  cfg: OdooConfig;
  busy?: boolean;
  onToggleGrid: (key: string, enabled: boolean) => void;
  onSyncEvery: (every: string) => void;
  onServerDb: (db: string) => void;
  onDisconnect: () => void;
  onReconnect: () => void;
}) {
  const grids = cfg.grids ?? [];
  const options = cfg.syncOptions ?? [];
  const on = grids.filter((g) => g.enabled).length;
  return (
    <div className="conn-odoo">
      <p className="conn-card-meta">{sourceLine(cfg)}</p>

      {/* ── the credential, as much of it as may be shown ────────────────────────────────── */}
      <div className="conn-odoo-row">
        <span className="conn-odoo-k">Key</span>
        <span className="conn-odoo-v">
          {cfg.preview ? cfg.preview : "held on this deployment"}
          {cfg.apiUser ? " · " + cfg.apiUser : ""}
        </span>
      </div>
      {cfg.serverUrl ? (
        <div className="conn-odoo-row">
          <span className="conn-odoo-k">Server</span>
          <span className="conn-odoo-v">{cfg.serverUrl}</span>
        </div>
      ) : null}
      <div className="conn-odoo-row">
        <span className="conn-odoo-k">Database</span>
        {cfg.serverDbEditable ? (
          // ⚠ A `defaultValue` + blur, not a controlled input: this component is PURE and holds
          // no state, and a controlled field with no owner is a box you cannot type in.
          <input
            className="conn-odoo-v"
            defaultValue={cfg.serverDb ?? ""}
            maxLength={80}
            disabled={busy || cfg.frozen}
            aria-label="Odoo server database"
            onBlur={(e) => {
              const next = e.target.value.trim();
              if (next && next !== (cfg.serverDb ?? "")) onServerDb(next);
            }}
          />
        ) : (
          <span className="conn-odoo-v">
            {cfg.serverDb || "—"}
            {/* R6's second sentence: a thing you cannot change says WHY, not nothing. */}
            <em> — set on this deployment, not in this workspace</em>
          </span>
        )}
      </div>

      {/* ── R9's second reading: WHICH grids this workspace materialises ─────────────────── */}
      <p className="conn-odoo-h">
        Databases to build <span className="conn-odoo-count">{on} of {grids.length}</span>
      </p>
      <div className="conn-odoo-grids">
        {grids.map((g) => (
          <label className="conn-odoo-grid" key={g.key}>
            <input
              type="checkbox"
              checked={g.enabled}
              disabled={busy || cfg.frozen}
              onChange={(e) => onToggleGrid(g.key, e.target.checked)}
            />
            <span>{g.label}</span>
          </label>
        ))}
      </div>
      {/* ⛔ SAID BEFORE IT IS DONE, not after. Unticking stops the NEXT sync from rebuilding a
          database; it does not delete the one you already have. A checkbox that silently threw
          away rows would be the worst control on this page. */}
      <p className="conn-card-meta">
        Unticking one stops it being rebuilt on the next sync. Nothing you already have is
        deleted.
      </p>

      {/* ── R11: the cadence, from the server's own preset list, floor included ──────────── */}
      <p className="conn-odoo-h">How often to sync</p>
      <select
        className="conn-odoo-every"
        value={cfg.syncEvery ?? ""}
        disabled={busy || cfg.frozen}
        aria-label="How often to sync"
        onChange={(e) => onSyncEvery(e.target.value)}
      >
        {options.map((o) => (
          <option value={o} key={o}>
            {everyLabel(o)}
          </option>
        ))}
      </select>

      {/* ── R10: off, and back on ────────────────────────────────────────────────────────── */}
      {cfg.frozen ? (
        <div className="conn-odoo-off">
          <p className="conn-card-meta">
            Disconnected{cfg.frozenAt ? " on " + cfg.frozenAt.slice(0, 10) : ""}. Every row and
            every column you had is still here and still readable — nothing is being refreshed.
          </p>
          <button type="button" className="conn-card-go" disabled={busy} onClick={onReconnect}>
            Reconnect
          </button>
        </div>
      ) : cfg.canDisconnect ? (
        <button type="button" className="conn-odoo-danger" disabled={busy} onClick={onDisconnect}>
          Disconnect Odoo
        </button>
      ) : null}

      {/* Whatever the server did with what it was sent — a clamp, a rewrite, a warning. */}
      {(cfg.notes ?? []).length ? (
        <ul className="conn-odoo-notes">
          {(cfg.notes ?? []).map((n) => (
            <li key={n}>{n}</li>
          ))}
        </ul>
      ) : null}
    </div>
  );
}
