/**
 * Settings — appearance, profile, and connecting an agent.
 *
 * The appearance section is where the user picks the accent that re-tints the
 * whole product. It is saved server-side, so the choice follows them between
 * devices rather than being a per-browser quirk.
 */
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { Check, LogOut, Monitor, Moon, Sun, Terminal } from 'lucide-react';
import { get, getSupabase, patch } from '@/lib/api';
import { useTheme } from '@/lib/theme';
import { getAccent, THEME_MODES, type ThemeMode } from '@pig/core';
import { Badge, Button, Card, CardContent, CardHeader, CardTitle, Input } from '@/components/ui';
import { useState } from 'react';
import { usePageTitle } from '@/lib/title';
import { AdminSettings } from '@/components/AdminSettings';
import { toast } from 'sonner';

interface Me {
  id: string;
  name: string;
  email: string;
  isPlatformAdmin: boolean;
  teams: { team: string; role: string }[];
  via: string;
}

export function Settings() {
  usePageTitle('Settings');
  const { data: me } = useQuery({ queryKey: ['me'], queryFn: () => get<Me>('/api/me') });

  return (
    <div className="space-y-6 pb-4">
      <header className="flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between">
        <div>
          <h1 className="text-xl font-semibold tracking-tight sm:text-2xl">Settings</h1>
          <p className="mt-1 max-w-2xl text-sm text-muted">
            Personal preferences, workspace access, and server-managed integration readiness.
          </p>
        </div>
        {me?.isPlatformAdmin ? <Badge tone="warning">Platform admin view</Badge> : null}
      </header>

      <div className="grid gap-6 xl:grid-cols-2">
        <Appearance />
        <Profile me={me} />
      </div>
      {me?.isPlatformAdmin ? <AdminSettings /> : null}
      <div className="grid gap-6 xl:grid-cols-2">
        <ConnectAgent />
        <SessionCard />
      </div>
    </div>
  );
}

function Appearance() {
  const { mode, accent, resolved, setMode, setAccent, accents } = useTheme();

  return (
    <Card>
      <CardHeader>
        <CardTitle className="text-base">Appearance</CardTitle>
        <p className="text-sm text-muted">
          Saved to your account, so it follows you between your laptop and your phone.
        </p>
      </CardHeader>
      <CardContent className="space-y-5">
        <div>
          <p className="mb-2 text-xs font-medium uppercase tracking-wide text-muted">Theme</p>
          <div className="inline-flex w-full rounded-lg bg-surface-2 p-1 sm:w-auto">
            {THEME_MODES.map((value) => {
              const Icon = value === 'light' ? Sun : value === 'dark' ? Moon : Monitor;
              return (
                <button
                  key={value}
                  type="button"
                  onClick={() => setMode(value as ThemeMode)}
                  aria-pressed={mode === value}
                  className={[
                    'tap flex flex-1 items-center justify-center gap-2 rounded-md px-4 text-sm font-medium capitalize transition-colors sm:flex-none',
                    mode === value ? 'bg-surface text-fg shadow-sm' : 'text-muted',
                  ].join(' ')}
                >
                  <Icon className="h-4 w-4" aria-hidden />
                  {value}
                </button>
              );
            })}
          </div>
        </div>

        <div>
          <p className="mb-2 text-xs font-medium uppercase tracking-wide text-muted">Accent</p>
          <div className="flex flex-wrap gap-2">
            {accents.map((option) => {
              const selected = option.key === accent;
              const definition = getAccent(option.key);
              return (
                <button
                  key={option.key}
                  type="button"
                  onClick={() => setAccent(option.key)}
                  aria-pressed={selected}
                  aria-label={option.label}
                  title={option.label}
                  className={[
                    'tap relative flex items-center gap-2 rounded-lg border px-3 py-2 text-sm font-medium transition-colors',
                    selected ? 'border-primary bg-accent-subtle' : 'border-border hover:bg-surface-2',
                  ].join(' ')}
                >
                  {/*
                    The swatch previews the value for the CURRENT theme, because
                    each accent is tuned twice — a colour that reads well on
                    white is usually too dark on near-black. Showing the light
                    value in dark mode made the near-black "Pig" swatch
                    effectively invisible against a dark card, which is exactly
                    the misrepresentation this avoids.
                  */}
                  <span
                    className="h-4 w-4 rounded-full border border-fg/15"
                    style={{
                      backgroundColor: `hsl(${
                        resolved === 'dark' ? definition.dark.accent : definition.light.accent
                      })`,
                    }}
                    aria-hidden
                  />
                  {option.label}
                  {selected ? <Check className="h-3.5 w-3.5" aria-hidden /> : null}
                </button>
              );
            })}
          </div>
          <p className="mt-2 text-xs text-muted">
            Status colours — positive, warning, danger — stay fixed regardless of your accent,
            so a warning always looks like a warning.
          </p>
        </div>
      </CardContent>
    </Card>
  );
}

function Profile({ me }: { me: Me | undefined }) {
  const queryClient = useQueryClient();
  const [name, setName] = useState('');
  const [title, setTitle] = useState('');

  const save = useMutation({
    mutationFn: () =>
      patch('/api/me/preferences', {
        ...(name ? { name } : {}),
        ...(title ? { title } : {}),
      }),
    onSuccess: () => {
      void queryClient.invalidateQueries({ queryKey: ['me'] });
      setName('');
      setTitle('');
      // The form clears itself on success, which without confirmation reads as
      // though the input was discarded rather than saved.
      toast.success('Profile saved');
    },
    onError: () => toast.error('Could not save your profile'),
  });

  if (!me) return null;

  return (
    <Card>
      <CardHeader>
        <CardTitle className="text-base">Profile</CardTitle>
      </CardHeader>
      <CardContent className="space-y-4">
        <dl className="grid gap-2 text-sm sm:grid-cols-2">
          <div>
            <dt className="text-xs text-muted">Name</dt>
            <dd>{me.name}</dd>
          </div>
          <div>
            <dt className="text-xs text-muted">Email</dt>
            <dd className="break-all">{me.email}</dd>
          </div>
          <div className="sm:col-span-2">
            <dt className="text-xs text-muted">Teams</dt>
            <dd className="mt-1 flex flex-wrap gap-1.5">
              {me.teams.length === 0 ? (
                <span className="text-muted">No team membership</span>
              ) : (
                me.teams.map((t) => (
                  <Badge key={t.team} tone="accent">
                    {t.team} · {t.role}
                  </Badge>
                ))
              )}
              {me.isPlatformAdmin ? <Badge tone="warning">Platform admin</Badge> : null}
            </dd>
          </div>
        </dl>

        <form
          className="grid gap-3 sm:grid-cols-2"
          onSubmit={(event) => {
            event.preventDefault();
            save.mutate();
          }}
        >
          <label className="block" htmlFor="profile-display-name">
            <span className="mb-1 block text-xs font-medium text-muted">Display name</span>
            <Input id="profile-display-name" name="displayName" value={name} onChange={(e) => setName(e.target.value)} placeholder={me.name} />
          </label>
          <label className="block" htmlFor="profile-title">
            <span className="mb-1 block text-xs font-medium text-muted">Title</span>
            <Input
              id="profile-title"
              name="title"
              value={title}
              onChange={(e) => setTitle(e.target.value)}
              placeholder="Head of Compute"
            />
          </label>
          <div className="sm:col-span-2">
            <Button
              type="submit"
              variant="primary"
              disabled={save.isPending || (!name && !title)}
            >
              {save.isPending ? 'Saving…' : 'Save profile'}
            </Button>
          </div>
        </form>
      </CardContent>
    </Card>
  );
}

function ConnectAgent() {
  const origin = typeof window !== 'undefined' ? window.location.origin : 'https://your-pig-host';

  return (
    <Card>
      <CardHeader>
        <div className="flex items-center gap-2">
          <Terminal className="h-4 w-4 text-accent-fg" aria-hidden />
          <CardTitle className="text-base">Connect your agent</CardTitle>
        </div>
        <p className="text-sm text-muted">
          PIG speaks MCP, so Claude Code, Codex, prime-agent and Buzz agents can all work with
          your pipeline directly from the terminal.
        </p>
      </CardHeader>
      <CardContent className="space-y-3">
        <div className="scroll-x rounded-lg bg-surface-2 p-3">
          <pre className="text-xs leading-relaxed">
            <code>{`export PIG_URL=${origin}
export PIG_API_KEY=pig_...      # create one below

claude mcp add pig -- npx -y @pig/mcp`}</code>
          </pre>
        </div>
        <p className="text-xs text-muted">
          The agent authenticates as its own principal, separately revocable from your own
          session, and can never reach further than you can.
        </p>
      </CardContent>
    </Card>
  );
}

/**
 * Signing out.
 *
 * There was no way to do this at all until now, which is an easy omission to
 * make when developing signed in and a genuinely stranded feeling for anyone
 * on a shared machine.
 *
 * Clearing the identity provider's session is enough: PIG holds no session of
 * its own, and the auth listener in App.tsx notices and returns to sign-in
 * without a reload.
 */
function SessionCard() {
  const [busy, setBusy] = useState(false);

  async function signOut() {
    setBusy(true);
    try {
      await getSupabase()?.auth.signOut();
    } finally {
      // Belt and braces: if the provider call fails, a reload still lands on
      // the sign-in screen rather than leaving a half-signed-out interface.
      window.location.href = '/';
    }
  }

  return (
    <Card>
      <CardHeader>
        <CardTitle className="text-base">Session</CardTitle>
        <p className="text-sm text-muted">
          Signing out clears this browser only. API keys you have issued keep working —
          revoke those separately.
        </p>
      </CardHeader>
      <CardContent>
        <Button type="button" variant="outline" onClick={signOut} disabled={busy}>
          <LogOut className="h-4 w-4" aria-hidden />
          {busy ? 'Signing out…' : 'Sign out'}
        </Button>
      </CardContent>
    </Card>
  );
}
