'use client';

import { Fragment, useEffect, useMemo, useState } from 'react';
import { Button, Listbox, ListboxButton, ListboxOption, ListboxOptions, Transition } from '@headlessui/react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import toast from 'react-hot-toast';
import { ChevronUpDownIcon, CheckIcon } from '@/components/icons/Icons';
import {
  Badge,
  Section,
  ToggleRow,
  buttonClass,
  inputClass,
  listboxButtonClass,
  listboxOptionClass,
  listboxOptionsClass,
} from '@/components/formPrimitives';
import { type TtsProviderId } from '@/lib/shared/tts-provider-catalog';
import { useSharedProviders, type SharedProviderEntry } from '@/hooks/useSharedProviders';

type RuntimeConfigSource = 'env-seed' | 'admin' | 'default';

interface SettingsResponse {
  values: Record<string, unknown>;
  sources: Record<string, RuntimeConfigSource>;
}

interface ProviderOption {
  id: string;
  name: string;
  providerType: TtsProviderId;
}

const ADMIN_SETTINGS_QUERY_KEY = ['admin-settings'] as const;

async function fetchAdminSettings(): Promise<SettingsResponse> {
  const res = await fetch('/api/admin/settings');
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return (await res.json()) as SettingsResponse;
}

async function patchAdminSettings(payload: { updates?: Record<string, unknown>; reset?: string[] }): Promise<void> {
  const res = await fetch('/api/admin/settings', {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });
  if (!res.ok && res.status !== 207) throw new Error(`HTTP ${res.status}`);
}

export function AdminFeaturesPanel() {
  const queryClient = useQueryClient();
  const { data, error } = useQuery({
    queryKey: ADMIN_SETTINGS_QUERY_KEY,
    queryFn: fetchAdminSettings,
  });
  const [draft, setDraft] = useState<Record<string, unknown>>({});
  const [dirty, setDirty] = useState<Set<string>>(new Set());
  const { providers: sharedProviders } = useSharedProviders();

  useEffect(() => {
    if (!data) return;
    setDraft({ ...data.values });
    setDirty(new Set());
  }, [data]);

  useEffect(() => {
    if (!error) return;
    console.error('[AdminFeaturesPanel] load failed:', error);
    toast.error('Failed to load site settings');
  }, [error]);

  const resetMutation = useMutation({
    mutationFn: async (key: string) => {
      await patchAdminSettings({ reset: [key] });
    },
    onSuccess: async () => {
      toast.success('Reset to env default');
      await queryClient.invalidateQueries({ queryKey: ADMIN_SETTINGS_QUERY_KEY });
    },
    onError: (mutationError) => {
      console.error(mutationError);
      toast.error('Reset failed');
    },
  });

  const saveMutation = useMutation({
    mutationFn: async (updates: Record<string, unknown>) => {
      await patchAdminSettings({ updates });
    },
    onSuccess: async () => {
      toast.success('Settings saved');
      await queryClient.invalidateQueries({ queryKey: ADMIN_SETTINGS_QUERY_KEY });
    },
    onError: (mutationError) => {
      console.error(mutationError);
      toast.error('Save failed');
    },
  });

  const saving = resetMutation.isPending || saveMutation.isPending;

  const updateDraft = (key: string, value: unknown) => {
    setDraft((d) => ({ ...d, [key]: value }));
    setDirty((s) => {
      const next = new Set(s);
      next.add(key);
      return next;
    });
  };

  const resetField = (key: string) => {
    if (saving) return;
    resetMutation.mutate(key);
  };

  const saveAll = () => {
    if (saving || dirty.size === 0) return;
    const updates: Record<string, unknown> = {};
    for (const key of dirty) updates[key] = draft[key];
    saveMutation.mutate(updates);
  };

  const discardAll = () => {
    if (!data) return;
    setDraft({ ...data.values });
    setDirty(new Set());
  };

  const providerOptions = useMemo<ProviderOption[]>(() => {
    return sharedProviders.map((entry) => ({
      id: entry.slug,
      name: `${entry.displayName} (shared)`,
      providerType: entry.providerType,
    }));
  }, [sharedProviders]);

  const currentProviderId =
    typeof draft.defaultTtsProvider === 'string'
      ? draft.defaultTtsProvider
      : '';
  const currentSharedEntry: SharedProviderEntry | undefined = sharedProviders.find(
    (p) => p.slug === currentProviderId,
  );
  const fallbackShared = providerOptions[0];
  const effectiveSelectedProvider = currentSharedEntry
    ? {
      id: currentSharedEntry.slug,
      name: `${currentSharedEntry.displayName} (shared)`,
      providerType: currentSharedEntry.providerType,
    } as ProviderOption
    : fallbackShared;
  const selectedProviderOption = effectiveSelectedProvider;

  const handleProviderChange = (opt: ProviderOption) => {
    updateDraft('defaultTtsProvider', opt.id);
  };

  const renderSource = (key: string) => {
    const source = data?.sources?.[key] ?? 'default';
    const isDirty = dirty.has(key);
    return (
      <SourceBadge
        source={source}
        dirty={isDirty}
        canReset={source !== 'default'}
        onReset={() => resetField(key)}
        saving={saving}
      />
    );
  };

  if (!data) {
    return (
      <AdminFeaturesSkeleton />
    );
  }

  return (
    <div className="space-y-4">
      <Section
        title="TTS defaults"
        subtitle="Defaults for new users."
        action={<Badge tone="foreground">Defaults</Badge>}
      >
        <div className="space-y-1.5 pb-2 border-b border-offbase">
          <div className="flex items-start justify-between gap-3">
            <div className="min-w-0">
              <p className="text-sm font-medium text-foreground">Default TTS provider</p>
              <p className="text-xs text-muted mt-0.5">
                Starting provider for new users.
              </p>
            </div>
            <div className="shrink-0">{renderSource('defaultTtsProvider')}</div>
          </div>
          {providerOptions.length > 0 ? (
            <Listbox value={selectedProviderOption} onChange={handleProviderChange}>
              <ListboxButton className={listboxButtonClass}>
                <span className="block truncate">{selectedProviderOption?.name ?? 'Select provider'}</span>
                <span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
                  <ChevronUpDownIcon className="h-4 w-4 text-muted" />
                </span>
              </ListboxButton>
              <Transition
                as={Fragment}
                leave="transition ease-in duration-100"
                leaveFrom="opacity-100"
                leaveTo="opacity-0"
              >
                <ListboxOptions anchor="bottom start" className={listboxOptionsClass}>
                  {providerOptions.map((opt) => (
                    <ListboxOption
                      key={opt.id}
                      value={opt}
                      className={({ active }) => listboxOptionClass(active)}
                    >
                      {({ selected }) => (
                        <>
                          <span className={`block truncate ${selected ? 'font-medium' : 'font-normal'}`}>
                            {opt.name}
                          </span>
                          {selected && (
                            <span className="absolute inset-y-0 left-0 flex items-center pl-3 text-accent">
                              <CheckIcon className="h-5 w-5" />
                            </span>
                          )}
                        </>
                      )}
                    </ListboxOption>
                  ))}
                </ListboxOptions>
              </Transition>
            </Listbox>
          ) : (
            <div className="px-0.5 py-2 text-sm text-muted">
              No shared providers yet. Add one first.
            </div>
          )}
        </div>

        <ToggleRow
          label="Restrict user API keys (recommended)"
          description="Only allow admin shared providers."
          checked={Boolean(draft.restrictUserApiKeys)}
          onChange={(checked) => {
            if (!checked) {
              const ok = confirm(
                'Turning this off allows user-supplied API keys to flow through this server. Continue?',
              );
              if (!ok) return;
            }
            updateDraft('restrictUserApiKeys', checked);
          }}
          right={renderSource('restrictUserApiKeys')}
          variant="flat"
        />
        <ToggleRow
          label="Show TTS provider settings tab"
          description="Allow per-user provider overrides."
          checked={Boolean(draft.enableTtsProvidersTab)}
          onChange={(checked) => updateDraft('enableTtsProvidersTab', checked)}
          right={renderSource('enableTtsProvidersTab')}
          variant="flat"
        />
        <ToggleRow
          label="Show all provider models"
          description="Allow model selection beyond defaults."
          checked={Boolean(draft.showAllProviderModels)}
          onChange={(checked) => updateDraft('showAllProviderModels', checked)}
          right={renderSource('showAllProviderModels')}
          variant="flat"
        />
      </Section>

      <Section
        title="Site features"
        subtitle="Feature flags for all users."
        action={<Badge tone="foreground">Feature Flags</Badge>}
      >
        <div className="space-y-1.5 pb-2 border-b border-offbase">
          <div className="flex items-start justify-between gap-3">
            <div className="min-w-0">
              <p className="text-sm font-medium text-foreground">Changelog feed URL</p>
              <p className="text-xs text-muted mt-0.5">
                Public URL to the changelog manifest JSON used by Settings.
              </p>
            </div>
            <div className="shrink-0">{renderSource('changelogFeedUrl')}</div>
          </div>
          <input
            type="text"
            className={inputClass}
            value={String(draft.changelogFeedUrl ?? '')}
            onChange={(event) => updateDraft('changelogFeedUrl', event.target.value)}
            placeholder="https://docs.openreader.richardr.dev/changelog/manifest.json"
          />
        </div>
        <ToggleRow
          label="Allow new account sign-ups"
          description="When off, new accounts cannot be created. Existing accounts can still sign in."
          checked={Boolean(draft.enableUserSignups)}
          onChange={(checked) => updateDraft('enableUserSignups', checked)}
          right={renderSource('enableUserSignups')}
          variant="flat"
        />
        <ToggleRow
          label="Audiobook export"
          description='Show "Export audiobook" on PDF/EPUB pages.'
          checked={Boolean(draft.enableAudiobookExport)}
          onChange={(checked) => updateDraft('enableAudiobookExport', checked)}
          right={renderSource('enableAudiobookExport')}
          variant="flat"
        />
        <ToggleRow
          label="DOCX upload conversion"
          description="Allow DOCX uploads (converted to PDF)."
          checked={Boolean(draft.enableDocxConversion)}
          onChange={(checked) => updateDraft('enableDocxConversion', checked)}
          right={renderSource('enableDocxConversion')}
          variant="flat"
        />
        <ToggleRow
          label="Destructive delete buttons"
          description='Show "Delete all data" actions (auth-off mode).'
          checked={Boolean(draft.enableDestructiveDeleteActions)}
          onChange={(checked) => updateDraft('enableDestructiveDeleteActions', checked)}
          right={renderSource('enableDestructiveDeleteActions')}
          variant="flat"
        />
      </Section>

      <div className="flex items-center justify-between gap-3">
        <p className="text-xs text-muted">
          {dirty.size > 0
            ? `${dirty.size} unsaved change${dirty.size === 1 ? '' : 's'}`
            : 'No unsaved changes'}
        </p>
        <div className="flex gap-2">
          <Button
            onClick={discardAll}
            disabled={dirty.size === 0 || saving}
            className={buttonClass({ variant: 'secondary', size: 'sm' })}
          >
            Discard
          </Button>
          <Button
            onClick={saveAll}
            disabled={dirty.size === 0 || saving}
            className={buttonClass({ variant: 'primary', size: 'sm' })}
          >
            {saving ? 'Saving…' : dirty.size > 0 ? `Save (${dirty.size})` : 'Save'}
          </Button>
        </div>
      </div>
    </div>
  );
}

function AdminFeaturesSkeleton() {
  return (
    <div className="space-y-4 animate-pulse" aria-label="Loading feature settings" aria-busy="true">
      <Section
        title="TTS defaults"
        subtitle="Defaults for new users."
        action={<div className="h-4 w-16 rounded bg-offbase" />}
      >
        <div className="space-y-1.5 pb-2 border-b border-offbase">
          <div className="flex items-start justify-between gap-3">
            <div className="space-y-1 min-w-0">
              <div className="h-4 w-40 rounded bg-offbase" />
              <div className="h-3 w-56 rounded bg-offbase" />
            </div>
            <div className="h-5 w-20 rounded bg-offbase" />
          </div>
          <div className="h-9 w-full rounded-md bg-offbase" />
        </div>
        <div className="space-y-2">
          <div className="h-14 w-full rounded-md border border-offbase bg-background" />
          <div className="h-14 w-full rounded-md border border-offbase bg-background" />
          <div className="h-14 w-full rounded-md border border-offbase bg-background" />
        </div>
      </Section>

      <Section
        title="Site features"
        subtitle="Feature flags for all users."
        action={<div className="h-4 w-24 rounded bg-offbase" />}
      >
        <div className="space-y-2">
          <div className="h-14 w-full rounded-md border border-offbase bg-background" />
          <div className="h-14 w-full rounded-md border border-offbase bg-background" />
          <div className="h-14 w-full rounded-md border border-offbase bg-background" />
        </div>
      </Section>
    </div>
  );
}

function SourceBadge({
  source,
  dirty,
  canReset,
  onReset,
  saving,
}: {
  source: RuntimeConfigSource;
  dirty: boolean;
  canReset: boolean;
  onReset: () => void;
  saving: boolean;
}) {
  return (
    <div className="flex items-center gap-1.5">
      {canReset && !dirty && (
        <button
          type="button"
          onClick={onReset}
          disabled={saving}
          className="text-[11px] font-medium text-muted hover:text-accent transition-colors disabled:opacity-50"
        >
          Reset
        </button>
      )}
      {dirty ? (
        <Badge tone="accent">Modified</Badge>
      ) : source === 'env-seed' ? (
        <Badge tone="muted">from env</Badge>
      ) : source === 'admin' ? (
        <Badge tone="foreground">admin</Badge>
      ) : (
        <Badge tone="muted">default</Badge>
      )}
    </div>
  );
}
