import { useRef, useState } from 'react';
import { Link } from '@tanstack/react-router';
import { Map as MapIcon, Cast, Download, ExternalLink, X } from 'lucide-react';
import type { BattleMap, Campaign } from '@/lib/schemas';
import { mapsRepo } from '@/lib/db/repositories';
import { parseUvtt, toUvtt } from '@/lib/vtt/uvtt';
import { downloadText, safeFilename } from '@/lib/io/file';
import { imageSize } from '@/lib/img/resize';
import { useUiStore } from '@/stores/uiStore';
import { useMaps } from './hooks';
import { Page, PageHeader, EmptyState, RequireCampaign } from '@/components/ui/Page';
import { Button } from '@/components/ui/Button';
import { cn } from '@/lib/cn';
import { MapEditor } from './map/MapEditor';

const MAX_FILE_BYTES = 24 * 1024 * 1024;
const VTT_RE = /\.(dd2vtt|uvtt|df2vtt)$/i;

export function MapsPage() {
  return <RequireCampaign>{(c) => <Maps campaign={c} />}</RequireCampaign>;
}

function Maps({ campaign }: { campaign: Campaign }) {
  const maps = useMaps(campaign.id);
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const fileRef = useRef<HTMLInputElement>(null);
  const activeMapId = useUiStore((s) => s.activeMapId);
  const setActiveMap = useUiStore((s) => s.setActiveMap);
  const selected = maps.find((m) => m.id === selectedId) ?? null;

  const importFile = (file: File) => {
    setError(null);
    if (file.size > MAX_FILE_BYTES) { setError('File is larger than 24 MB.'); return; }
    const isVtt = VTT_RE.test(file.name);
    const name = file.name.replace(/\.[^.]+$/, '');
    const reader = new FileReader();
    reader.onload = async () => {
      const result = reader.result;
      if (typeof result !== 'string') return;
      try {
        if (isVtt) {
          const vtt = parseUvtt(result);
          if (!vtt) { setError('Could not read that .dd2vtt / .uvtt file.'); return; }
          const map = await mapsRepo.createFromVtt(campaign.id, name, vtt);
          setSelectedId(map.id);
        } else {
          const map = await mapsRepo.create(campaign.id, name, result);
          setSelectedId(map.id);
        }
      } catch {
        setError('Import failed.');
      }
    };
    if (isVtt) reader.readAsText(file); else reader.readAsDataURL(file);
  };

  const exportUvtt = async (map: BattleMap) => {
    const { w, h } = await imageSize(map.image);
    const cols = Math.max(0, Math.round(w / map.gridSize));
    const rows = Math.max(0, Math.round(h / map.gridSize));
    const obj = toUvtt({
      image: map.image, gridSize: map.gridSize, cols, rows,
      walls: map.walls ?? [], doors: map.doors ?? [], lights: map.lights ?? [],
    });
    downloadText(`${safeFilename(map.name)}.uvtt`, JSON.stringify(obj));
  };

  return (
    <Page>
      <PageHeader
        eyebrow="The cartographer's table"
        title="Maps"
        subtitle={campaign.name}
        actions={<Button variant="primary" onClick={() => fileRef.current?.click()}>+ Add map</Button>}
      />
      <input ref={fileRef} type="file" accept=".dd2vtt,.uvtt,.df2vtt,image/*" className="hidden"
        onChange={(e) => { const f = e.target.files?.[0]; if (f) importFile(f); e.target.value = ''; }} />
      {error && <p className="mb-3 text-sm text-danger">{error}</p>}

      {maps.length === 0 ? (
        <EmptyState title="No maps yet" hint="Upload an image or import a Universal VTT (.dd2vtt / .uvtt) map — walls, doors and lights come across — then add a grid, fog, tokens, and show it to players." action={<Button variant="primary" onClick={() => fileRef.current?.click()}>+ Add map</Button>} />
      ) : (
        <div className="grid gap-4 lg:grid-cols-[230px_1fr]">
          <ul className="space-y-1.5">
            {maps.map((mp) => {
              const active = selected?.id === mp.id;
              return (
                <li key={mp.id} className={cn('group flex items-center gap-1 rounded-xl border bg-panel transition-colors', active ? 'border-accent bg-accent-glow' : 'border-line hover:border-line-strong')}>
                  <button className={cn('flex min-w-0 flex-1 items-center gap-2 px-3 py-2 text-left text-sm', active ? 'text-accent-deep' : 'text-ink')} onClick={() => setSelectedId(mp.id)}>
                    <MapIcon size={15} className={active ? 'text-accent-deep' : 'text-muted'} aria-hidden />
                    <span className="truncate font-display font-medium">{mp.name}</span>
                    {activeMapId === mp.id && <Cast size={13} className="ml-auto shrink-0 text-accent-deep" aria-hidden />}
                  </button>
                  <button className="px-2 text-muted opacity-0 hover:text-danger group-hover:opacity-100" onClick={async () => { await mapsRepo.remove(mp.id); if (selectedId === mp.id) setSelectedId(null); if (activeMapId === mp.id) setActiveMap(null); }} aria-label={`Delete ${mp.name}`}>
                    <X size={14} aria-hidden />
                  </button>
                </li>
              );
            })}
          </ul>
          {selected ? (
            <div>
              <div className="mb-2 flex flex-wrap items-center gap-2 rounded-xl border border-line bg-panel p-2">
                <Button size="sm" variant={activeMapId === selected.id ? 'primary' : 'secondary'} onClick={() => setActiveMap(activeMapId === selected.id ? null : selected.id)}>
                  <Cast size={15} aria-hidden />
                  {activeMapId === selected.id ? 'Showing to players' : 'Show to players'}
                </Button>
                <Link to="/play" className="inline-flex items-center gap-1 text-sm text-accent hover:text-accent-deep hover:underline">Open player view <ExternalLink size={13} aria-hidden /></Link>
                <Button size="sm" variant="ghost" className="ml-auto" onClick={() => void exportUvtt(selected)} title="Export as Universal VTT (.uvtt)">
                  <Download size={15} aria-hidden />
                  Export .uvtt
                </Button>
              </div>
              <MapEditor key={selected.id} map={selected} campaign={campaign} />
            </div>
          ) : (
            <div className="grid place-items-center rounded-xl border border-dashed border-line bg-panel/50 p-10 text-center text-sm text-muted">
              <MapIcon size={26} className="mb-2 text-faint" aria-hidden />
              Select a map.
            </div>
          )}
        </div>
      )}
    </Page>
  );
}
