import { useEffect, useState, useRef } from "react";
import {
  mcpHardDenyLabels,
  mcpPolicyLoad,
  mcpPolicySave,
  type McpPolicy,
} from "../ipc";

type Bucket = "deny" | "ask" | "allow";

const BUCKET_LABELS: Record<Bucket, string> = {
  deny: "Deny: blocked outright",
  ask: "Ask: confirm in a modal",
  allow: "Silently run",
};

interface RuleListProps {
  bucket: Bucket;
  rules: string[];
  onRemove: (bucket: Bucket, index: number) => void;
  onAdd: (bucket: Bucket, rule: string) => void;
}

function RuleList({ bucket, rules, onRemove, onAdd }: RuleListProps) {
  const [draft, setDraft] = useState("");
  const inputRef = useRef<HTMLInputElement>(null);

  function handleAdd() {
    const trimmed = draft.trim();
    if (!trimmed) return;
    onAdd(bucket, trimmed);
    setDraft("");
    inputRef.current?.focus();
  }

  function handleKeyDown(e: React.KeyboardEvent) {
    if (e.key === "Enter") handleAdd();
  }

  return (
    <div className={`policy-bucket policy-bucket--${bucket}`}>
      <div className="policy-bucket-header">{BUCKET_LABELS[bucket]}</div>
      <ul className="policy-rule-list">
        {rules.length === 0 && (
          <li className="policy-rule-empty">—</li>
        )}
        {rules.map((r, i) => (
          <li key={i} className="policy-rule">
            <code className="policy-rule-text">{r}</code>
            <button
              className="policy-rule-remove"
              onClick={() => onRemove(bucket, i)}
              aria-label={`Remove rule ${r}`}
            >
              ×
            </button>
          </li>
        ))}
      </ul>
      <div className="policy-add-row">
        <input
          ref={inputRef}
          className="policy-add-input"
          value={draft}
          onChange={(e) => setDraft(e.target.value)}
          onKeyDown={handleKeyDown}
          placeholder="e.g. write_pane(git push *)"
          aria-label={`Add ${bucket} rule`}
        />
        <button
          className="policy-add-btn"
          onClick={handleAdd}
          disabled={!draft.trim()}
        >
          Add
        </button>
      </div>
    </div>
  );
}

export default function PolicyTab() {
  const [policy, setPolicy] = useState<McpPolicy | null>(null);
  const [hardDenyLabels, setHardDenyLabels] = useState<string[]>([]);
  const [dirty, setDirty] = useState(false);
  const [saving, setSaving] = useState(false);
  const [saveError, setSaveError] = useState<string | null>(null);

  useEffect(() => {
    void mcpPolicyLoad().then(setPolicy);
    void mcpHardDenyLabels().then(setHardDenyLabels);
  }, []);

  function mutate(updater: (p: McpPolicy) => McpPolicy) {
    setPolicy((prev) => {
      if (!prev) return prev;
      const next = updater(prev);
      setDirty(true);
      return next;
    });
  }

  function handleRemove(bucket: Bucket, index: number) {
    mutate((p) => ({
      ...p,
      permissions: {
        ...p.permissions,
        [bucket]: p.permissions[bucket].filter((_, i) => i !== index),
      },
    }));
  }

  function handleAdd(bucket: Bucket, rule: string) {
    mutate((p) => ({
      ...p,
      permissions: {
        ...p.permissions,
        [bucket]: [...p.permissions[bucket], rule],
      },
    }));
  }

  function setSshSafeguard(
    key: "allowOpenSsh" | "autoAllowSpawnedSsh" | "allowAddHost",
    value: boolean,
  ) {
    mutate((p) => ({
      ...p,
      sshSafeguards: { ...p.sshSafeguards, [key]: value },
    }));
  }

  async function handleSave() {
    if (!policy || !dirty || saving) return;
    setSaving(true);
    setSaveError(null);
    try {
      await mcpPolicySave(policy);
      setDirty(false);
    } catch (e) {
      setSaveError(String(e));
    } finally {
      setSaving(false);
    }
  }

  if (!policy) {
    return <p className="policy-loading">Loading policy…</p>;
  }

  return (
    <div className="policy-tab">
      <div className="policy-toolbar">
        <p className="policy-hint">
          Empty policy = every MCP tool call asks for confirmation. Add rules
          to bypass the prompt for patterns you trust, or to block patterns
          outright.
        </p>
        <div className="policy-save-area">
          {saveError && (
            <span className="policy-save-error">{saveError}</span>
          )}
          <button
            className="policy-save-btn"
            onClick={() => { void handleSave(); }}
            disabled={!dirty || saving}
          >
            {saving ? "Saving…" : "Save"}
          </button>
        </div>
      </div>

      <div className="policy-ssh-safeguards">
        <div className="policy-bucket-header">SSH safeguards</div>
        <label className="policy-toggle-row">
          <input
            type="checkbox"
            checked={policy.sshSafeguards.allowOpenSsh}
            onChange={(e) => setSshSafeguard("allowOpenSsh", e.target.checked)}
          />
          <div className="policy-toggle-text">
            <strong>Allow Claude to open SSH connections.</strong> When off,
            the <code>connect_host</code> and <code>spawn_pane(kind=ssh)</code>
            {" "}tools refuse with a clear error. You can still open SSH
            sessions manually via the titlebar 🔑 picker, and Claude can
            interact with them if you toggle 🤖 on.
          </div>
        </label>
        <label className="policy-toggle-row">
          <input
            type="checkbox"
            checked={policy.sshSafeguards.autoAllowSpawnedSsh}
            onChange={(e) =>
              setSshSafeguard("autoAllowSpawnedSsh", e.target.checked)
            }
            disabled={!policy.sshSafeguards.allowOpenSsh}
          />
          <div className="policy-toggle-text">
            <strong>Auto-grant Claude access to newly-spawned SSH panes.</strong>{" "}
            When off, an SSH pane Claude opens starts with 🤖 off — you have
            to explicitly toggle it before Claude can read scrollback or send
            keystrokes. Only meaningful when the switch above is on.
          </div>
        </label>
        <label className="policy-toggle-row">
          <input
            type="checkbox"
            checked={policy.sshSafeguards.allowAddHost}
            onChange={(e) =>
              setSshSafeguard("allowAddHost", e.target.checked)
            }
          />
          <div className="policy-toggle-text">
            <strong>Allow Claude to save or delete SSH hosts.</strong> When
            off, the <code>add_host</code> and <code>delete_host</code> tools
            refuse with a clear error — only you manage the saved-hosts list
            via the titlebar 🔑 picker. Extra ssh args (<code>-o ...</code>)
            on saved hosts are still sanitised to reject command-execution
            primitives (<code>ProxyCommand</code>, <code>LocalCommand</code>,
            etc.) regardless of this switch.
          </div>
        </label>
      </div>

      <div className="policy-buckets">
        {(["deny", "ask", "allow"] as Bucket[]).map((bucket) => (
          <RuleList
            key={bucket}
            bucket={bucket}
            rules={policy.permissions[bucket]}
            onRemove={handleRemove}
            onAdd={handleAdd}
          />
        ))}
      </div>

      <div className="policy-hard-deny">
        <div className="policy-hard-deny-header">Always blocked (built-in)</div>
        <ul className="policy-hard-deny-list">
          {hardDenyLabels.map((label) => (
            <li key={label} className="policy-hard-deny-rule">
              <code>{label}</code>
              <span className="policy-hard-deny-badge">Cannot be disabled</span>
            </li>
          ))}
        </ul>
        <p className="policy-hard-deny-footnote">
          These patterns are caught regardless of policy. Best-effort accident
          prevention, not a sandbox — see README.
        </p>
      </div>
    </div>
  );
}
