import type { ReactNode } from 'react';
import { useState } from 'react';
import { Loader2, Plug, ShieldCheck } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Textarea } from '@/components/ui/textarea';
import { useMaintenance, useSession } from './useTransmissionData';

// The daemon's settings, as one form with a draft.
//
// Only the fields you actually changed are sent — session-set is a partial update, and posting the whole
// object back would race the 5-second poll and re-apply stale values you never touched. The sidecar reads
// the settings back after writing because Transmission clamps several of them, so the form snaps to what
// the daemon really holds rather than to what you typed.

export const SettingsView = () => {
  const { session, isLoading, save } = useSession();
  const { portTest, updateBlocklist } = useMaintenance();
  const [draft, setDraft] = useState<Record<string, unknown>>({});

  if (isLoading && !session) {
    return (
      <div className="flex h-full items-center justify-center text-sm text-muted-foreground">
        <Loader2 className="mr-2 h-4 w-4 animate-spin" />
        Loading settings…
      </div>
    );
  }

  if (!session) {
    return (
      <div className="flex h-full items-center justify-center text-sm text-muted-foreground">
        Could not read the session settings.
      </div>
    );
  }

  const get = <T,>(key: string, fallback: T): T =>
    key in draft ? (draft[key] as T) : ((session[key] as T) ?? fallback);
  const set = (key: string, value: unknown) => setDraft((prev) => ({ ...prev, [key]: value }));

  const dirty = Object.keys(draft).length > 0;

  const apply = async () => {
    await save.mutateAsync(draft);
    setDraft({});
  };

  const bool = (key: string, label: string, hint?: string) => (
    <Toggle checked={get(key, false)} onChange={(v) => set(key, v)} label={label} hint={hint} />
  );

  const number = (key: string, label: string, unit?: string, min = 0) => (
    <Field label={label} unit={unit}>
      <Input
        type="number"
        min={min}
        value={String(get(key, 0))}
        onChange={(ev) => set(key, Number(ev.target.value))}
        className="w-32"
      />
    </Field>
  );

  const text = (key: string, label: string, placeholder?: string) => (
    <div className="space-y-1.5">
      <Label htmlFor={`tr-${key}`}>{label}</Label>
      <Input
        id={`tr-${key}`}
        value={String(get(key, ''))}
        placeholder={placeholder}
        onChange={(ev) => set(key, ev.target.value)}
        className="font-mono text-xs"
      />
    </div>
  );

  return (
    <div className="flex h-full min-h-0 flex-col">
      <Tabs defaultValue="download" className="flex min-h-0 flex-1 flex-col">
        <TabsList className="mx-4 mt-3 w-fit shrink-0">
          <TabsTrigger value="download">Downloads</TabsTrigger>
          <TabsTrigger value="bandwidth">Bandwidth</TabsTrigger>
          <TabsTrigger value="network">Network</TabsTrigger>
          <TabsTrigger value="queue">Queue</TabsTrigger>
          <TabsTrigger value="other">Other</TabsTrigger>
        </TabsList>

        <div className="min-h-0 flex-1 overflow-y-auto p-4">
          <TabsContent value="download" className="mt-0 space-y-5">
            <Section title="Location">
              {text('download-dir', 'Download directory', '/downloads')}
              {bool('incomplete-dir-enabled', 'Keep incomplete files somewhere else')}
              {get('incomplete-dir-enabled', false) && text('incomplete-dir', 'Incomplete directory')}
              {bool(
                'rename-partial-files',
                'Append .part to incomplete files',
                'Stops media scanners picking up half-downloaded files.',
              )}
            </Section>

            <Section title="When adding">
              {bool('start-added-torrents', 'Start torrents as soon as they are added')}
              {bool('trash-original-torrent-files', 'Delete the .torrent file after adding')}
            </Section>

            <Section title="Seeding limits">
              {bool('seedRatioLimited', 'Stop seeding at a ratio')}
              {get('seedRatioLimited', false) && (
                <Field label="Ratio">
                  <Input
                    type="number"
                    step="0.1"
                    min={0}
                    value={String(get('seedRatioLimit', 2))}
                    onChange={(ev) => set('seedRatioLimit', Number(ev.target.value))}
                    className="w-32"
                  />
                </Field>
              )}
              {bool('idle-seeding-limit-enabled', 'Stop seeding when idle')}
              {get('idle-seeding-limit-enabled', false) && number('idle-seeding-limit', 'Idle for', 'minutes')}
            </Section>
          </TabsContent>

          <TabsContent value="bandwidth" className="mt-0 space-y-5">
            <Section title="Speed limits">
              {bool('speed-limit-down-enabled', 'Limit download speed')}
              {get('speed-limit-down-enabled', false) && number('speed-limit-down', 'Download', 'kB/s')}
              {bool('speed-limit-up-enabled', 'Limit upload speed')}
              {get('speed-limit-up-enabled', false) && number('speed-limit-up', 'Upload', 'kB/s')}
            </Section>

            <Section
              title="Alternative speed limits"
              hint="The turtle button in the toolbar switches to these without touching the limits above."
            >
              {bool('alt-speed-enabled', 'Use the alternative limits now')}
              {number('alt-speed-down', 'Download', 'kB/s')}
              {number('alt-speed-up', 'Upload', 'kB/s')}
              {bool('alt-speed-time-enabled', 'Switch automatically on a schedule')}
              {get('alt-speed-time-enabled', false) && (
                <>
                  <TimeField
                    label="From"
                    minutes={get('alt-speed-time-begin', 0)}
                    onChange={(v) => set('alt-speed-time-begin', v)}
                  />
                  <TimeField
                    label="To"
                    minutes={get('alt-speed-time-end', 0)}
                    onChange={(v) => set('alt-speed-time-end', v)}
                  />
                  <DayPicker mask={get('alt-speed-time-day', 127)} onChange={(v) => set('alt-speed-time-day', v)} />
                </>
              )}
            </Section>
          </TabsContent>

          <TabsContent value="network" className="mt-0 space-y-5">
            <Section title="Listening port">
              {number('peer-port', 'Peer port', undefined, 1)}
              {bool('peer-port-random-on-start', 'Pick a random port on each start')}
              {bool('port-forwarding-enabled', 'Ask the router to forward it (UPnP / NAT-PMP)')}
              <div>
                <Button size="sm" variant="outline" onClick={() => portTest.mutate()} disabled={portTest.isPending}>
                  {portTest.isPending ? (
                    <Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" />
                  ) : (
                    <Plug className="mr-1.5 h-3.5 w-3.5" />
                  )}
                  Test port
                </Button>
              </div>
            </Section>

            <Section title="Peers">
              {number('peer-limit-global', 'Maximum peers overall', undefined, 1)}
              {number('peer-limit-per-torrent', 'Maximum peers per torrent', undefined, 1)}
              <Field label="Encryption">
                <Select value={String(get('encryption', 'preferred'))} onValueChange={(v) => set('encryption', v)}>
                  <SelectTrigger className="w-48">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="tolerated">Allow unencrypted</SelectItem>
                    <SelectItem value="preferred">Prefer encryption</SelectItem>
                    <SelectItem value="required">Require encryption</SelectItem>
                  </SelectContent>
                </Select>
              </Field>
            </Section>

            <Section title="Peer discovery" hint="Private torrents ignore all of these by design.">
              {bool('pex-enabled', 'Peer exchange (PEX)')}
              {bool('dht-enabled', 'Distributed hash table (DHT)')}
              {bool('lpd-enabled', 'Local peer discovery')}
              {bool('utp-enabled', 'µTP — yields to other traffic on your connection')}
            </Section>

            <Section title="Blocklist">
              {bool('blocklist-enabled', 'Use a blocklist')}
              {text('blocklist-url', 'Blocklist URL')}
              <div className="flex items-center gap-3">
                <Button
                  size="sm"
                  variant="outline"
                  onClick={() => updateBlocklist.mutate()}
                  disabled={updateBlocklist.isPending}
                >
                  {updateBlocklist.isPending ? (
                    <Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" />
                  ) : (
                    <ShieldCheck className="mr-1.5 h-3.5 w-3.5" />
                  )}
                  Update now
                </Button>
                <span className="text-xs text-muted-foreground">
                  {Number(session['blocklist-size'] ?? 0).toLocaleString()} rules loaded
                </span>
              </div>
            </Section>
          </TabsContent>

          <TabsContent value="queue" className="mt-0 space-y-5">
            <Section title="Download queue">
              {bool('download-queue-enabled', 'Limit how many torrents download at once')}
              {get('download-queue-enabled', false) && number('download-queue-size', 'At most', 'torrents', 1)}
            </Section>
            <Section title="Seed queue">
              {bool('seed-queue-enabled', 'Limit how many torrents seed at once')}
              {get('seed-queue-enabled', false) && number('seed-queue-size', 'At most', 'torrents', 1)}
            </Section>
            <Section
              title="Stalled torrents"
              hint="A stalled torrent gives up its queue slot so something else can run."
            >
              {bool('queue-stalled-enabled', 'Treat torrents with no activity as stalled')}
              {get('queue-stalled-enabled', false) && number('queue-stalled-minutes', 'After', 'minutes', 1)}
            </Section>
          </TabsContent>

          <TabsContent value="other" className="mt-0 space-y-5">
            <Section title="Performance">{number('cache-size-mb', 'Disk cache', 'MB')}</Section>

            <Section title="On completion">
              {bool('script-torrent-done-enabled', 'Run a script when a torrent finishes')}
              {get('script-torrent-done-enabled', false) &&
                text('script-torrent-done-filename', 'Script path', '/etc/transmission/done.sh')}
            </Section>

            <Section
              title="Default trackers"
              hint="Added to every public torrent. One URL per line, blank line between tiers."
            >
              <Textarea
                rows={6}
                value={String(get('default-trackers', ''))}
                onChange={(ev) => set('default-trackers', ev.target.value)}
                className="font-mono text-xs"
              />
            </Section>

            <Section title="About">
              <dl className="grid gap-x-6 gap-y-1.5 text-xs sm:grid-cols-2">
                <About label="Version" value={String(session.version ?? '—')} />
                <About label="RPC version" value={String(session['rpc-version'] ?? '—')} />
                <About label="Config directory" value={String(session['config-dir'] ?? '—')} />
                <About label="Session ID" value={String(session['session-id'] ?? '—')} />
              </dl>
            </Section>
          </TabsContent>
        </div>
      </Tabs>

      {/* The bar only exists when there is something to save — a permanently-visible disabled Save button
          teaches you to ignore it. */}
      {dirty && (
        <div className="flex shrink-0 items-center gap-3 border-t border-border bg-muted/40 px-4 py-2">
          <span className="flex-1 text-xs text-muted-foreground">
            {Object.keys(draft).length} unsaved change{Object.keys(draft).length === 1 ? '' : 's'}
          </span>
          <Button size="sm" variant="ghost" onClick={() => setDraft({})}>
            Discard
          </Button>
          <Button size="sm" onClick={apply} disabled={save.isPending}>
            {save.isPending && <Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" />}
            Save
          </Button>
        </div>
      )}
    </div>
  );
};

const Section = ({ title, hint, children }: { title: string; hint?: string; children: ReactNode }) => (
  <section className="max-w-2xl rounded-xl border border-border p-4">
    <h3 className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">{title}</h3>
    {hint && <p className="mt-0.5 text-xs text-muted-foreground">{hint}</p>}
    <div className="mt-3 space-y-3">{children}</div>
  </section>
);

const Toggle = ({
  checked,
  onChange,
  label,
  hint,
}: {
  checked: boolean;
  onChange: (value: boolean) => void;
  label: string;
  hint?: string;
}) => (
  <label className="flex items-start gap-2.5 text-sm">
    <Checkbox className="mt-0.5" checked={checked} onCheckedChange={(v) => onChange(v === true)} />
    <span>
      {label}
      {hint && <span className="mt-0.5 block text-xs text-muted-foreground">{hint}</span>}
    </span>
  </label>
);

const Field = ({ label, unit, children }: { label: string; unit?: string; children: ReactNode }) => (
  <div className="flex items-center gap-3 text-sm">
    <span className="w-56 shrink-0">{label}</span>
    {children}
    {unit && <span className="text-xs text-muted-foreground">{unit}</span>}
  </div>
);

/** Transmission stores the alt-speed schedule as minutes past midnight; the browser wants HH:MM. */
const TimeField = ({
  label,
  minutes,
  onChange,
}: {
  label: string;
  minutes: number;
  onChange: (minutes: number) => void;
}) => {
  const pad = (n: number) => String(n).padStart(2, '0');
  const value = `${pad(Math.floor(minutes / 60))}:${pad(minutes % 60)}`;
  return (
    <Field label={label}>
      <Input
        type="time"
        value={value}
        onChange={(ev) => {
          const [h, m] = ev.target.value.split(':').map(Number);
          if (Number.isFinite(h) && Number.isFinite(m)) onChange(h! * 60 + m!);
        }}
        className="w-32"
      />
    </Field>
  );
};

// alt-speed-time-day is a bitfield, Sunday = bit 0 (rpc-spec.md). 127 is every day.
const DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

const DayPicker = ({ mask, onChange }: { mask: number; onChange: (mask: number) => void }) => (
  <Field label="On days">
    <div className="flex gap-1">
      {DAYS.map((day, i) => {
        const bit = 1 << i;
        const on = (mask & bit) !== 0;
        return (
          <button
            key={day}
            type="button"
            onClick={() => onChange(on ? mask & ~bit : mask | bit)}
            className={`rounded px-2 py-1 text-xs transition-colors ${
              on ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground hover:bg-muted/70'
            }`}
          >
            {day}
          </button>
        );
      })}
    </div>
  </Field>
);

const About = ({ label, value }: { label: string; value: string }) => (
  <div className="flex min-w-0 items-baseline gap-3">
    <dt className="w-32 shrink-0 text-muted-foreground">{label}</dt>
    <dd className="min-w-0 flex-1 truncate font-mono" title={value}>
      {value}
    </dd>
  </div>
);
