import { useEffect, useState, useCallback } from "react";
import {
  writeText as clipboardWriteText,
} from "@tauri-apps/plugin-clipboard-manager";
import type { McpStatus, McpAuditEntry } from "../ipc";
import AuditTab from "./AuditTab";
import PolicyTab from "./PolicyTab";
import "./McpPanel.css";

interface McpPanelProps {
  status: McpStatus;
  onStart: () => Promise<void>;
  onStop: () => Promise<void>;
  onRegenerateToken: () => Promise<void>;
  onClose: () => void;
  /** Count of leaves with mcpAllow=true — shown so the user knows whether
   *  enabling the server will actually expose anything. */
  allowedPaneCount: number;
  /** Total pane count for context. */
  totalPaneCount: number;
  /** Persistent audit log, owned by App so it survives panel close. */
  auditEntries: McpAuditEntry[];
  onClearAudit: () => void;
}

type TabId = "config" | "audit" | "policy";

export default function McpPanel({
  status,
  onStart,
  onStop,
  onRegenerateToken,
  onClose,
  allowedPaneCount,
  totalPaneCount,
  auditEntries,
  onClearAudit,
}: McpPanelProps) {
  const [busy, setBusy] = useState(false);
  const [revealToken, setRevealToken] = useState(false);
  const [regenBusy, setRegenBusy] = useState(false);
  const [tab, setTab] = useState<TabId>("config");
  // Unread badge on Audit tab: count of entries arrived since the user last
  // visited Audit. Tracked via a baseline count, reset on switch-to-audit.
  const [auditSeenCount, setAuditSeenCount] = useState(auditEntries.length);
  const auditUnread = auditEntries.length > auditSeenCount;

  useEffect(() => {
    function onKey(e: KeyboardEvent) {
      if (e.key === "Escape") {
        e.preventDefault();
        onClose();
      }
    }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  const toggle = useCallback(async () => {
    if (busy) return;
    setBusy(true);
    try {
      if (status.running) await onStop();
      else await onStart();
    } finally {
      setBusy(false);
    }
  }, [busy, status.running, onStart, onStop]);

  const copy = useCallback((s: string) => {
    void clipboardWriteText(s).catch((e) =>
      console.warn("clipboard write failed:", e),
    );
  }, []);

  const regenerate = useCallback(async () => {
    if (regenBusy) return;
    const warn = status.running
      ? "Regenerate token? Existing MCP clients will be disconnected and need the new token to reconnect."
      : "Regenerate token? Any saved client config with the old token will stop working.";
    if (!window.confirm(warn)) return;
    setRegenBusy(true);
    try {
      await onRegenerateToken();
    } finally {
      setRegenBusy(false);
    }
  }, [regenBusy, status.running, onRegenerateToken]);

  function switchTab(id: TabId) {
    setTab(id);
    if (id === "audit") setAuditSeenCount(auditEntries.length);
  }

  return (
    <>
      <button className="backdrop" onClick={onClose} aria-label="Close" />
      <div className="mcp-panel" role="dialog" aria-label="MCP server">
        <header className="mcp-header">
          <span className="mcp-title">MCP server</span>
          <button className="mcp-close" onClick={onClose} aria-label="Close">×</button>
        </header>

        {/* Tab bar */}
        <div className="mcp-tabs" role="tablist">
          <button
            className={`mcp-tab${tab === "config" ? " mcp-tab--active" : ""}`}
            role="tab"
            aria-selected={tab === "config"}
            onClick={() => switchTab("config")}
          >
            Config
          </button>
          <button
            className={`mcp-tab${tab === "audit" ? " mcp-tab--active" : ""}`}
            role="tab"
            aria-selected={tab === "audit"}
            onClick={() => switchTab("audit")}
          >
            Audit
            {auditUnread && <span className="mcp-tab-badge" aria-label="new entries" />}
          </button>
          <button
            className={`mcp-tab${tab === "policy" ? " mcp-tab--active" : ""}`}
            role="tab"
            aria-selected={tab === "policy"}
            onClick={() => switchTab("policy")}
          >
            Policy
          </button>
        </div>

        <div className="mcp-body">
          {tab === "config" && (
            <>
              <p className="mcp-blurb">
                Lets a Claude session on the same machine inspect this workspace
                via Model Context Protocol — see which panes are running, read
                their scrollback, wait for commands to settle. Read-only in v1;
                Claude can't send keystrokes or reshape the layout yet.
              </p>

              <div className="mcp-toggle-row">
                <button
                  className={`mcp-toggle${status.running ? " on" : ""}`}
                  onClick={() => { void toggle(); }}
                  disabled={busy}
                >
                  <span className="mcp-dot" />
                  {status.running ? "Server: ON" : "Server: OFF"}
                </button>
                <span className="mcp-allow-count">
                  {allowedPaneCount} of {totalPaneCount} pane
                  {totalPaneCount === 1 ? "" : "s"} allow-listed
                  {allowedPaneCount === 0 && status.running && (
                    <span className="mcp-allow-warn">
                      {" "}
                      — Claude will see nothing until you toggle 🤖 on at least
                      one pane.
                    </span>
                  )}
                </span>
              </div>

              {status.running && status.url && status.token && (
                <>
                  <div className="mcp-field">
                    <label>URL</label>
                    <div className="mcp-field-row">
                      <input readOnly value={status.url} onFocus={(e) => e.currentTarget.select()} />
                      <button onClick={() => copy(status.url!)}>Copy</button>
                    </div>
                  </div>
                  <div className="mcp-field">
                    <label>Bearer token</label>
                    <div className="mcp-field-row">
                      <input
                        readOnly
                        type={revealToken ? "text" : "password"}
                        value={status.token}
                        onFocus={(e) => e.currentTarget.select()}
                      />
                      <button onClick={() => setRevealToken((r) => !r)}>
                        {revealToken ? "Hide" : "Show"}
                      </button>
                      <button onClick={() => copy(status.token!)}>Copy</button>
                      <button onClick={() => { void regenerate(); }} disabled={regenBusy}>
                        {regenBusy ? "…" : "Regenerate"}
                      </button>
                    </div>
                    <p className="mcp-hint">
                      URL + token persist across restarts — paste the snippet
                      into your Claude config once. Regenerate if the token
                      leaks.
                    </p>
                  </div>

                  <div className="mcp-field">
                    <label>Claude Code config snippet (.mcp.json)</label>
                    <pre className="mcp-snippet">
{`{
  "mcpServers": {
    "tiletopia": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "${status.url}",
        "--allow-http",
        "--header", "Authorization: Bearer ${status.token}"
      ]
    }
  }
}`}
                    </pre>
                    <button
                      onClick={() =>
                        copy(
                          JSON.stringify(
                            {
                              mcpServers: {
                                tiletopia: {
                                  command: "npx",
                                  args: [
                                    "-y",
                                    "mcp-remote",
                                    status.url,
                                    "--allow-http",
                                    "--header",
                                    `Authorization: Bearer ${status.token}`,
                                  ],
                                },
                              },
                            },
                            null,
                            2,
                          ),
                        )
                      }
                    >
                      Copy config snippet
                    </button>
                  </div>

                  <div className="mcp-tips">
                    <strong>Why the shim?</strong> Claude Code's HTTP-MCP
                    client tries OAuth discovery and ignores static{" "}
                    <code>headers</code> auth (Anthropic issues #17152, #46879).
                    The <code>mcp-remote</code> stdio shim transparently
                    proxies the HTTP endpoint with the bearer header attached,
                    which sidesteps the OAuth flow entirely. Other MCP
                    clients that handle bearer auth correctly can connect
                    directly to the URL above with the token in an{" "}
                    <code>Authorization</code> header.
                    <br />
                    <br />
                    <strong>WSL connectivity:</strong> the URL uses{" "}
                    <code>127.0.0.1</code>; a Claude session running inside
                    WSL needs to either swap that for the WSL gateway IP
                    (<code>ip route show default | awk '{`{print $3}`}'</code>{" "}
                    inside WSL — changes after each WSL restart), or enable
                    mirrored networking (<code>networkingMode=mirrored</code>{" "}
                    in <code>%UserProfile%\.wslconfig</code>, Win11 22H2+)
                    so <code>127.0.0.1</code> in WSL routes to this host.
                    You'll likely also need to allow the port through Windows
                    Defender Firewall:{" "}
                    <code>
                      New-NetFirewallRule -DisplayName 'tiletopia MCP'
                      -Direction Inbound -Action Allow -Protocol TCP
                      -LocalPort {status.url.match(/:(\d+)\//)?.[1] ?? "47821"}{" "}
                      -Profile Any
                    </code>{" "}
                    (elevated PowerShell).
                  </div>
                </>
              )}

              {!status.running && (
                <p className="mcp-off-hint">
                  Server is off — no port is open. Token is generated when you
                  start. Each pane needs the 🤖 chip toggled on for Claude to
                  see it.
                </p>
              )}

              <p className="mcp-security">
                <strong>Security:</strong> bound to <code>0.0.0.0</code> so WSL
                distros and other machines on your LAN can reach it; bearer
                token is the only thing keeping them out. Treat MCP access as
                equivalent to terminal access — don't share the token, don't
                run the server on an untrusted network. Saved SSH passwords are{" "}
                <em>never</em> exposed through MCP.
              </p>
            </>
          )}

          {tab === "audit" && (
            <AuditTab entries={auditEntries} onClear={onClearAudit} />
          )}

          {tab === "policy" && <PolicyTab />}
        </div>
      </div>
    </>
  );
}
