import { useEffect, useMemo, useRef, useState } from 'react';
import { Bell, X } from 'lucide-react';
import type { Campaign } from '@/lib/schemas';
import { buildSignals } from '@/lib/assistant/signals';
import type { Suggestion } from '@/lib/assistant/advisors';
import { useActiveCampaign } from '@/features/campaigns/hooks';
import { useCharacters } from '@/features/characters/hooks';
import { useNotes, useQuests } from '@/features/world/hooks';
import { useEncounters } from '@/features/combat/hooks';
import { Button } from '@/components/ui/Button';
import { cn } from '@/lib/cn';
import { useSignalAction } from './useSignalAction';

const DOT: Record<Suggestion['severity'], string> = {
  danger: 'bg-danger',
  warn: 'bg-warning',
  info: 'bg-info',
};

/**
 * Ambient, app-wide "what needs my attention" surface. Watches the active
 * campaign's state and surfaces one-click signals from anywhere — the proactive
 * core of the assistant. Renders nothing without an active campaign.
 */
export function SignalsBell() {
  const campaign = useActiveCampaign();
  if (!campaign) return null;
  return <SignalsBellInner campaign={campaign} />;
}

function SignalsBellInner({ campaign }: { campaign: Campaign }) {
  const characters = useCharacters(campaign.id);
  const notes = useNotes(campaign.id);
  const quests = useQuests(campaign.id);
  const encounters = useEncounters(campaign.id);
  const activeEnc = encounters.find((e) => e.status === 'active');

  const [dismissed, setDismissed] = useState<Set<string>>(new Set());
  const [open, setOpen] = useState(false);
  const [msg, setMsg] = useState<string | null>(null);
  const runAction = useSignalAction(campaign.id, setMsg);
  const ref = useRef<HTMLDivElement>(null);

  const signals = useMemo(
    () => buildSignals({ activeEncounter: activeEnc, characters, notes, quests }).filter((s) => !dismissed.has(s.id)),
    [activeEnc, characters, notes, quests, dismissed],
  );
  // The badge counts only things that genuinely want attention.
  const attention = signals.filter((s) => s.severity !== 'info').length;

  // Close the popover on an outside click or Escape.
  useEffect(() => {
    if (!open) return;
    const onDown = (e: MouseEvent) => { if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false); };
    const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDown);
    document.addEventListener('keydown', onKey);
    return () => { document.removeEventListener('mousedown', onDown); document.removeEventListener('keydown', onKey); };
  }, [open]);

  return (
    <div className="relative" ref={ref}>
      <button
        onClick={() => setOpen((o) => !o)}
        aria-label={`Signals${attention > 0 ? ` (${attention} need attention)` : ''}`}
        title="Signals — things that may need your attention"
        className="relative grid h-9 w-9 flex-none place-items-center rounded-md text-muted hover:bg-elevated hover:text-ink"
      >
        <Bell size={18} aria-hidden />
        {attention > 0 && (
          <span className="absolute -right-0.5 -top-0.5 grid min-w-[16px] place-items-center rounded-full bg-danger px-1 text-[10px] font-semibold leading-4 text-white">
            {attention}
          </span>
        )}
      </button>

      {open && (
        <div className="absolute right-0 top-11 z-50 w-80 max-w-[calc(100vw-1.5rem)] rounded-xl border border-line bg-panel p-2 shadow-xl">
          <div className="flex items-center justify-between px-2 py-1">
            <span className="smallcaps">Signals</span>
            {signals.length > 0 && (
              <button className="text-xs text-faint hover:text-ink" onClick={() => setDismissed(new Set(signals.map((s) => s.id)))}>Clear all</button>
            )}
          </div>
          {msg && <p className="px-2 pb-1 text-xs text-success" aria-live="polite">{msg}</p>}
          {signals.length === 0 ? (
            <p className="px-2 py-3 text-sm text-muted">Nothing needs your attention.</p>
          ) : (
            <ul className="max-h-[60vh] space-y-1 overflow-y-auto">
              {signals.map((s) => (
                <li key={s.id} className="flex items-start gap-2 rounded-lg border border-line/70 bg-surface p-2">
                  <span className={cn('mt-1.5 h-2 w-2 shrink-0 rounded-full', DOT[s.severity])} aria-hidden />
                  <div className="min-w-0 flex-1">
                    <div className="text-sm font-medium text-ink">{s.title}</div>
                    {s.detail && <div className="text-xs text-muted">{s.detail}</div>}
                    {s.action && (
                      <Button size="sm" variant="secondary" className="mt-1.5" onClick={() => { void runAction(s.action!); if (s.action!.type !== 'goto') setMsg(null); setOpen(s.action!.type !== 'goto'); }}>
                        {s.action.label}
                      </Button>
                    )}
                  </div>
                  <button className="text-faint hover:text-ink" aria-label="Dismiss" onClick={() => setDismissed((d) => new Set(d).add(s.id))}>
                    <X size={13} aria-hidden />
                  </button>
                </li>
              ))}
            </ul>
          )}
        </div>
      )}
    </div>
  );
}
