'use client';

import { useCallback, useEffect, useMemo, useState } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import { Plus, Save, Trash2, GripVertical, RotateCcw, Loader2 } from 'lucide-react';

import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { PageHeader } from '@/components/shared/page-header';
import { apiFetch } from '@/lib/api/client';
import { VOCABULARIES, type VocabularyDef, type VocabularyKey } from '@/lib/vocabularies';

interface SettingRow {
  key: string;
  value: unknown;
  portId: string | null;
  updatedAt: string;
}

interface VocabState {
  /** Working copy of the entries the admin is editing. */
  entries: string[];
  /** True when entries differ from the loaded server value. */
  dirty: boolean;
  /** Server value as last loaded (so Reset / dirty-check have something to compare against). */
  loaded: string[];
  /** The last in-progress add value (so the typing buffer survives re-renders). */
  newEntry: string;
}

const DOMAINS: Array<VocabularyDef['domain']> = ['Interests', 'Berths', 'Expenses', 'Documents'];

function arraysEqual(a: readonly string[], b: readonly string[]): boolean {
  if (a.length !== b.length) return false;
  return a.every((v, i) => v === b[i]);
}

export function VocabulariesManager() {
  const queryClient = useQueryClient();
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState<VocabularyKey | null>(null);
  const [state, setState] = useState<Record<VocabularyKey, VocabState>>(() => {
    const initial: Partial<Record<VocabularyKey, VocabState>> = {};
    for (const def of VOCABULARIES) {
      const defaults = [...def.defaults];
      initial[def.key] = { entries: defaults, loaded: defaults, dirty: false, newEntry: '' };
    }
    return initial as Record<VocabularyKey, VocabState>;
  });

  const fetchAll = useCallback(async () => {
    setLoading(true);
    try {
      const res = await apiFetch<{ data: { portSettings: SettingRow[] } }>(
        '/api/v1/admin/settings',
      );
      const byKey = new Map<string, unknown>();
      for (const row of res.data.portSettings) byKey.set(row.key, row.value);
      setState((prev) => {
        const next = { ...prev };
        for (const def of VOCABULARIES) {
          const remote = byKey.get(def.key);
          // Only adopt the remote value when it parses as a string array;
          // anything else (legacy malformed rows, accidental object) falls
          // back to the shipped defaults so the editor never starts in a
          // broken state.
          const remoteList = Array.isArray(remote)
            ? remote.filter((v): v is string => typeof v === 'string')
            : null;
          const entries = remoteList && remoteList.length > 0 ? remoteList : [...def.defaults];
          next[def.key] = {
            entries,
            loaded: entries,
            dirty: false,
            newEntry: '',
          };
        }
        return next;
      });
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    // Initial vocabularies load on mount.
    // eslint-disable-next-line react-hooks/set-state-in-effect
    void fetchAll();
  }, [fetchAll]);

  const grouped = useMemo(() => {
    const map = new Map<VocabularyDef['domain'], VocabularyDef[]>();
    for (const d of DOMAINS) map.set(d, []);
    for (const def of VOCABULARIES) map.get(def.domain)?.push(def);
    return map;
  }, []);

  function patch(key: VocabularyKey, partial: Partial<VocabState>) {
    setState((prev) => {
      const current = prev[key];
      const next = { ...current, ...partial };
      next.dirty = !arraysEqual(next.entries, current.loaded);
      return { ...prev, [key]: next };
    });
  }

  function addEntry(key: VocabularyKey) {
    const current = state[key];
    const value = current.newEntry.trim();
    if (!value) return;
    if (current.entries.some((e) => e.toLowerCase() === value.toLowerCase())) {
      // Don't allow exact-case-insensitive duplicates.
      patch(key, { newEntry: '' });
      return;
    }
    patch(key, { entries: [...current.entries, value], newEntry: '' });
  }

  function removeEntry(key: VocabularyKey, index: number) {
    const current = state[key];
    patch(key, { entries: current.entries.filter((_, i) => i !== index) });
  }

  function moveEntry(key: VocabularyKey, from: number, direction: -1 | 1) {
    const current = state[key];
    const to = from + direction;
    if (to < 0 || to >= current.entries.length) return;
    const entries = [...current.entries];
    const [moved] = entries.splice(from, 1);
    entries.splice(to, 0, moved!);
    patch(key, { entries });
  }

  function updateEntry(key: VocabularyKey, index: number, value: string) {
    const current = state[key];
    const entries = current.entries.map((e, i) => (i === index ? value : e));
    patch(key, { entries });
  }

  async function save(key: VocabularyKey) {
    setSaving(key);
    try {
      const value = state[key].entries.map((e) => e.trim()).filter((e) => e.length > 0);
      await apiFetch('/api/v1/admin/settings', {
        method: 'PUT',
        body: { key, value },
      });
      patch(key, { entries: value, loaded: value, newEntry: '' });
      // Drop the in-app vocabularies cache so consumers (status-change
      // dialog, expense form, etc.) pick up the new list immediately.
      void queryClient.invalidateQueries({ queryKey: ['vocabularies'] });
    } finally {
      setSaving(null);
    }
  }

  function resetToDefaults(key: VocabularyKey) {
    const def = VOCABULARIES.find((v) => v.key === key);
    if (!def) return;
    patch(key, { entries: [...def.defaults], newEntry: '' });
  }

  if (loading) {
    return (
      <div>
        <PageHeader title="Vocabularies" description="Per-port pick lists used across the CRM." />
        <div className="flex items-center justify-center py-12 text-muted-foreground">
          <Loader2 className="mr-2 h-4 w-4 animate-spin" /> Loading…
        </div>
      </div>
    );
  }

  return (
    <div>
      <PageHeader
        title="Vocabularies"
        description="Per-port pick lists used across the CRM. Override the shipped defaults so reps see the wording your team actually uses. Changes apply immediately for this port; Reset restores the defaults the CRM ships with."
      />

      <div className="mt-6 space-y-8">
        {DOMAINS.map((domain) => {
          const defs = grouped.get(domain) ?? [];
          if (defs.length === 0) return null;
          return (
            <section key={domain} className="space-y-4">
              <h2 className="text-lg font-semibold">{domain}</h2>
              <div className="space-y-4">
                {defs.map((def) => {
                  const v = state[def.key];
                  return (
                    <Card key={def.key}>
                      <CardHeader>
                        <div className="flex items-start justify-between gap-3">
                          <div>
                            <CardTitle className="text-base">{def.label}</CardTitle>
                            <CardDescription>{def.description}</CardDescription>
                            <p className="mt-1 text-xs text-muted-foreground font-mono">
                              {def.key}
                            </p>
                          </div>
                          <div className="flex shrink-0 items-center gap-2">
                            <Button
                              size="sm"
                              variant="ghost"
                              onClick={() => resetToDefaults(def.key)}
                              disabled={saving === def.key}
                              title="Restore the shipped defaults"
                            >
                              <RotateCcw className="mr-1 h-3.5 w-3.5" />
                              Reset
                            </Button>
                            <Button
                              size="sm"
                              onClick={() => save(def.key)}
                              disabled={!v.dirty || saving === def.key}
                            >
                              {saving === def.key ? (
                                <Loader2 className="mr-1 h-3.5 w-3.5 animate-spin" />
                              ) : (
                                <Save className="mr-1 h-3.5 w-3.5" />
                              )}
                              Save
                            </Button>
                          </div>
                        </div>
                      </CardHeader>
                      <CardContent className="space-y-3">
                        {v.entries.length === 0 ? (
                          <p className="text-sm text-muted-foreground">
                            No entries yet. Add at least one before saving, or hit Reset to restore
                            defaults.
                          </p>
                        ) : (
                          <ul className="space-y-1.5">
                            {v.entries.map((entry, index) => (
                              <li key={`${def.key}-${index}`} className="flex items-center gap-2">
                                <div className="flex flex-col gap-0.5">
                                  <button
                                    type="button"
                                    aria-label="Move up"
                                    className="text-muted-foreground hover:text-foreground disabled:opacity-30"
                                    onClick={() => moveEntry(def.key, index, -1)}
                                    disabled={index === 0}
                                  >
                                    <GripVertical className="h-3.5 w-3.5 rotate-90" />
                                  </button>
                                </div>
                                <Input
                                  value={entry}
                                  onChange={(e) => updateEntry(def.key, index, e.target.value)}
                                  className="flex-1"
                                />
                                <Button
                                  variant="ghost"
                                  size="icon"
                                  className="h-8 w-8 text-destructive hover:text-destructive"
                                  onClick={() => removeEntry(def.key, index)}
                                  aria-label={`Remove ${entry}`}
                                >
                                  <Trash2 className="h-3.5 w-3.5" />
                                </Button>
                              </li>
                            ))}
                          </ul>
                        )}
                        <div className="flex items-center gap-2 pt-2">
                          <Input
                            value={v.newEntry}
                            onChange={(e) => patch(def.key, { newEntry: e.target.value })}
                            onKeyDown={(e) => {
                              if (e.key === 'Enter') {
                                e.preventDefault();
                                addEntry(def.key);
                              }
                            }}
                            placeholder="Add an entry…"
                            className="flex-1"
                          />
                          <Button
                            type="button"
                            variant="outline"
                            size="sm"
                            onClick={() => addEntry(def.key)}
                            disabled={!v.newEntry.trim()}
                          >
                            <Plus className="mr-1 h-3.5 w-3.5" /> Add
                          </Button>
                        </div>
                      </CardContent>
                    </Card>
                  );
                })}
              </div>
            </section>
          );
        })}
      </div>
    </div>
  );
}
