// Puck's settings — the prototype's "Tweaks" reborn as product UI,
// styled with the app's own theme tokens.

import * as React from "react";
import { requestNotifyPermission } from "../lib/notify";
import { ACCENTS, type Settings } from "../lib/settings";
import { type Pos, useDrag } from "../lib/useDrag";
import { KOKORO_VOICES, listVoices, previewVoice, sayVoices, type VoiceEngine } from "../lib/voice";

function Row({ label, children }: { label: string; children: React.ReactNode }) {
  return (
    <div className="set-row">
      <span className="set-label">{label}</span>
      <div className="set-ctl">{children}</div>
    </div>
  );
}

function Pills<T extends string>({
  value,
  options,
  onChange,
}: {
  value: T;
  options: { value: T; label: string }[];
  onChange: (v: T) => void;
}) {
  return (
    <div className="set-pills">
      {options.map((o) => (
        <button
          type="button"
          key={o.value}
          className={`mode-pill${value === o.value ? " on" : ""}`}
          onClick={() => onChange(o.value)}
        >
          {o.label}
        </button>
      ))}
    </div>
  );
}

function Slider({ value, onChange }: { value: number; onChange: (v: number) => void }) {
  return (
    <div className="set-slider">
      <input
        type="range"
        min={0}
        max={100}
        step={5}
        value={value}
        onChange={(e) => onChange(Number(e.target.value))}
      />
      <span className="set-val">{value}</span>
    </div>
  );
}

export function SettingsPanel({
  open,
  pos,
  onPos,
  onClose,
  settings,
  onChange,
  overlay = false,
  onPatrol,
  onSleep,
}: {
  open: boolean;
  pos: Pos;
  onPos: (p: Pos) => void;
  onClose: () => void;
  settings: Settings;
  onChange: <K extends keyof Settings>(key: K, value: Settings[K]) => void;
  /** Overlay (WKWebView) forces the Mac `say` engine — browser audio is dead there. */
  overlay?: boolean;
  onPatrol: () => void;
  onSleep: () => void;
}) {
  const drag = useDrag(pos, onPos, { marginR: 120, marginB: 80 });
  // the overlay can only use the Mac voice; elsewhere honor the chosen engine
  const engine: VoiceEngine = overlay ? "say" : settings.voiceEngine;
  const [sayList, setSayList] = React.useState<{ id: string; lang: string }[]>([]);
  React.useEffect(() => {
    if (open && engine === "say") void sayVoices().then(setSayList);
  }, [open, engine]);
  if (!open) return null;

  return (
    <div className="settings panel" style={{ left: pos.x, top: pos.y }}>
      <div
        className="comp-head"
        onMouseDown={(e) => {
          if ((e.target as HTMLElement).closest(".comp-x")) return;
          drag(e);
        }}
      >
        <div className="comp-id">
          <b>Settings</b>
          <span>how Puck looks, sounds, behaves</span>
        </div>
        <button type="button" className="comp-x" onClick={onClose}>
          ✕
        </button>
      </div>

      <div className="comp-body scroll set-body">
        <div className="sect-label">Look</div>
        <Row label="Theme">
          <Pills
            value={settings.theme}
            options={[
              { value: "terrarium", label: "Terrarium" },
              { value: "candlelight", label: "Candle" },
              { value: "nocturne", label: "Glass" },
            ]}
            onChange={(v) => onChange("theme", v)}
          />
        </Row>
        <Row label="Accent">
          <div className="set-swatches">
            {ACCENTS.map((c) => (
              <button
                type="button"
                key={c}
                className={`set-swatch${settings.accent === c ? " on" : ""}`}
                style={{ background: c }}
                onClick={() => onChange("accent", c)}
              />
            ))}
          </div>
        </Row>

        <div className="sect-label">Creature</div>
        <Row label="Form">
          <Pills
            value={settings.form}
            options={[
              { value: "mossling", label: "Mossling" },
              { value: "wisp", label: "Wisp" },
              { value: "gremlin", label: "Gremlin" },
              { value: "moth", label: "Moth" },
            ]}
            onChange={(v) => onChange("form", v)}
          />
        </Row>
        <Row label="Presence">
          <Slider value={settings.presence} onChange={(v) => onChange("presence", v)} />
        </Row>
        <Row label="Mischief">
          <Slider value={settings.mischief} onChange={(v) => onChange("mischief", v)} />
        </Row>

        <div className="sect-label">Voice & behaviour</div>
        <Row label="Spoken voice">
          <Pills
            value={settings.voiceSound}
            options={[
              { value: "off", label: "Silent" },
              { value: "whisper", label: "Whisper" },
              { value: "full", label: "Full" },
            ]}
            onChange={(v) => onChange("voiceSound", v)}
          />
        </Row>
        {settings.voiceSound !== "off" && (
          <>
            {!overlay && (
              <Row label="Engine">
                <Pills
                  value={settings.voiceEngine}
                  options={[
                    { value: "say", label: "Mac" },
                    { value: "kokoro", label: "Neural" },
                    { value: "browser", label: "Web" },
                  ]}
                  onChange={(v) => onChange("voiceEngine", v)}
                />
              </Row>
            )}
            <Row label="Which voice">
              <div className="set-voicepick">
                {engine === "say" ? (
                  <select
                    className="set-select"
                    value={settings.sayVoice}
                    onChange={(e) => {
                      onChange("sayVoice", e.target.value);
                      previewVoice("say", e.target.value); // audition on pick
                    }}
                  >
                    <option value="">System default</option>
                    {sayList.map((v) => (
                      <option key={v.id} value={v.id}>
                        {v.id} · {v.lang}
                      </option>
                    ))}
                  </select>
                ) : engine === "kokoro" ? (
                  <select
                    className="set-select"
                    value={settings.kokoroVoice}
                    onChange={(e) => {
                      onChange("kokoroVoice", e.target.value);
                      previewVoice("kokoro", e.target.value);
                    }}
                  >
                    {KOKORO_VOICES.map((v) => (
                      <option key={v.id} value={v.id}>
                        {v.label}
                      </option>
                    ))}
                  </select>
                ) : (
                  <select
                    className="set-select"
                    value={settings.voiceName}
                    onChange={(e) => {
                      onChange("voiceName", e.target.value);
                      previewVoice("browser", e.target.value);
                    }}
                  >
                    <option value="">Auto (soft english)</option>
                    {listVoices().map((v) => (
                      <option key={v.name} value={v.name}>
                        {v.name} · {v.lang}
                      </option>
                    ))}
                  </select>
                )}
                <button
                  type="button"
                  className="btn"
                  onClick={() =>
                    previewVoice(
                      engine,
                      engine === "say"
                        ? settings.sayVoice
                        : engine === "kokoro"
                          ? settings.kokoroVoice
                          : settings.voiceName,
                    )
                  }
                >
                  ▸ test
                </button>
              </div>
            </Row>
            {engine === "say" && (
              <div className="set-note">
                Mac's built-in voice — works everywhere, including the overlay. For much better voices,
                install Premium/Siri voices in System Settings → Accessibility → Spoken Content → System
                Voice.
              </div>
            )}
            {engine === "kokoro" && (
              <div className="set-note">
                Neural voice runs locally in your browser. First use downloads the ~80MB model once (cached
                after); the system voice covers the gap while it loads.
              </div>
            )}
            <Row label="Speak shouts">
              <Pills
                value={settings.speakShouts ? "on" : "off"}
                options={[
                  { value: "on", label: "On" },
                  { value: "off", label: "Off" },
                ]}
                onChange={(v) => onChange("speakShouts", v === "on")}
              />
            </Row>
          </>
        )}
        <Row label="Comments">
          <Pills
            value={settings.voice}
            options={[
              { value: "voice", label: "Fairy" },
              { value: "plain", label: "Plain" },
              { value: "hand", label: "Hand" },
            ]}
            onChange={(v) => onChange("voice", v)}
          />
        </Row>
        <Row label="Notifications">
          <Pills
            value={settings.notify}
            options={[
              { value: "adaptive", label: "Adaptive" },
              { value: "bubble", label: "Bubble" },
              { value: "toast", label: "Toast" },
              { value: "interrupt", label: "Interrupt" },
            ]}
            onChange={(v) => onChange("notify", v)}
          />
        </Row>
        <Row label="When hidden">
          <Pills
            value={settings.osNotify ? "on" : "off"}
            options={[
              { value: "on", label: "OS notification" },
              { value: "off", label: "Stay quiet" },
            ]}
            onChange={async (v) => {
              // permission request must ride a user gesture; only persist on if granted
              if (v === "on") onChange("osNotify", await requestNotifyPermission());
              else onChange("osNotify", false);
            }}
          />
        </Row>

        <div className="sect-label">Eyes (vision)</div>
        <Row label="Mode">
          <Pills
            value={settings.visionMode}
            options={[
              { value: "off", label: "Off" },
              { value: "ondemand", label: "On demand" },
              { value: "ambient", label: "Ambient" },
            ]}
            onChange={(v) => onChange("visionMode", v)}
          />
        </Row>
        <div className="set-note">
          {settings.visionMode === "off"
            ? "Puck's eyes are shut."
            : settings.visionMode === "ondemand"
              ? "Looks only when you pick “Look around”. No cost unattended."
              : "Looks every ~45s while you're active; pauses when idle so the cloud GPU scales to zero."}
        </div>

        <div className="set-actions">
          <button type="button" className="btn primary" onClick={onPatrol}>
            Patrol now
          </button>
          <button type="button" className="btn" onClick={onSleep}>
            Night Bloom
          </button>
        </div>
      </div>
    </div>
  );
}
