import { ReactNode, useEffect, useMemo, useState } from 'react';
import { api, LinkItem, SetupState } from './lib/api';

type Page = 'loading' | 'setup' | 'login' | 'dashboard' | 'admin';
type LinkForm = {
  name: string;
  url: string;
  description: string;
  category: string;
  icon_url: string;
  enabled: boolean;
};

export default function App() {
  const [state, setState] = useState<SetupState | null>(null);
  const [links, setLinks] = useState<LinkItem[]>([]);
  const [page, setPage] = useState<Page>('loading');
  const [query, setQuery] = useState('');

  async function refresh() {
    const current = await api.request<SetupState>('/api/me');
    setState(current);
    const path = window.location.pathname;
    if (current.needs_setup) {
      setPage('setup');
      return;
    }
    if (!current.current_user) {
      setPage(path.startsWith('/admin') ? 'login' : 'dashboard');
    } else {
      setPage(path.startsWith('/admin') ? 'admin' : 'dashboard');
    }
    setLinks(await api.request<LinkItem[]>('/api/links'));
  }

  useEffect(() => {
    refresh().catch(() => setPage('setup'));
  }, []);

  useEffect(() => {
    const handlePopState = () => {
      refresh().catch(() => setPage('setup'));
    };
    window.addEventListener('popstate', handlePopState);
    return () => window.removeEventListener('popstate', handlePopState);
  }, []);

  if (page === 'loading') return <Shell><Centered>Loading...</Centered></Shell>;
  if (page === 'setup') return <SetupPage onDone={refresh} />;
  if (page === 'login') return <LoginPage onDone={refresh} />;

  if (page === 'admin') {
    return (
      <Shell>
        <div className="container-wrap">
          <AdminHeader
            currentUser={state?.current_user?.username ?? null}
            onBack={() => nav('/')}
            onLogout={async () => {
              await api.request('/api/logout', { method: 'POST' });
              nav('/');
              await refresh();
            }}
          />
          <AdminPage
            links={links}
            onSave={async (payload, file, editingId) => {
              const fd = toFormData(payload, file);
              const url = editingId ? `/api/links/${editingId}` : '/api/links';
              try {
                await api.request(url, { method: editingId ? 'PATCH' : 'POST', body: fd });
              } catch (err) {
                const message = err instanceof Error ? err.message : String(err);
                if (message.includes('Link name already exists')) {
                  alert('Link names must be unique.');
                  return;
                }
                throw err;
              }
              await refresh();
            }}
            onDelete={async (id) => {
              await api.request(`/api/links/${id}`, { method: 'DELETE' });
              await refresh();
            }}
          />
        </div>
      </Shell>
    );
  }

  return (
    <Shell>
      <Dashboard
        links={links}
        query={query}
        setQuery={setQuery}
        canLogout={Boolean(state?.current_user)}
        onAdmin={() => nav('/admin')}
        onLogout={async () => {
          await api.request('/api/logout', { method: 'POST' });
          await refresh();
        }}
      />
    </Shell>
  );
}

function nav(path: string) {
  window.history.pushState({}, '', path);
  window.dispatchEvent(new PopStateEvent('popstate'));
}

function toFormData(payload: LinkForm, file?: File | null) {
  const fd = new FormData();
  fd.append('name', payload.name);
  fd.append('url', payload.url);
  fd.append('description', payload.description);
  fd.append('category', payload.category);
  fd.append('icon_url', payload.icon_url);
  fd.append('enabled', String(payload.enabled));
  if (file) fd.append('icon', file);
  return fd;
}

function Shell({ children }: { children: ReactNode }) {
  return <div className="min-h-screen">{children}</div>;
}

function Centered({ children }: { children: ReactNode }) {
  return <div className="grid min-h-screen place-items-center p-8 text-slate-500">{children}</div>;
}

function Dashboard({
  links,
  query,
  setQuery,
  canLogout,
  onAdmin,
  onLogout,
}: {
  links: LinkItem[];
  query: string;
  setQuery: (value: string) => void;
  canLogout: boolean;
  onAdmin: () => void;
  onLogout: () => Promise<void>;
}) {
  const activeLinks = useMemo(() => links.filter((link) => link.enabled), [links]);

  const filtered = useMemo(() => {
    const text = query.trim().toLowerCase();
    if (!text) return activeLinks;
    return activeLinks.filter((l) => `${l.name} ${l.description} ${l.category}`.toLowerCase().includes(text));
  }, [activeLinks, query]);

  const grouped = useMemo(() => {
    const map = new Map<string, LinkItem[]>();
    for (const link of filtered) {
      const key = (link.category || 'General').trim() || 'General';
      const list = map.get(key) ?? [];
      list.push(link);
      map.set(key, list);
    }
    return Array.from(map.entries())
      .map(([category, items]) => ({ category, items: items.sort((a, b) => a.name.localeCompare(b.name)) }))
      .sort((a, b) => a.category.localeCompare(b.category));
  }, [filtered]);

  return (
    <div className="container-wrap">
      <header className="row-between">
        <h1 className="title-sm">Jellomator</h1>
        <div className="flex items-center gap-2">
          {canLogout ? <button type="button" className="btn-subtle" onClick={onLogout}>Logout</button> : null}
          <button type="button" className="btn-subtle" onClick={onAdmin}>Admin</button>
        </div>
      </header>

      <input
        className="input"
        placeholder="Search links"
        value={query}
        onChange={(event) => setQuery(event.target.value)}
      />

      <main className="mt-4 space-y-4">
        {grouped.map((group) => (
          <section key={group.category} className="panel">
            <h2 className="section-title">{group.category}</h2>
            <ul className="link-list">
              {group.items.map((link) => (
                <li key={link.id}>
                  <a href={link.url} target="_blank" rel="noreferrer" className="link-row">
                    <span className="link-icon" aria-hidden="true">
                      {link.icon_url ? <img src={link.icon_url} alt="" className="link-icon-image" /> : <span>{link.name[0]}</span>}
                    </span>
                    <span className="min-w-0">
                      <span className="link-name">{link.name}</span>
                      <span className="link-description">{link.description || link.url}</span>
                    </span>
                  </a>
                </li>
              ))}
            </ul>
          </section>
        ))}

        {!grouped.length && <EmptyState title="No links found" body="No enabled links match your search." />}
      </main>
    </div>
  );
}

function AdminHeader({ currentUser, onBack, onLogout }: { currentUser: string | null; onBack: () => void; onLogout: () => Promise<void> }) {
  return (
    <div className="row-between">
      <div>
        <h1 className="title-sm">Manage links</h1>
        <p className="mt-1 text-xs text-slate-500">{currentUser ? `Signed in as ${currentUser}` : 'Sign in required'}</p>
      </div>
      <div className="flex gap-2">
        <button type="button" className="btn-subtle" onClick={onBack}>Back</button>
        <button type="button" className="btn-subtle" onClick={onLogout}>Logout</button>
      </div>
    </div>
  );
}

function EmptyState({ title, body }: { title: string; body: string }) {
  return (
    <div className="panel text-slate-500">
      <h3 className="text-sm font-medium text-slate-200">{title}</h3>
      <p className="mt-1 text-sm">{body}</p>
    </div>
  );
}

function SetupPage({ onDone }: { onDone: () => Promise<void> }) {
  return (
    <AuthCard
      title="First-run setup"
      action="Create admin"
      onSubmit={async (fd) => {
        await api.request('/api/setup', { method: 'POST', body: JSON.stringify(Object.fromEntries(fd)) });
        await onDone();
      }}
      fields={['username', 'password']}
    />
  );
}

function LoginPage({ onDone }: { onDone: () => Promise<void> }) {
  return (
    <AuthCard
      title="Admin login"
      action="Sign in"
      onSubmit={async (fd) => {
        await api.request('/api/login', { method: 'POST', body: JSON.stringify(Object.fromEntries(fd)) });
        nav('/');
        await onDone();
      }}
      fields={['username', 'password']}
    />
  );
}

function AuthCard({
  title,
  action,
  onSubmit,
  fields,
}: {
  title: string;
  action: string;
  fields: string[];
  onSubmit: (fd: FormData) => Promise<void>;
}) {
  return (
    <div className="grid min-h-screen place-items-center p-4">
      <form
        className="panel w-full max-w-sm space-y-3"
        onSubmit={async (e) => {
          e.preventDefault();
          await onSubmit(new FormData(e.currentTarget));
          location.reload();
        }}
      >
        <h1 className="title-sm">{title}</h1>
        {fields.map((f) => (
          <input key={f} name={f} type={f === 'password' ? 'password' : 'text'} className="input" placeholder={f} required />
        ))}
        <button className="btn-subtle w-full" type="submit">{action}</button>
      </form>
    </div>
  );
}

function AdminPage({
  links,
  onSave,
  onDelete,
}: {
  links: LinkItem[];
  onSave: (payload: LinkForm, file?: File | null, editingId?: number) => Promise<void>;
  onDelete: (id: number) => Promise<void>;
}) {
  const [form, setForm] = useState<LinkForm>(emptyForm());
  const [file, setFile] = useState<File | null>(null);
  const [editingId, setEditingId] = useState<number | null>(null);

  const startEdit = (link: LinkItem) => {
    setEditingId(link.id);
    setFile(null);
    setForm({
      name: link.name,
      url: link.url,
      description: link.description,
      category: link.category,
      icon_url: link.icon_url ?? '',
      enabled: link.enabled,
    });
  };

  const reset = () => {
    setEditingId(null);
    setFile(null);
    setForm(emptyForm());
  };

  return (
    <div className="mt-4 grid gap-4 lg:grid-cols-[320px_1fr]">
      <form
        className="panel space-y-2"
        onSubmit={async (event) => {
          event.preventDefault();
          await onSave(form, file, editingId ?? undefined);
          reset();
        }}
      >
        <input className="input" value={form.name} placeholder="name" onChange={(e) => setForm({ ...form, name: e.target.value })} required />
        <input className="input" value={form.url} placeholder="url" onChange={(e) => setForm({ ...form, url: e.target.value })} required />
        <input className="input" value={form.description} placeholder="description" onChange={(e) => setForm({ ...form, description: e.target.value })} />
        <input className="input" value={form.category} placeholder="category" onChange={(e) => setForm({ ...form, category: e.target.value })} />
        <input className="input" value={form.icon_url} placeholder="icon URL" onChange={(e) => setForm({ ...form, icon_url: e.target.value })} />
        <input className="input" type="file" accept="image/*" onChange={(e) => setFile(e.target.files?.[0] ?? null)} />
        <label className="flex items-center gap-2 text-xs text-slate-400">
          <input type="checkbox" checked={form.enabled} onChange={(e) => setForm({ ...form, enabled: e.target.checked })} />
          Enabled
        </label>
        <div className="flex gap-2 pt-1">
          <button type="submit" className="btn-subtle">{editingId ? 'Update' : 'Add'}</button>
          {editingId ? <button type="button" className="btn-subtle" onClick={reset}>Cancel</button> : null}
        </div>
      </form>

      <div className="panel">
        <ul className="admin-list">
          {links.map((link) => (
            <li key={link.id} className="admin-row">
              <div className="min-w-0">
                <div className="truncate text-sm text-slate-100">{link.name}</div>
                <div className="truncate text-xs text-slate-500">{link.category || 'General'} | {link.enabled ? 'enabled' : 'disabled'}</div>
              </div>
              <div className="flex gap-2">
                <button type="button" className="btn-subtle" onClick={() => startEdit(link)}>Edit</button>
                <button type="button" className="btn-subtle" onClick={() => onDelete(link.id)}>Delete</button>
              </div>
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
}

function emptyForm(): LinkForm {
  return {
    name: '',
    url: '',
    description: '',
    category: 'General',
    icon_url: '',
    enabled: true,
  };
}
