import { useEffect, useState } from "react";
import { apiGet, apiPost, AppConfig } from "../api";
import { Loading, PageHead } from "../components/ui";
import { IconBook, IconCheck, IconEmby, IconFolder, IconMusic, IconRefresh, IconSettings } from "../components/icons";
import { useToast } from "../lib/toast";

interface SettingsValues {
  emby_url: string;
  emby_api_key: string;
  homescreen_db_path: string;
  tmdb_api_key: string;
  navidrome_url: string;
  navidrome_user: string;
  navidrome_password: string;
  audiobookshelf_url: string;
  audiobookshelf_token: string;
  music_root: string;
  deploy_nas_host: string;
  deploy_nas_user: string;
  deploy_nas_password: string;
  deploy_remote_app_dir: string;
  deploy_music_host_path: string;
}

interface UpdateStatus {
  available: boolean;
  allowed: boolean;
  configured: boolean;
  transport: string | null;
  transport_ready: boolean;
  password_configured: boolean;
  client_host: string;
  nas_host: string;
  nas_user: string;
  remote_app_dir: string;
  reason: string | null;
  runtime: {
    running: boolean;
    last_started_at: string | null;
    last_finished_at: string | null;
    last_status: string;
    last_message: string | null;
    last_output_tail: string[];
  };
}

const LABELS: Record<keyof SettingsValues, { label: string; secret?: boolean; placeholder?: string }> = {
  emby_url: { label: "Server URL", placeholder: "http://10.0.0.2:8096" },
  emby_api_key: { label: "API key", secret: true },
  homescreen_db_path: { label: "Homescreen DB path", placeholder: "C:\\ProgramData\\Emby-Server\\data\\users.db" },
  tmdb_api_key: { label: "TMDB API key", secret: true },
  navidrome_url: { label: "Server URL", placeholder: "http://10.0.0.2:4533" },
  navidrome_user: { label: "Username" },
  navidrome_password: { label: "Password", secret: true },
  audiobookshelf_url: { label: "Server URL", placeholder: "http://10.0.0.2:13378" },
  audiobookshelf_token: { label: "API token", secret: true },
  music_root: { label: "Library path", placeholder: "/music" },
  deploy_nas_host: { label: "NAS host", placeholder: "MATT-NAS or 10.0.0.10" },
  deploy_nas_user: { label: "NAS SSH user", placeholder: "ssh" },
  deploy_nas_password: { label: "NAS SSH password", secret: true },
  deploy_remote_app_dir: { label: "Remote app dir", placeholder: "/share/Docker/homelabtoolkit" },
  deploy_music_host_path: { label: "Host music path", placeholder: "/share/Movies/Music" },
};

type DotState = "ok" | "off" | "idle";

export default function Settings({ onSaved }: { onSaved?: () => void }) {
  const toast = useToast();
  const [values, setValues] = useState<SettingsValues | null>(null);
  const [saving, setSaving] = useState(false);
  const [reveal, setReveal] = useState(false);
  const [config, setConfig] = useState<AppConfig | null>(null);
  const [navStatus, setNavStatus] = useState<{ configured: boolean; connected: boolean } | null>(null);
  const [absStatus, setAbsStatus] = useState<{ configured: boolean; connected: boolean } | null>(null);
  const [updateStatus, setUpdateStatus] = useState<UpdateStatus | null>(null);
  const [updating, setUpdating] = useState(false);

  function loadStatuses() {
    apiGet<AppConfig>("/api/config").then(setConfig).catch(() => setConfig(null));
    apiGet("/api/navidrome/status").then(setNavStatus).catch(() => setNavStatus(null));
    apiGet("/api/audiobookshelf/status").then(setAbsStatus).catch(() => setAbsStatus(null));
    apiGet<UpdateStatus>("/api/update/status").then(setUpdateStatus).catch(() => setUpdateStatus(null));
  }

  useEffect(() => {
    apiGet<SettingsValues>("/api/settings")
      .then(setValues)
      .catch((e) => toast(e.message, "err"));
    loadStatuses();
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
    if (!updating && !updateStatus?.runtime.running) return;
    const timer = window.setInterval(() => {
      loadStatuses();
    }, 1500);
    return () => window.clearInterval(timer);
  }, [updating, updateStatus?.runtime.running]); // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
    if (!updating || updateStatus?.runtime.running) return;
    setUpdating(false);
    if (updateStatus?.runtime.last_message) {
      toast(updateStatus.runtime.last_message, updateStatus.runtime.last_status === "ok" ? "ok" : "err");
    }
  }, [updating, updateStatus?.runtime.running, updateStatus?.runtime.last_finished_at]); // eslint-disable-line react-hooks/exhaustive-deps

  function set<K extends keyof SettingsValues>(k: K, v: string) {
    setValues((current) => (current ? { ...current, [k]: v } : current));
  }

  async function save() {
    if (!values) return;
    setSaving(true);
    try {
      await apiPost("/api/settings", values);
      toast("Settings saved", "ok");
      loadStatuses();
      onSaved?.();
    } catch (e: any) {
      toast(e.message, "err");
    } finally {
      setSaving(false);
    }
  }

  async function runUpdate() {
    if (!values) return;
    setUpdating(true);
    try {
      await apiPost("/api/settings", values);
      const result = await apiPost<{ result: { message: string }; status: UpdateStatus }>("/api/update/run");
      setUpdateStatus(result.status);
      toast(result.result.message || "Deployment started", "ok");
    } catch (e: any) {
      toast(e.message, "err");
      loadStatuses();
      setUpdating(false);
      return;
    }
  }

  if (!values) return <Loading />;

  const deployConfiguredDraft = !!values.deploy_nas_host.trim() && !!values.deploy_nas_user.trim();
  const deployToolsReady = !!updateStatus?.transport_ready;
  const deployAllowed = !!updateStatus?.allowed;
  const deployReady = deployAllowed && deployConfiguredDraft && deployToolsReady;
  const deployStateLabel = updateStatus
    ? deployReady
      ? "Ready"
      : deployAllowed
        ? deployConfiguredDraft
          ? "Missing tools"
          : "Needs setup"
        : "Local only"
    : "Checking";
  const deployReason =
    !updateStatus
      ? null
      : !deployAllowed
        ? updateStatus.reason
        : !deployConfiguredDraft
          ? "Set a NAS host and NAS SSH user, then deploy directly from this screen."
          : !deployToolsReady
            ? updateStatus.reason
            : updateStatus.reason;

  const dot = (configured: boolean, connected: boolean): [DotState, string] =>
    !configured ? ["idle", "Not configured"] : connected ? ["ok", "Connected"] : ["off", "Offline"];

  const cards: {
    title: string;
    icon: React.ReactNode;
    keys: (keyof SettingsValues)[];
    status: [DotState, string];
  }[] = [
    {
      title: "Emby",
      icon: <IconEmby />,
      keys: ["emby_url", "emby_api_key", "homescreen_db_path", "tmdb_api_key"],
      status: dot(!!config?.emby.connected, !!config?.emby.connected),
    },
    {
      title: "Navidrome",
      icon: <IconMusic />,
      keys: ["navidrome_url", "navidrome_user", "navidrome_password"],
      status: dot(!!navStatus?.configured, !!navStatus?.connected),
    },
    {
      title: "Audiobookshelf",
      icon: <IconBook />,
      keys: ["audiobookshelf_url", "audiobookshelf_token"],
      status: dot(!!absStatus?.configured, !!absStatus?.connected),
    },
    {
      title: "Music library",
      icon: <IconFolder />,
      keys: ["music_root"],
      status: config?.music.available ? (["ok", "Mounted"] as [DotState, string]) : (["off", "Not mounted"] as [DotState, string]),
    },
  ];

  return (
    <>
      <div className="page-toolbar">
        <PageHead title="Settings" icon={<IconSettings />} />
        <div className="page-toolbar-actions">
          <label className="chip" style={{ cursor: "pointer" }}>
            <input type="checkbox" checked={reveal} onChange={(e) => setReveal(e.target.checked)} /> Show secrets
          </label>
          <button className="btn btn-primary" onClick={save} disabled={saving}>
            {saving ? <span className="spinner" /> : <IconCheck />} Save
          </button>
        </div>
      </div>

      <div className="section-label">Connections</div>
      <div className="settings-grid">
        {cards.map((c) => (
          <div className="panel" key={c.title}>
            <div className="panel-head">
              <div className="stat-icon" style={{ width: 32, height: 32, borderRadius: 9 }}>
                {c.icon}
              </div>
              <h3 className="grow">{c.title}</h3>
              <span className="status-chip" style={{ padding: "5px 9px", background: "transparent", border: 0 }}>
                <span className={`dot ${c.status[0] === "ok" ? "" : c.status[0]}`} />
                <span className="dim" style={{ fontSize: 12 }}>
                  {c.status[1]}
                </span>
              </span>
            </div>
            <div className="panel-body col" style={{ gap: 14 }}>
              {c.keys.map((k) => (
                <div className="field" key={k}>
                  <label className="field-label">{LABELS[k].label}</label>
                  <input
                    className="input"
                    type={LABELS[k].secret && !reveal ? "password" : "text"}
                    autoComplete="off"
                    spellCheck={false}
                    placeholder={LABELS[k].placeholder}
                    value={values[k]}
                    onChange={(e) => set(k, e.target.value)}
                  />
                </div>
              ))}
            </div>
          </div>
        ))}
      </div>
      <p className="hint" style={{ marginTop: 12 }}>
        Secrets are stored in plaintext in the app's config file on the server. Use this on a trusted local network.
      </p>

      <div className="section-label" style={{ marginTop: 28 }}>Update</div>
      <div className="panel" style={{ maxWidth: 980 }}>
        <div className="panel-head">
          <div className="stat-icon" style={{ width: 32, height: 32, borderRadius: 9 }}>
            <IconRefresh />
          </div>
          <h3 className="grow">Deploy to Docker host</h3>
          {updateStatus ? (
            <span className="status-chip" style={{ padding: "5px 9px", background: "transparent", border: 0 }}>
              <span className={`dot ${deployReady ? "" : deployAllowed ? "off" : "idle"}`} />
              <span className="dim" style={{ fontSize: 12 }}>
                {deployStateLabel}
              </span>
            </span>
          ) : null}
        </div>
        <div className="panel-body col" style={{ gap: 14 }}>
          <div className="settings-update-grid">
            {(["deploy_nas_host", "deploy_nas_user", "deploy_nas_password", "deploy_remote_app_dir", "deploy_music_host_path"] as const).map((k) => (
              <div className="field" key={k}>
                <label className="field-label">{LABELS[k].label}</label>
                <input
                  className="input"
                  type={LABELS[k].secret && !reveal ? "password" : "text"}
                  autoComplete="off"
                  spellCheck={false}
                  placeholder={LABELS[k].placeholder}
                  value={values[k]}
                  onChange={(e) => set(k, e.target.value)}
                />
              </div>
            ))}
          </div>

          <div className="row gap-sm" style={{ flexWrap: "wrap" }}>
            <button className="btn btn-primary" onClick={runUpdate} disabled={updating || !!updateStatus?.runtime.running || !deployReady}>
              {updating || updateStatus?.runtime.running ? <span className="spinner" /> : <IconRefresh />} Deploy now
            </button>
            <button className="btn btn-sm" onClick={loadStatuses}>
              <IconRefresh /> Refresh status
            </button>
            {updateStatus?.runtime.last_finished_at ? <span className="badge">Last run: {updateStatus.runtime.last_finished_at}</span> : null}
            {updateStatus?.runtime.last_status && updateStatus.runtime.last_status !== "idle" ? (
              <span className={`badge ${updateStatus.runtime.last_status === "ok" ? "badge-ok" : updateStatus.runtime.last_status === "running" ? "badge-accent" : "badge-bad"}`}>
                {updateStatus.runtime.last_status}
              </span>
            ) : null}
          </div>

          <p className="hint" style={{ margin: 0 }}>
            Available only when the app is opened from a local/private address like <code>127.0.0.1</code> or <code>10.0.0.124</code>. The app now deploys directly over Python SSH, syncing the repo and rebuilding Docker on the configured NAS host without relying on PowerShell or interactive prompts.
          </p>
          <p className="hint" style={{ margin: 0 }}>
            The remote compose file is rendered from these deploy settings, including the NAS-side music bind mount path.
          </p>
          {updateStatus?.client_host ? <p className="hint" style={{ margin: 0 }}>Detected client: <code>{updateStatus.client_host}</code></p> : null}
          {updateStatus?.transport ? <p className="hint" style={{ margin: 0 }}>Transport: <code>{updateStatus.transport}</code> · Auth: <code>{updateStatus.password_configured ? "saved password" : "SSH keys / agent"}</code></p> : null}
          {deployReason ? <p className="hint" style={{ margin: 0, color: "var(--red)" }}>{deployReason}</p> : null}
          {updateStatus?.runtime.running ? <p className="hint" style={{ margin: 0 }}>Deployment in progress. Status refreshes automatically.</p> : null}
          {updateStatus?.runtime.last_message ? <p className="hint" style={{ margin: 0 }}>Last result: {updateStatus.runtime.last_message}</p> : null}
          {updateStatus?.runtime.last_output_tail?.length ? (
            <div className="field">
              <label className="field-label">Recent deploy output</label>
              <div className="console" style={{ maxHeight: 260 }}>
                {updateStatus.runtime.last_output_tail.map((line, index) => (
                  <div className="log-line" key={`${index}-${line}`}>
                    <span>{line}</span>
                  </div>
                ))}
              </div>
            </div>
          ) : null}
        </div>
      </div>
    </>
  );
}
