import { useMemo, useState } from 'react';
import {
  Send, X, CheckCircle2, Trash2, Bell,
  MessageCircle, MessagesSquare, Smartphone, Hash, Globe, Mail,
} from 'lucide-react';
import { Card, CardBody, CardHeader, CardTitle } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button';
import { Badge } from '@/components/ui/Badge';
import { Field, Input, Select, Switch } from '@/components/ui/Input';
import { Loading, ErrorState, Toast } from '@/components/ui/States';
import { api, type ConfiguredNotifier, type NotifierCatalogItem, type SchemaField } from '@/lib/api';
import { useQuery, useMutation } from '@/hooks/useQuery';
import { useConfirm } from '@/components/ConfirmProvider';
import { cn } from '@/lib/cn';

// Icon + accent color per provider
const ICONS: Record<string, { Icon: React.ComponentType<{ className?: string }>; tint: string }> = {
  email:    { Icon: Mail,            tint: 'bg-ink-900' },
  telegram: { Icon: Send,            tint: 'bg-[#229ED9]' },
  slack:    { Icon: Hash,            tint: 'bg-[#4A154B]' },
  discord:  { Icon: MessageCircle,   tint: 'bg-[#5865F2]' },
  pushover: { Icon: Smartphone,      tint: 'bg-[#249DF1]' },
  teams:    { Icon: MessagesSquare,  tint: 'bg-[#5059C9]' },
  gchat:    { Icon: MessageCircle,   tint: 'bg-[#0F9D58]' },
  webhook:  { Icon: Globe,           tint: 'bg-violet-600' },
};
const ICON_FALLBACK = { Icon: Bell, tint: 'bg-ink-700' };
const ico = (t: string) => ICONS[t] ?? ICON_FALLBACK;

export function Notifications() {
  const catalogQ = useQuery(() => api.notifiers.catalog(), []);
  const listQ    = useQuery(() => api.notifiers.list(),    []);
  const [openType, setOpenType] = useState<string | null>(null);
  const [editing, setEditing]   = useState<ConfiguredNotifier | null>(null);
  const [toast, setToast]       = useState<{ msg: string; tone: 'success' | 'danger' } | null>(null);
  const confirm = useConfirm();

  const catalog = catalogQ.data?.items ?? [];
  const configured = listQ.data?.items ?? [];

  if (catalogQ.isLoading || listQ.isLoading) return <Loading label="Loading notifications…" />;
  if (catalogQ.error) return <ErrorState error={catalogQ.error} onRetry={catalogQ.refetch} />;
  if (listQ.error)    return <ErrorState error={listQ.error}    onRetry={listQ.refetch} />;

  return (
    <div className="space-y-5 max-w-[1400px]">
      <Card>
        <CardBody className="flex items-center justify-between">
          <div>
            <h2 className="text-base font-semibold text-ink-900">Notifications</h2>
            <p className="text-xs text-ink-500 mt-0.5">Get notified when backups finish or fail. Pick any channel below.</p>
          </div>
          {configured.length > 0 && (
            <Badge tone="neutral">{configured.length} configured</Badge>
          )}
        </CardBody>
      </Card>

      {/* Configured channels */}
      {configured.length > 0 && (
        <Card>
          <CardHeader>
            <CardTitle>Active channels</CardTitle>
          </CardHeader>
          <CardBody className="space-y-2">
            {configured.map((n) => (
              <ConfiguredRow key={n.id} item={n}
                onUpdated={() => listQ.refetch()}
                onTest={(msg, ok) => setToast({ msg, tone: ok ? 'success' : 'danger' })}
                onRemove={async () => {
                  const ok = await confirm({
                    title: `Remove ${n.name}?`,
                    description: 'You will stop receiving notifications via this channel.',
                    confirmLabel: 'Remove',
                    tone: 'danger',
                  });
                  if (!ok) return;
                  try {
                    await api.notifiers.remove(n.id);
                    listQ.refetch();
                    setToast({ msg: `${n.name} removed`, tone: 'success' });
                  } catch (e: any) {
                    setToast({ msg: e.message ?? String(e), tone: 'danger' });
                  }
                }}
                onEdit={() => { setEditing(n); setOpenType(null); }}
              />
            ))}
          </CardBody>
        </Card>
      )}

      {/* Catalog — providers to add */}
      <Card>
        <CardHeader>
          <div>
            <CardTitle>Add a channel</CardTitle>
            <p className="text-xs text-ink-500 mt-0.5">Pick a provider. The connection form appears below.</p>
          </div>
        </CardHeader>
        <CardBody className="space-y-4">
          <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3">
            {catalog.map((c) => {
              const { Icon, tint } = ico(c.type);
              const active = openType === c.type;
              return (
                <button key={c.type}
                  onClick={() => { setOpenType(active ? null : c.type); setEditing(null); }}
                  className={cn(
                    'flex items-center gap-3 p-3 rounded-xl border-2 text-left transition-all min-w-0',
                    active ? 'border-brand-500 bg-brand-50'
                           : 'border-ink-200 hover:border-ink-300 hover:bg-ink-50',
                  )}>
                  <span className={cn('h-9 w-9 grid place-items-center rounded-lg text-white shrink-0', tint)}>
                    <Icon className="h-4 w-4" />
                  </span>
                  <div className="min-w-0 flex-1">
                    <p className="text-sm font-medium text-ink-900 truncate leading-tight">{c.label}</p>
                    <p className="text-[11px] text-ink-500 truncate mt-0.5">{c.description}</p>
                  </div>
                  <span className="text-[11px] font-medium text-brand-700 shrink-0">
                    {active ? 'Open' : 'Configure'}
                  </span>
                </button>
              );
            })}
          </div>

          {(openType || editing) && (
            <NotifierForm
              type={(editing?.type ?? openType) as string}
              existing={editing ?? undefined}
              schema={catalog.find((c) => c.type === (editing?.type ?? openType))?.schema ?? []}
              label={catalog.find((c) => c.type === (editing?.type ?? openType))?.label ?? (editing?.type ?? openType ?? '')}
              onCancel={() => { setOpenType(null); setEditing(null); }}
              onSaved={() => { setOpenType(null); setEditing(null); listQ.refetch(); setToast({ msg: 'Notification channel saved', tone: 'success' }); }}
              onError={(m) => setToast({ msg: m, tone: 'danger' })}
            />
          )}
        </CardBody>
      </Card>

      {toast && <Toast message={toast.msg} tone={toast.tone} onClose={() => setToast(null)} />}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────

function ConfiguredRow({
  item, onUpdated, onTest, onRemove, onEdit,
}: {
  item: ConfiguredNotifier;
  onUpdated: () => void;
  onTest: (msg: string, ok: boolean) => void;
  onRemove: () => void;
  onEdit: () => void;
}) {
  const { Icon, tint } = ico(item.type);
  const test   = useMutation(() => api.notifiers.test(item.id),    { onSuccess: () => onTest('Test message sent', true),  onError: (e) => onTest(e.message, false) });
  const toggle = useMutation((v: boolean) => api.notifiers.update(item.id, { enabled: v }), { onSuccess: onUpdated });

  return (
    <div className="flex items-center gap-3 p-3 rounded-xl border border-ink-200 flex-wrap">
      <span className={cn('h-9 w-9 grid place-items-center rounded-lg text-white shrink-0', tint)}>
        <Icon className="h-4 w-4" />
      </span>
      <div className="flex-1 min-w-[160px]">
        <p className="text-sm font-semibold text-ink-900 truncate">{item.name}</p>
        <div className="flex items-center gap-2 mt-0.5 flex-wrap">
          {item.on_failure && <Badge tone="danger" dot>on failure</Badge>}
          {item.on_success && <Badge tone="success" dot>on success</Badge>}
          {!item.on_failure && !item.on_success && <Badge tone="neutral">never (configure events)</Badge>}
        </div>
      </div>
      <div className="flex items-center gap-2 ml-auto">
        <Switch checked={item.enabled} onChange={(v) => toggle.mutate(v)} />
        <Button size="sm" variant="secondary" disabled={test.isLoading} onClick={() => test.mutate(undefined as any)}>
          {test.isLoading ? 'Sending…' : 'Test'}
        </Button>
        <Button size="sm" variant="ghost" onClick={onEdit}>Edit</Button>
        <button
          onClick={onRemove}
          title="Remove"
          className="h-8 w-8 grid place-items-center rounded-md text-ink-400 hover:text-danger hover:bg-red-50"
        >
          <Trash2 className="h-4 w-4" />
        </button>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────

function NotifierForm({
  type, label, schema, existing, onCancel, onSaved, onError,
}: {
  type: string;
  label: string;
  schema: SchemaField[];
  existing?: ConfiguredNotifier;
  onCancel: () => void;
  onSaved: () => void;
  onError: (m: string) => void;
}) {
  const initial = useMemo<Record<string, unknown>>(() => {
    if (existing) return { ...existing.config };
    const v: Record<string, unknown> = {};
    for (const f of schema) v[f.key] = f.default ?? (f.type === 'bool' ? false : f.type === 'int' ? 0 : '');
    return v;
  }, [existing, schema]);

  const [name, setName]       = useState(existing?.name ?? label);
  const [values, setValues]   = useState<Record<string, unknown>>(initial);
  const [enabled, setEnabled] = useState(existing?.enabled ?? true);
  const [onSucc, setOnSucc]   = useState(existing?.on_success ?? false);
  const [onFail, setOnFail]   = useState(existing?.on_failure ?? true);
  const [errors, setErrors]   = useState<Record<string, string>>({});
  const { Icon, tint } = ico(type);

  const save = useMutation(
    async () => {
      if (existing) {
        return api.notifiers.update(existing.id, {
          name, enabled, on_success: onSucc, on_failure: onFail, config: values,
        });
      }
      return api.notifiers.create({
        type, name, enabled, on_success: onSucc, on_failure: onFail, config: values,
      });
    },
    {
      onSuccess: onSaved,
      onError: (e: any) => {
        if (e?.payload?.errors) setErrors(e.payload.errors);
        else onError(e.message ?? String(e));
      },
    },
  );

  return (
    <div className="rounded-xl border border-ink-200 bg-ink-50/40 overflow-hidden">
      <div className="flex items-center justify-between px-4 py-3 border-b border-ink-200 bg-white">
        <div className="flex items-center gap-2.5">
          <span className={cn('h-7 w-7 grid place-items-center rounded-md text-white', tint)}>
            <Icon className="h-3.5 w-3.5" />
          </span>
          <div>
            <p className="text-sm font-semibold text-ink-900">{existing ? `Edit ${existing.name}` : `Configure ${label}`}</p>
            <p className="text-[11px] text-ink-500">Notifications will be sent through this channel</p>
          </div>
        </div>
        <button onClick={onCancel} title="Cancel" aria-label="Cancel notification setup"
          className="h-7 w-7 grid place-items-center rounded-md text-ink-500 hover:bg-ink-100 hover:text-ink-900">
          <X className="h-4 w-4" />
        </button>
      </div>

      <div className="p-4 space-y-4">
        <Field label="Channel name" hint="Shown in the active channels list">
          <Input value={name} onChange={(e) => setName(e.target.value)} />
        </Field>

        <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
          {(() => {
            // Identify the non-bool fields (they sit in the grid). If there's an
            // odd number of them, the trailing one would leave an empty half-cell
            // — span it full-width so the form stays balanced.
            const nonBool = schema
              .map((f, i) => ({ f, i }))
              .filter((x) => x.f.type !== 'bool');
            const lastIsOdd      = nonBool.length % 2 === 1;
            const lastNonBoolIdx = lastIsOdd ? nonBool[nonBool.length - 1].i : -1;

            return schema.map((f, i) => {
              const span2 = f.type === 'bool' || i === lastNonBoolIdx;
              return (
                <div key={f.key} className={span2 ? 'sm:col-span-2' : ''}>
                  <NotifierField field={f} value={values[f.key]} error={errors[f.key]}
                    onChange={(v) => { setValues({ ...values, [f.key]: v }); if (errors[f.key]) setErrors({ ...errors, [f.key]: '' }); }} />
                </div>
              );
            });
          })()}
        </div>

        {/* "When to send" — event subscriptions only. Channel enabled is
            split out below because it's conceptually different (a master
            on/off switch, not an event filter). Tile layout makes the two
            event options visually parallel + clickable as whole cards so
            the touch target is larger than the bare toggle. */}
        <div>
          <p className="text-[11px] font-medium uppercase tracking-wider text-ink-500 mb-2">
            Send notifications when
          </p>
          <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
            <EventTile
              label={<>A backup <strong>fails</strong></>}
              checked={onFail}
              onChange={setOnFail}
              tone="danger"
            />
            <EventTile
              label={<>A backup <strong>succeeds</strong></>}
              checked={onSucc}
              onChange={setOnSucc}
              tone="success"
            />
          </div>
        </div>

        {/* Master enable/disable, separated from event filters. Turning
            this off silences the channel without losing any of its
            configuration — useful for temporary blackouts during planned
            maintenance windows. */}
        <label className="flex items-center justify-between gap-3 p-3 rounded-lg border border-ink-200 bg-white cursor-pointer">
          <div className="min-w-0">
            <p className="text-sm font-medium text-ink-900">Channel enabled</p>
            <p className="text-[11px] text-ink-500 mt-0.5">
              Turn off to silence this channel without deleting it.
            </p>
          </div>
          <Switch checked={enabled} onChange={setEnabled} />
        </label>

        <div className="flex items-center justify-between gap-2 pt-3 mt-1 border-t border-ink-200">
          <Button variant="primary" leftIcon={<CheckCircle2 className="h-4 w-4" />} disabled={save.isLoading}
            onClick={() => save.mutate(undefined as any)}>
            {save.isLoading ? 'Saving…' : (existing ? 'Save changes' : 'Add channel')}
          </Button>
          <Button variant="ghost" onClick={onCancel}>Cancel</Button>
        </div>
      </div>
    </div>
  );
}

/**
 * One event subscription tile. Tone determines the colored ring + dot when
 * the tile is selected: danger=red for "fails", success=green for
 * "succeeds". The whole tile is the click target (label-wrapped switch) so
 * the touch area is large.
 */
function EventTile({
  label,
  checked,
  onChange,
  tone,
}: {
  label: React.ReactNode;
  checked: boolean;
  onChange: (v: boolean) => void;
  tone: 'danger' | 'success';
}) {
  const dotClass = checked
    ? (tone === 'danger' ? 'bg-red-500' : 'bg-emerald-500')
    : 'bg-ink-300';
  const ringClass = checked
    ? (tone === 'danger' ? 'border-red-300 bg-red-50/50' : 'border-emerald-300 bg-emerald-50/50')
    : 'border-ink-200 bg-white';
  return (
    <label className={cn(
      'flex items-center justify-between gap-3 p-3 rounded-lg border-2 cursor-pointer transition-colors',
      ringClass,
    )}>
      <span className="flex items-center gap-2.5 text-sm text-ink-800 min-w-0">
        <span className={cn('h-2 w-2 rounded-full shrink-0', dotClass)} />
        <span className="truncate">{label}</span>
      </span>
      <Switch checked={checked} onChange={onChange} />
    </label>
  );
}

function NotifierField({ field, value, error, onChange }: { field: SchemaField; value: unknown; error?: string; onChange: (v: unknown) => void }) {
  if (field.type === 'bool') {
    return (
      <div className="flex items-start justify-between gap-3 p-3 rounded-lg border border-ink-200 bg-white">
        <div>
          <p className="text-sm text-ink-800">{field.label}</p>
          {field.help && <p className="text-[11px] text-ink-500 mt-0.5">{field.help}</p>}
        </div>
        <Switch checked={!!value} onChange={(v) => onChange(v)} />
      </div>
    );
  }
  if (field.type === 'select') {
    return (
      <Field label={field.label} hint={error || field.help}>
        <Select value={String(value ?? '')} onChange={(e) => onChange(e.target.value)}>
          {(field.options ?? []).map((o) => <option key={o} value={o}>{o}</option>)}
        </Select>
      </Field>
    );
  }
  return (
    <Field label={field.label} hint={error || field.help}>
      <Input
        type={field.type === 'password' ? 'password' : field.type === 'int' ? 'number' : 'text'}
        value={String(value ?? '')}
        onChange={(e) => onChange(field.type === 'int' ? parseInt(e.target.value || '0', 10) : e.target.value)}
        className={error ? 'border-danger focus:border-danger focus:ring-red-200' : ''}
      />
    </Field>
  );
}
