import {
  useState,
  useCallback,
  useEffect,
  useRef,
  type FormEvent,
} from "react";
import type { SshHost } from "../ipc";
import "./HostManager.css";

function newId(): string {
  return (
    globalThis.crypto?.randomUUID?.() ??
    Math.random().toString(36).slice(2, 12)
  );
}

function blankHost(): SshHost {
  return { id: newId(), label: "", hostname: "" };
}

/** Per-edit transient state for the password field. The actual password
 *  text never lives on `SshHost` — it stays in this map until the user
 *  clicks Save, at which point we either send a set/delete to keyring
 *  via the parent callbacks or do nothing. */
interface PasswordDraft {
  /** What the user typed (or "" if untouched). */
  input: string;
  /** True iff the user clicked "Remove password" — overrides `input`. */
  cleared: boolean;
}

interface HostManagerProps {
  hosts: SshHost[];
  /** Persist the host list (label/hostname/etc — no password). */
  onSave: (hosts: SshHost[]) => void;
  /** Write a new password to keyring for the given host id. Called only
   *  on Save, only when the user typed something into the password field. */
  onSavePassword: (hostId: string, password: string) => void;
  /** Delete the keyring entry for this host id. Called when the user
   *  clicked "Remove password" before Save. */
  onClearPassword: (hostId: string) => void;
  onClose: () => void;
}

export default function HostManager({
  hosts,
  onSave,
  onSavePassword,
  onClearPassword,
  onClose,
}: HostManagerProps) {
  // Local editable copy. Any save / delete acts on this and pushes the
  // whole list back up via onSave.
  const [draft, setDraft] = useState<SshHost[]>(() => hosts.map((h) => ({ ...h })));
  // Per-row password edits (keyed by host id). Absent = unchanged.
  const [pwDrafts, setPwDrafts] = useState<Record<string, PasswordDraft>>({});
  // Which row is being edited. null = list view only.
  const [editingId, setEditingId] = useState<string | null>(null);
  const dialogRef = useRef<HTMLDivElement>(null);

  // Escape closes; click outside the panel closes.
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") onClose();
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  const startEdit = useCallback((id: string) => {
    setEditingId(id);
    setPwDrafts((cur) => {
      if (cur[id]) return cur;
      return { ...cur, [id]: { input: "", cleared: false } };
    });
  }, []);

  const cancelEdit = useCallback(() => {
    // Revert any unsaved edits to that row from props; drop password drafts.
    setDraft((cur) =>
      cur
        .map((h) => {
          if (h.id !== editingId) return h;
          const original = hosts.find((o) => o.id === editingId);
          return original ?? h;
        })
        .filter((h) => {
          if (h.id !== editingId) return true;
          return hosts.some((o) => o.id === editingId);
        }),
    );
    if (editingId) {
      setPwDrafts((cur) => {
        if (!(editingId in cur)) return cur;
        const next = { ...cur };
        delete next[editingId];
        return next;
      });
    }
    setEditingId(null);
  }, [editingId, hosts]);

  const onFieldChange = useCallback(
    (id: string, field: keyof SshHost, value: string) => {
      setDraft((cur) =>
        cur.map((h) => {
          if (h.id !== id) return h;
          if (field === "port") {
            if (value.trim() === "") return { ...h, port: undefined };
            const n = Number(value);
            if (!Number.isFinite(n) || n < 1 || n > 65535) return h;
            return { ...h, port: n };
          }
          if (field === "extraArgs") {
            const parts = value
              .split(/\s+/)
              .map((s) => s.trim())
              .filter((s) => s.length > 0);
            return { ...h, extraArgs: parts.length > 0 ? parts : undefined };
          }
          if (value.trim() === "" && field !== "label" && field !== "hostname") {
            const next = { ...h };
            delete next[field];
            return next;
          }
          return { ...h, [field]: value };
        }),
      );
    },
    [],
  );

  const onPasswordInput = useCallback((id: string, value: string) => {
    setPwDrafts((cur) => ({
      ...cur,
      [id]: { input: value, cleared: false },
    }));
  }, []);

  const onPasswordClear = useCallback((id: string) => {
    setPwDrafts((cur) => ({
      ...cur,
      [id]: { input: "", cleared: true },
    }));
  }, []);

  const saveRow = useCallback(
    (id: string, e: FormEvent) => {
      e.preventDefault();
      const row = draft.find((h) => h.id === id);
      if (!row) return;
      if (!row.hostname.trim()) {
        // Hostname is the only truly required field. Refuse the save instead
        // of silently persisting a useless entry.
        return;
      }
      // Auto-fill label from hostname if the user left it blank.
      const cleaned: SshHost = {
        ...row,
        label: row.label.trim() || row.hostname.trim(),
        hostname: row.hostname.trim(),
      };

      // Apply the password edit — if any — BEFORE flipping `hasPassword`
      // on the local copy so the row redraws with the right state.
      const pw = pwDrafts[id];
      let nextHasPassword = row.hasPassword;
      if (pw) {
        if (pw.cleared) {
          onClearPassword(id);
          nextHasPassword = false;
        } else if (pw.input.length > 0) {
          onSavePassword(id, pw.input);
          nextHasPassword = true;
        }
      }
      cleaned.hasPassword = nextHasPassword;

      const next = draft.map((h) => (h.id === id ? cleaned : h));
      setDraft(next);
      onSave(next.map(({ hasPassword: _hp, ...rest }) => rest));
      // Drop the pw draft so re-edit doesn't carry it over.
      setPwDrafts((cur) => {
        if (!(id in cur)) return cur;
        const nxt = { ...cur };
        delete nxt[id];
        return nxt;
      });
      setEditingId(null);
    },
    [draft, pwDrafts, onSave, onSavePassword, onClearPassword],
  );

  const removeRow = useCallback(
    (id: string) => {
      const next = draft.filter((h) => h.id !== id);
      setDraft(next);
      // Strip hasPassword on persist — the backend recomputes it. (The
      // save command sweeps orphan credentials, so the deleted host's
      // password is also removed from keyring.)
      onSave(next.map(({ hasPassword: _hp, ...rest }) => rest));
      if (editingId === id) setEditingId(null);
      setPwDrafts((cur) => {
        if (!(id in cur)) return cur;
        const nxt = { ...cur };
        delete nxt[id];
        return nxt;
      });
    },
    [draft, editingId, onSave],
  );

  const addRow = useCallback(() => {
    const fresh = blankHost();
    setDraft((cur) => [...cur, fresh]);
    setEditingId(fresh.id);
    setPwDrafts((cur) => ({
      ...cur,
      [fresh.id]: { input: "", cleared: false },
    }));
  }, []);

  return (
    <div className="host-mgr-overlay" onClick={onClose}>
      <div
        className="host-mgr-panel"
        ref={dialogRef}
        onClick={(e) => e.stopPropagation()}
        role="dialog"
        aria-modal="true"
        aria-label="Manage SSH hosts"
      >
        <header className="host-mgr-header">
          <span className="host-mgr-title">SSH hosts</span>
          <button className="host-mgr-close" onClick={onClose} aria-label="Close">
            ×
          </button>
        </header>

        <div className="host-mgr-body">
          {draft.length === 0 ? (
            <p className="host-mgr-empty">
              No saved hosts. Click <strong>Add host</strong> to create one.
            </p>
          ) : (
            <ul className="host-mgr-list">
              {draft.map((h) => (
                <li key={h.id} className="host-row">
                  {editingId === h.id ? (
                    <form className="host-form" onSubmit={(e) => saveRow(h.id, e)}>
                      <label>
                        Label
                        <input
                          type="text"
                          value={h.label}
                          onChange={(e) =>
                            onFieldChange(h.id, "label", e.target.value)
                          }
                          placeholder="prod-web"
                          autoFocus
                        />
                      </label>
                      <label>
                        Hostname <span className="required">*</span>
                        <input
                          type="text"
                          required
                          value={h.hostname}
                          onChange={(e) =>
                            onFieldChange(h.id, "hostname", e.target.value)
                          }
                          placeholder="example.com or 10.0.0.5"
                        />
                      </label>
                      <div className="host-form-row">
                        <label>
                          User
                          <input
                            type="text"
                            value={h.user ?? ""}
                            onChange={(e) =>
                              onFieldChange(h.id, "user", e.target.value)
                            }
                            placeholder="(default)"
                          />
                        </label>
                        <label className="host-form-port">
                          Port
                          <input
                            type="number"
                            min={1}
                            max={65535}
                            value={h.port ?? ""}
                            onChange={(e) =>
                              onFieldChange(h.id, "port", e.target.value)
                            }
                            placeholder="22"
                          />
                        </label>
                      </div>
                      <label>
                        Identity file
                        <input
                          type="text"
                          value={h.identityFile ?? ""}
                          onChange={(e) =>
                            onFieldChange(h.id, "identityFile", e.target.value)
                          }
                          placeholder="(uses ssh-agent / default)"
                        />
                      </label>
                      <label>
                        Jump host
                        <input
                          type="text"
                          value={h.jumpHost ?? ""}
                          onChange={(e) =>
                            onFieldChange(h.id, "jumpHost", e.target.value)
                          }
                          placeholder="user@bastion[:port]"
                        />
                      </label>
                      <label>
                        Extra ssh args
                        <input
                          type="text"
                          value={(h.extraArgs ?? []).join(" ")}
                          onChange={(e) =>
                            onFieldChange(h.id, "extraArgs", e.target.value)
                          }
                          placeholder="-o ServerAliveInterval=30"
                        />
                      </label>

                      <PasswordField
                        hostHasPassword={!!h.hasPassword}
                        draft={pwDrafts[h.id]}
                        onChange={(v) => onPasswordInput(h.id, v)}
                        onClear={() => onPasswordClear(h.id)}
                      />

                      <div className="host-form-actions">
                        <button type="submit" className="primary">
                          Save
                        </button>
                        <button type="button" onClick={cancelEdit}>
                          Cancel
                        </button>
                        <button
                          type="button"
                          className="danger"
                          onClick={() => removeRow(h.id)}
                        >
                          Delete
                        </button>
                      </div>
                    </form>
                  ) : (
                    <div className="host-display">
                      <div className="host-summary">
                        <div className="host-summary-label">
                          {h.label || h.hostname}
                          {h.hasPassword && (
                            <span
                              className="host-pw-badge"
                              title="Password stored in Windows Credential Manager"
                            >
                              🔒
                            </span>
                          )}
                        </div>
                        <div className="host-summary-detail">
                          {h.user ? `${h.user}@` : ""}
                          {h.hostname}
                          {h.port ? `:${h.port}` : ""}
                          {h.jumpHost ? ` via ${h.jumpHost}` : ""}
                        </div>
                      </div>
                      <button
                        className="host-edit-btn"
                        onClick={() => startEdit(h.id)}
                      >
                        Edit
                      </button>
                    </div>
                  )}
                </li>
              ))}
            </ul>
          )}

          <button className="host-add-btn" onClick={addRow}>
            + Add host
          </button>
        </div>
      </div>
    </div>
  );
}

function PasswordField({
  hostHasPassword,
  draft,
  onChange,
  onClear,
}: {
  hostHasPassword: boolean;
  draft: PasswordDraft | undefined;
  onChange: (value: string) => void;
  onClear: () => void;
}) {
  const [reveal, setReveal] = useState(false);
  const cleared = draft?.cleared ?? false;
  const showClearButton = hostHasPassword && !cleared;

  const placeholder = cleared
    ? "(password will be removed on save)"
    : hostHasPassword
      ? "(saved — leave blank to keep, or type new)"
      : "password (optional)";

  return (
    <label>
      <span className="host-form-pw-label">
        Password
        <span
          className="host-form-pw-hint"
          title="Stored in Windows Credential Manager; auto-typed at the ssh password prompt on connect."
        >
          stored encrypted; auto-typed at prompt
        </span>
      </span>
      <div className="host-form-pw-row">
        <input
          type={reveal ? "text" : "password"}
          value={draft?.input ?? ""}
          onChange={(e) => onChange(e.target.value)}
          placeholder={placeholder}
          autoComplete="off"
        />
        <button
          type="button"
          className="host-form-pw-reveal"
          onClick={() => setReveal((r) => !r)}
          title={reveal ? "Hide" : "Show"}
        >
          {reveal ? "🙈" : "👁"}
        </button>
        {showClearButton && (
          <button
            type="button"
            className="host-form-pw-clear"
            onClick={onClear}
            title="Remove the saved password from keyring on next Save"
          >
            Remove
          </button>
        )}
      </div>
    </label>
  );
}
