import { useEffect, useState } from "react";

import * as api from "../api";
import type { Theme } from "../hooks/useAppearance";
import type { Config, Group, WorkApp } from "../types";
import { Trash } from "./icons";
import {
  BTN,
  BTN_PRIMARY,
  Badge,
  Dialog,
  Favicon,
  HELP,
  ICON_CHROME,
  INPUT,
  LABEL,
  SUBPANEL,
  SectionHeading,
  Segmented,
} from "./ui";

interface Props {
  config: Config;
  /** The app a test notification is fired from. */
  activeId: string | null;
  theme: Theme;
  /** Opens straight onto the hidden-elements section when set. */
  focusHiddenFor?: string | null;
  onConfig: (c: Config) => void;
  onTheme: (t: Theme) => void;
  onClose: () => void;
}

export default function Settings({
  config, theme, activeId, focusHiddenFor, onConfig, onTheme, onClose,
}: Props) {
  const [name, setName] = useState("");
  const [url, setUrl] = useState("");
  const [groupId, setGroupId] = useState("");
  const [groupName, setGroupName] = useState("");
  const [error, setError] = useState<string | null>(null);
  const [confirmDelete, setConfirmDelete] = useState<WorkApp | null>(null);

  const [notifyStatus, setNotifyStatus] = useState<string | null>(null);
  const [reports, setReports] = useState<[string, string][]>([]);

  const groups = [...config.groups].sort((a, b) => a.order - b.order);

  // Same order the nav shows, so the two lists never disagree about position.
  const groupRank = (id: string | null) =>
    id === null ? -1 : (groups.find((g) => g.id === id)?.order ?? Number.MAX_SAFE_INTEGER);
  const orderedApps = [...config.apps].sort(
    (a, b) => groupRank(a.groupId) - groupRank(b.groupId) || a.order - b.order,
  );


  useEffect(() => {
    if (!focusHiddenFor) return;
    document.getElementById("hidden-elements")?.scrollIntoView({ behavior: "smooth" });
  }, [focusHiddenFor]);

  const run = async (fn: () => Promise<Config>) => {
    try {
      onConfig(await fn());
      setError(null);
    } catch (e) {
      setError(String(e));
    }
  };

  const addApp = async () => {
    if (!name.trim() || !url.trim()) return;
    await run(() => api.addApp(name, url, groupId || null));
    setName("");
    setUrl("");
  };

  const addGroup = async () => {
    if (!groupName.trim()) return;
    await run(() => api.addGroup(groupName));
    setGroupName("");
  };

  const patch = (app: WorkApp, fields: Partial<WorkApp>) =>
    run(() => api.updateApp({ ...app, ...fields }));


  const withHidden = orderedApps.filter((a) => a.hidden.length > 0);

  return (
    <>
      <Dialog title="Settings" onCancel={onClose} wide footer={<div />}>
        <div className="space-y-6">
          {error && (
            <p className="rounded-lg bg-red-500/10 px-3 py-2 text-[12px] text-red-600 dark:text-red-400">
              {error}
            </p>
          )}

          {/* ----------------------------------------------------- apps */}
          <section className="space-y-2">
            <div className="flex items-center justify-between">
              <SectionHeading>Apps</SectionHeading>
              <button
                onClick={() => run(() => api.refreshFavicons())}
                title="Fetch every icon again — one that cached wrong will not fix itself"
                className={BTN}
              >
                Refresh icons
              </button>
            </div>
            <div className="space-y-1.5">
              {config.apps.length === 0 && <p className={HELP}>Nothing yet. Add the first one below.</p>}
              {orderedApps.map((app) => (
                <div
                  key={app.id}
                  className="flex items-center gap-2 rounded-lg border border-slate-200 px-2 py-1.5 dark:border-slate-800"
                >
                  <Favicon url={app.url} name={app.name} version={config.settings.faviconVersion ?? 0} />
                  <input
                    value={app.name}
                    onChange={(e) => patch(app, { name: e.target.value })}
                    className={`${INPUT} h-[26px] w-full flex-1`}
                  />
                  <input
                    value={app.url}
                    onChange={(e) => patch(app, { url: e.target.value })}
                    title="Changing this rebuilds the app's view"
                    className={`${INPUT} h-[26px] w-full flex-[1.4] font-mono text-[11px]`}
                  />
                  <select
                    value={app.groupId ?? ""}
                    onChange={(e) => patch(app, { groupId: e.target.value || null })}
                    className={`${INPUT} h-[26px] w-[110px] shrink-0 cursor-pointer`}
                  >
                    <option value="">No group</option>
                    {groups.map((g) => (
                      <option key={g.id} value={g.id}>{g.name}</option>
                    ))}
                  </select>
                  <button
                    onClick={() => setConfirmDelete(app)}
                    title={`Remove ${app.name}`}
                    className={`${ICON_CHROME} hover:text-red-500!`}
                  >
                    <Trash />
                  </button>
                </div>
              ))}
            </div>

            <div className={`${SUBPANEL} flex items-end gap-2`}>
              <label className="flex-1 space-y-1">
                <span className={LABEL}>Name</span>
                <input value={name} onChange={(e) => setName(e.target.value)}
                  placeholder="Odoo" className={`${INPUT} w-full`} />
              </label>
              <label className="flex-[1.4] space-y-1">
                <span className={LABEL}>URL</span>
                <input value={url} onChange={(e) => setUrl(e.target.value)}
                  onKeyDown={(e) => e.key === "Enter" && addApp()}
                  placeholder="example.com" className={`${INPUT} w-full font-mono text-[11px]`} />
              </label>
              <label className="w-[110px] space-y-1">
                <span className={LABEL}>Group</span>
                <select value={groupId} onChange={(e) => setGroupId(e.target.value)}
                  className={`${INPUT} w-full cursor-pointer`}>
                  <option value="">No group</option>
                  {groups.map((g) => (
                    <option key={g.id} value={g.id}>{g.name}</option>
                  ))}
                </select>
              </label>
              <button onClick={addApp} className={BTN_PRIMARY}>Add</button>
            </div>
            <p className={HELP}>
              An app owns its URL's host. Links to any other app on this list switch to it;
              everything else opens in your browser.
            </p>
          </section>

          {/* ------------------------------------------- hidden elements */}
          <section id="hidden-elements" className="space-y-2">
            <SectionHeading>Hidden elements</SectionHeading>
            {withHidden.length === 0 ? (
              <p className={HELP}>
                Nothing hidden. Right-click anything in an app and choose <em>Hide this
                element</em> — or use the eye button in the nav to pick one.
              </p>
            ) : (
              <div className="space-y-3">
                {withHidden.map((app) => (
                  <div key={app.id} className={SUBPANEL}>
                    <div className="mb-2 flex items-center gap-2">
                      <Favicon url={app.url} name={app.name} version={config.settings.faviconVersion ?? 0} />
                      <span className="text-[12px] font-medium">{app.name}</span>
                      <Badge tone="accent">{app.hidden.length}</Badge>
                    </div>
                    <div className="space-y-1">
                      {app.hidden.map((sel, i) => (
                        <div key={i} className="flex items-center gap-2">
                          <input
                            value={sel}
                            onChange={(e) => {
                              const next = [...app.hidden];
                              next[i] = e.target.value;
                              run(() => api.setHidden(app.id, next));
                            }}
                            title="Edit the selector to widen or narrow what it hides"
                            className={`${INPUT} h-[26px] w-full flex-1 font-mono text-[11px]`}
                          />
                          <button
                            onClick={() =>
                              run(() => api.setHidden(app.id, app.hidden.filter((_, j) => j !== i)))
                            }
                            title="Show this again"
                            className={`${ICON_CHROME} hover:text-red-500!`}
                          >
                            <Trash />
                          </button>
                        </div>
                      ))}
                    </div>
                  </div>
                ))}
              </div>
            )}
            <p className={HELP}>
              Changes apply immediately, without a reload. A selector that stops matching
              after the site changes simply hides nothing.
            </p>
          </section>

          {/* ------------------------------------------------------ zoom */}
          <section className="space-y-2">
            <SectionHeading>Zoom</SectionHeading>
            <div className="space-y-1.5">
              {orderedApps.map((app) => (
                <div
                  key={app.id}
                  className="flex items-center gap-3 rounded-lg border border-slate-200 px-2 py-1.5 dark:border-slate-800"
                >
                  <Favicon url={app.url} name={app.name} version={config.settings.faviconVersion ?? 0} />
                  <span className="w-28 shrink-0 truncate text-[12px]">{app.name}</span>
                  <input
                    type="range"
                    min={50}
                    max={200}
                    step={5}
                    value={Math.round((app.zoom ?? 1) * 100)}
                    onChange={(e) =>
                      run(() => api.setZoom(app.id, Number(e.target.value) / 100))
                    }
                    className="min-w-0 flex-1 cursor-pointer accent-sky-500"
                  />
                  <span className="w-12 shrink-0 text-right font-mono text-[11px] tabular-nums text-slate-500 dark:text-slate-400">
                    {Math.round((app.zoom ?? 1) * 100)}%
                  </span>
                </div>
              ))}
            </div>
            <p className={HELP}>
              ⌘+ and ⌘− zoom the app you are in; ⌘0 puts it back to 100%. Each app keeps
              its own size.
            </p>
          </section>

          {/* --------------------------------------------- notifications */}
          <section className="space-y-2">
            <SectionHeading>Notifications</SectionHeading>
            <div className="flex items-center gap-2">
              <button
                onClick={() => activeId && api.testNotification(activeId)}
                disabled={!activeId}
                className={BTN}
              >
                Send a test notification
              </button>
              <button
                onClick={async () => setNotifyStatus(await api.notificationStatus())}
                className={BTN}
              >
                Check permission
              </button>
              <button
                onClick={async () => {
                  await api.probeApps();
                  await new Promise((r) => setTimeout(r, 500));
                  setReports(await api.appReports());
                }}
                className={BTN}
              >
                Check background apps
              </button>
            </div>
            {reports.length > 0 && (
              <div className="space-y-0.5">
                {reports.map(([name, report]) => (
                  <p key={name} className={`${HELP} font-mono`}>
                    <span className="font-semibold">{name}</span> · {report}
                  </p>
                ))}
              </div>
            )}
            <div className="hidden">
            </div>
            {notifyStatus && (
              <p className={`${HELP} font-mono`}>{notifyStatus}</p>
            )}
            <p className={HELP}>
              WKWebView defines a notification API that silently does nothing, so it is
              replaced with one that forwards to macOS. Notifications raised by a service
              worker in the background are not covered — only those a page raises while open.
            </p>
          </section>

          {/* ---------------------------------------------------- groups */}
          <section className="space-y-2">
            <SectionHeading>Groups</SectionHeading>
            <div className="space-y-1.5">
              {groups.length === 0 && <p className={HELP}>No groups yet.</p>}
              {groups.map((g: Group) => (
                <div key={g.id}
                  className="flex items-center gap-2 rounded-lg border border-slate-200 px-2 py-1.5 dark:border-slate-800">
                  <input
                    value={g.name}
                    onChange={(e) => run(() => api.updateGroup({ ...g, name: e.target.value }))}
                    className={`${INPUT} h-[26px] w-full flex-1`}
                  />
                  <span className="shrink-0 font-mono text-[10px] text-slate-400">
                    {config.apps.filter((a) => a.groupId === g.id).length} apps
                  </span>
                  <button
                    onClick={() => run(() => api.deleteGroup(g.id))}
                    title={`Delete ${g.name} — its apps stay, ungrouped`}
                    className={`${ICON_CHROME} hover:text-red-500!`}
                  >
                    <Trash />
                  </button>
                </div>
              ))}
            </div>
            <div className={`${SUBPANEL} flex items-end gap-2`}>
              <label className="flex-1 space-y-1">
                <span className={LABEL}>New group</span>
                <input value={groupName} onChange={(e) => setGroupName(e.target.value)}
                  onKeyDown={(e) => e.key === "Enter" && addGroup()}
                  placeholder="Finance" className={`${INPUT} w-full`} />
              </label>
              <button onClick={addGroup} className={BTN}>Add group</button>
            </div>
          </section>

          {/* ----------------------------------------------- appearance */}
          <section className="space-y-2">
            <SectionHeading>Appearance</SectionHeading>
            <Segmented<Theme>
              value={theme}
              onChange={onTheme}
              options={[
                { value: "system", label: "System" },
                { value: "light", label: "Light" },
                { value: "dark", label: "Dark" },
              ]}
            />
          </section>

          <div className="flex justify-end border-t border-slate-200 pt-4 dark:border-slate-800">
            <button onClick={onClose} className={BTN}>Done</button>
          </div>
        </div>
      </Dialog>

      {confirmDelete && (
        <Dialog
          title={`Remove ${confirmDelete.name}?`}
          onCancel={() => setConfirmDelete(null)}
          onConfirm={async () => {
            await run(() => api.deleteApp(confirmDelete.id));
            setConfirmDelete(null);
          }}
          confirmLabel="Remove"
          destructive
        >
          It disappears from the nav and its view is closed. The session it holds for{" "}
          <span className="font-mono text-[12px]">{confirmDelete.url}</span> is kept, so
          adding it back does not mean signing in again.
        </Dialog>
      )}
    </>
  );
}
