
import { useState } from 'react';
import { X } from 'lucide-react';
import type { Campaign, Quest, Objective } from '@/lib/schemas';
import { questsRepo } from '@/lib/db/repositories';
import { newId } from '@/lib/ids';
import { complete } from '@/lib/llm/client';
import { getLlmConfig, useAssistantStore } from '@/stores/assistantStore';
import { buildQuestHookPrompt, questHookSchema, fallbackQuestHook, type QuestHook } from '@/lib/assistant/session';
import { buildCampaignContext } from '@/lib/assistant/context';
import { useDebouncedCallback } from '@/lib/useDebouncedCallback';
import { useQuests } from './hooks';
import { useCharacters } from '@/features/characters/hooks';
import { useNotes } from '@/features/world/hooks';
import { useEncounters } from '@/features/combat/hooks';
import { Page, PageHeader, EmptyState, RequireCampaign } from '@/components/ui/Page';
import { Button } from '@/components/ui/Button';
import { Input, Select } from '@/components/ui/Input';
import { useConfirm } from '@/components/ui/useConfirm';
import { cn } from '@/lib/cn';

const STATUSES: Quest['status'][] = ['active', 'on-hold', 'completed', 'failed'];
const STATUS_COLOR: Record<Quest['status'], string> = {
  active: 'text-info', 'on-hold': 'text-muted', completed: 'text-success', failed: 'text-danger',
};

export function QuestsPage() {
  return <RequireCampaign>{(c) => <Quests campaign={c} />}</RequireCampaign>;
}

function Quests({ campaign }: { campaign: Campaign }) {
  const quests = useQuests(campaign.id);
  const characters = useCharacters(campaign.id);
  const notes = useNotes(campaign.id);
  const encounters = useEncounters(campaign.id);
  const order = { active: 0, 'on-hold': 1, completed: 2, failed: 3 };
  const sorted = [...quests].sort((a, b) => order[a.status] - order[b.status] || a.title.localeCompare(b.title));
  const [genBusy, setGenBusy] = useState(false);
  const llmEnabled = useAssistantStore((s) => s.enabled);
  const hasKey = useAssistantStore((s) => !!s.apiKey);
  const canUseLlm = llmEnabled && hasKey;

  const generateQuest = async () => {
    setGenBusy(true);
    try {
      let hook: QuestHook | null = null;
      if (canUseLlm) {
        const ctx = buildCampaignContext({ campaign, characters, encounters, quests, notes });
        const { system, user } = buildQuestHookPrompt(ctx);
        const res = await complete<QuestHook>(getLlmConfig(), { system, user, schema: questHookSchema });
        if (res.ok && 'data' in res) hook = res.data;
      }
      if (!hook) hook = fallbackQuestHook();
      const created = await questsRepo.create(campaign.id, hook.title);
      await questsRepo.update(created.id, {
        description: hook.description,
        reward: hook.reward,
        objectives: hook.objectives.map((text) => ({ id: newId(), text, done: false })),
      });
    } finally {
      setGenBusy(false);
    }
  };

  return (
    <Page>
      <PageHeader
        title="Quests"
        subtitle={campaign.name}
        actions={
          <div className="flex gap-2">
            <Button variant="secondary" disabled={genBusy} onClick={() => void generateQuest()}>
              {genBusy ? 'Generating…' : 'Generate quest hook'}
            </Button>
            <Button variant="primary" onClick={() => questsRepo.create(campaign.id, 'New quest')}>+ New quest</Button>
          </div>
        }
      />
      {quests.length === 0 ? (
        <EmptyState title="No quests yet" hint="Track objectives, rewards, and progress." action={<Button variant="primary" onClick={() => questsRepo.create(campaign.id, 'New quest')}>+ New quest</Button>} />
      ) : (
        <div className="space-y-3">
          {sorted.map((q) => <QuestCard key={q.id} quest={q} />)}
        </div>
      )}
    </Page>
  );
}

function QuestCard({ quest }: { quest: Quest }) {
  const [q, setQ] = useState(quest);
  const [newObj, setNewObj] = useState('');
  const { confirm, confirmElement } = useConfirm();
  const save = useDebouncedCallback((next: Quest) => void questsRepo.update(next.id, {
    title: next.title, status: next.status, description: next.description, reward: next.reward, objectives: next.objectives,
  }), 350);
  const update = (patch: Partial<Quest>) => setQ((p) => { const next = { ...p, ...patch }; save(next); return next; });

  const setObjective = (id: string, patch: Partial<Objective>) =>
    update({ objectives: q.objectives.map((o) => (o.id === id ? { ...o, ...patch } : o)) });
  const addObjective = () => {
    if (newObj.trim() === '') return;
    update({ objectives: [...q.objectives, { id: newId(), text: newObj.trim(), done: false }] });
    setNewObj('');
  };
  const done = q.objectives.filter((o) => o.done).length;

  return (
    <div className="rounded-lg border border-line bg-panel p-4">
      <div className="flex flex-wrap items-center gap-2">
        <Input className="flex-1 font-display font-semibold" value={q.title} onChange={(e) => update({ title: e.target.value })} aria-label="Quest title" />
        <Select className={cn('w-auto py-1 text-xs font-medium', STATUS_COLOR[q.status])} value={q.status} onChange={(e) => update({ status: e.target.value as Quest['status'] })} aria-label="Quest status">
          {STATUSES.map((s) => <option key={s} value={s}>{s}</option>)}
        </Select>
        <Button size="icon" variant="ghost" className="text-danger" onClick={async () => { if (await confirm({ title: 'Delete quest?', message: <>Delete <strong className="text-ink">{q.title || 'this quest'}</strong>? This can’t be undone.</> })) void questsRepo.remove(quest.id); }} aria-label={`Delete ${q.title}`}><X size={14} aria-hidden /></Button>
      </div>
      {confirmElement}

      <textarea
        value={q.description}
        onChange={(e) => update({ description: e.target.value })}
        placeholder="What's this quest about?"
        className="mt-2 min-h-16 w-full resize-y rounded-md border border-line bg-surface px-3 py-2 text-sm text-ink focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/60"
      />

      <label className="mt-2 block text-xs text-muted">
        Reward
        <Input value={q.reward} onChange={(e) => update({ reward: e.target.value })} placeholder="500 gp, a favour, a magic sword…" />
      </label>

      <div className="mt-3">
        <div className="mb-1 smallcaps">
          Objectives {q.objectives.length > 0 && `(${done}/${q.objectives.length})`}
        </div>
        <ul className="space-y-1">
          {q.objectives.map((o) => (
            <li key={o.id} className="flex items-center gap-2 text-sm">
              <input type="checkbox" checked={o.done} onChange={(e) => setObjective(o.id, { done: e.target.checked })} aria-label={o.text} />
              <input
                className={cn('flex-1 bg-transparent text-sm focus:outline-none', o.done ? 'text-muted line-through' : 'text-ink')}
                value={o.text}
                onChange={(e) => setObjective(o.id, { text: e.target.value })}
              />
              <button className="text-muted hover:text-danger" onClick={() => update({ objectives: q.objectives.filter((x) => x.id !== o.id) })} aria-label="Remove objective"><X size={13} aria-hidden /></button>
            </li>
          ))}
        </ul>
        <div className="mt-1 flex gap-2">
          <Input className="h-8 text-sm" value={newObj} onChange={(e) => setNewObj(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && addObjective()} placeholder="Add objective…" />
          <Button size="sm" onClick={addObjective}>Add</Button>
        </div>
      </div>
    </div>
  );
}
