'use client';

import { useEffect, useMemo, useRef, useState } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import {
  Bell,
  CalendarDays,
  Mail,
  Mic,
  MicOff,
  MoreVertical,
  Phone,
  Pencil,
  Plus,
  Trash2,
  Users,
  Video,
} from 'lucide-react';
import { useVoiceTranscription } from '@/hooks/use-voice-transcription';
import { WhatsAppIcon } from '@/components/icons/whatsapp';
import { format, formatDistanceToNowStrict } from 'date-fns';
import { toast } from 'sonner';

import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
// §2.1: contact-log compose surface migrated from Dialog to Sheet so it
// matches the side-panel doctrine used by every other compose surface in
// the app (ClientForm, InterestForm, YachtForm, EOI Generate). The
// dialog name `ComposeDialog` is kept for git-blame continuity but the
// component now renders <Sheet side="right">.
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
} from '@/components/ui/sheet';
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Checkbox } from '@/components/ui/checkbox';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import { Skeleton } from '@/components/ui/skeleton';
import { Textarea } from '@/components/ui/textarea';
import { apiFetch } from '@/lib/api/client';
import { toastError } from '@/lib/api/toast-error';
import { useConfirmation } from '@/hooks/use-confirmation';
import { cn } from '@/lib/utils';

interface InterestContactLogTabProps {
  interestId: string;
}

type Channel = 'email' | 'phone' | 'whatsapp' | 'in_person' | 'video' | 'other';
type Direction = 'outbound' | 'inbound';

type Template = 'call' | 'visit' | 'email';

interface ContactLogEntry {
  id: string;
  occurredAt: string;
  channel: Channel;
  direction: Direction;
  summary: string;
  voiceTranscript: string | null;
  templateUsed: string | null;
  followUpAt: string | null;
  reminderId: string | null;
  createdBy: string;
  createdAt: string;
  updatedAt: string;
}

/** Quick-template seeds — drop a starting structure into the summary so reps
 *  spend their typing on the substance, not the scaffolding. */
const TEMPLATE_SEEDS: Record<
  Template,
  { channel: Channel; direction: Direction; summary: string; label: string; icon: ChannelIcon }
> = {
  call: {
    channel: 'phone',
    direction: 'outbound',
    summary: 'Called the client. Discussed:\n\n• \n\nNext step: ',
    label: 'Call',
    icon: Phone,
  },
  visit: {
    channel: 'in_person',
    direction: 'outbound',
    summary: 'Met with the client in person. Discussed:\n\n• \n\nNext step: ',
    label: 'Visit',
    icon: Users,
  },
  email: {
    channel: 'email',
    direction: 'outbound',
    summary: 'Emailed the client.\n\nTopic: \n\nResponse expected: ',
    label: 'Email',
    icon: Mail,
  },
};

type ChannelIcon = React.ComponentType<{ className?: string }>;
const CHANNEL_META: Record<Channel, { label: string; icon: ChannelIcon; tone: string }> = {
  email: { label: 'Email', icon: Mail, tone: 'bg-sky-100 text-sky-700' },
  phone: { label: 'Phone', icon: Phone, tone: 'bg-emerald-100 text-emerald-700' },
  whatsapp: { label: 'WhatsApp', icon: WhatsAppIcon, tone: 'bg-emerald-100 text-emerald-700' },
  in_person: { label: 'In person', icon: Users, tone: 'bg-amber-100 text-amber-800' },
  video: { label: 'Video', icon: Video, tone: 'bg-violet-100 text-violet-700' },
  other: { label: 'Other', icon: CalendarDays, tone: 'bg-slate-100 text-slate-700' },
};

/**
 * Per-interaction contact log. Sales reps log every email / call /
 * WhatsApp / meeting touch with the client here so the team has a
 * structured history of "what was the last conversation about" — not
 * just the bare "last contact 8d ago" timestamp on the interest.
 *
 * Each entry can optionally schedule a follow-up that auto-creates a
 * reminder pointing back at the interest. Editing the entry's
 * follow-up date keeps the linked reminder in sync; deleting the
 * entry removes the reminder.
 */
export function InterestContactLogTab({ interestId }: InterestContactLogTabProps) {
  const [composeOpen, setComposeOpen] = useState(false);
  const [editTarget, setEditTarget] = useState<ContactLogEntry | null>(null);

  const { data: res, isLoading } = useQuery<{ data: ContactLogEntry[] }>({
    queryKey: ['interests', interestId, 'contact-log'],
    queryFn: () =>
      apiFetch<{ data: ContactLogEntry[] }>(`/api/v1/interests/${interestId}/contact-log`),
  });

  const entries = res?.data ?? [];

  return (
    <div className="space-y-4">
      <div className="flex items-center justify-between">
        <div>
          <h3 className="text-sm font-semibold text-foreground">Contact log</h3>
          <p className="text-xs text-muted-foreground">
            Record each conversation. The most recent log entry sets the &ldquo;Last contact&rdquo;
            chip on the interest header.
          </p>
        </div>
        <Button size="sm" onClick={() => setComposeOpen(true)} className="gap-1.5">
          <Plus className="size-4" aria-hidden />
          Log contact
        </Button>
      </div>

      {isLoading ? (
        <div className="space-y-2">
          <Skeleton className="h-20 rounded-lg" aria-hidden />
          <Skeleton className="h-20 rounded-lg" aria-hidden />
        </div>
      ) : entries.length === 0 ? (
        <EmptyState onAdd={() => setComposeOpen(true)} />
      ) : (
        <ol className="space-y-2">
          {entries.map((e) => (
            <ContactLogRow key={e.id} entry={e} interestId={interestId} onEdit={setEditTarget} />
          ))}
        </ol>
      )}

      <ComposeDialog interestId={interestId} open={composeOpen} onOpenChange={setComposeOpen} />
      {editTarget && (
        <ComposeDialog
          interestId={interestId}
          existing={editTarget}
          open={!!editTarget}
          onOpenChange={(o) => !o && setEditTarget(null)}
        />
      )}
    </div>
  );
}

// ─── Row ─────────────────────────────────────────────────────────────────────

function ContactLogRow({
  entry,
  interestId,
  onEdit,
}: {
  entry: ContactLogEntry;
  interestId: string;
  onEdit: (e: ContactLogEntry) => void;
}) {
  const queryClient = useQueryClient();
  const { confirm, dialog: confirmDialog } = useConfirmation();
  const channelMeta = CHANNEL_META[entry.channel];
  const Icon = channelMeta.icon;

  const deleteMutation = useMutation({
    mutationFn: () => apiFetch(`/api/v1/contact-log/${entry.id}`, { method: 'DELETE' }),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['interests', interestId, 'contact-log'] });
      queryClient.invalidateQueries({ queryKey: ['interests', interestId] });
      toast.success('Contact log entry deleted.');
    },
    onError: (err) => toastError(err),
  });

  return (
    <li className="rounded-lg border bg-background p-3">
      <div className="flex items-start gap-3">
        <span
          aria-hidden
          className={cn(
            'mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-full',
            channelMeta.tone,
          )}
        >
          <Icon className="size-3.5" aria-hidden />
        </span>
        <div className="min-w-0 flex-1 space-y-1.5">
          <div className="flex items-center gap-2 flex-wrap">
            <span className="text-sm font-medium text-foreground">{channelMeta.label}</span>
            <Badge variant="outline" className="text-[10px] capitalize">
              {entry.direction}
            </Badge>
            <span className="text-xs text-muted-foreground">
              {format(new Date(entry.occurredAt), 'MMM d, yyyy · HH:mm')}
            </span>
            <span className="text-xs text-muted-foreground">
              ({formatDistanceToNowStrict(new Date(entry.occurredAt))} ago)
            </span>
          </div>
          <p className="text-sm text-foreground whitespace-pre-wrap">{entry.summary}</p>
          {entry.followUpAt && (
            <p className="inline-flex items-center gap-1.5 rounded-md border border-amber-200 bg-amber-50 px-2 py-0.5 text-xs text-amber-900">
              <Bell className="size-3" aria-hidden />
              Follow up {format(new Date(entry.followUpAt), 'MMM d, yyyy')} (reminder created)
            </p>
          )}
        </div>
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button variant="ghost" size="icon" className="h-7 w-7" aria-label="Row actions">
              <MoreVertical className="h-4 w-4" aria-hidden />
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end">
            <DropdownMenuItem onClick={() => onEdit(entry)}>
              <Pencil className="mr-2 size-3.5" aria-hidden />
              Edit
            </DropdownMenuItem>
            <DropdownMenuItem
              className="text-destructive"
              disabled={deleteMutation.isPending}
              onClick={async () => {
                const ok = await confirm({
                  title: 'Delete contact log entry',
                  description: 'This cannot be undone.',
                  confirmLabel: 'Delete',
                });
                if (ok) deleteMutation.mutate();
              }}
            >
              <Trash2 className="mr-2 size-3.5" aria-hidden />
              Delete
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      </div>
      {confirmDialog}
    </li>
  );
}

// ─── Empty state ─────────────────────────────────────────────────────────────

function EmptyState({ onAdd }: { onAdd: () => void }) {
  return (
    <div className="rounded-xl border border-dashed bg-muted/20 p-8 text-center">
      <div className="mx-auto flex size-12 items-center justify-center rounded-full bg-background text-muted-foreground">
        <Phone className="size-5" aria-hidden />
      </div>
      <h3 className="mt-3 text-sm font-medium text-foreground">No contact logged yet</h3>
      <p className="mt-1 text-xs text-muted-foreground">
        Record every call, email, and meeting so the team has full context the next time someone
        picks up the deal.
      </p>
      <Button size="sm" onClick={onAdd} className="mt-4 gap-1.5">
        <Plus className="size-3.5" aria-hidden />
        Log first contact
      </Button>
    </div>
  );
}

// ─── Compose / edit sheet ───────────────────────────────────────────────────

// Exported for §1.4 — interest-detail-header.tsx mounts this sheet
// directly via a "Log contact" quick-action button (sibling to the
// Email / Call / WhatsApp pills) so the rep doesn't have to navigate
// to the Contact log tab first.
export function ComposeDialog(props: {
  interestId: string;
  existing?: ContactLogEntry;
  open: boolean;
  onOpenChange: (open: boolean) => void;
}) {
  // Key-based remount: body keyed on open + existing.id so useState
  // initializers re-run each time the dialog opens with a new row.
  // Replaces the prior useEffect(setState, [open, existing]) sync.
  return (
    <ComposeDialogBody
      key={props.open ? `open:${props.existing?.id ?? 'new'}` : 'closed'}
      {...props}
    />
  );
}

function ComposeDialogBody({
  interestId,
  existing,
  open,
  onOpenChange,
}: {
  interestId: string;
  existing?: ContactLogEntry;
  open: boolean;
  onOpenChange: (open: boolean) => void;
}) {
  const queryClient = useQueryClient();
  const isEdit = !!existing;

  const defaultOccurredAt = useMemo(() => {
    if (existing) return localIsoString(existing.occurredAt);
    return localIsoString(new Date().toISOString());
  }, [existing]);

  const [occurredAt, setOccurredAt] = useState<string>(defaultOccurredAt);
  const [channel, setChannel] = useState<Channel>(existing?.channel ?? 'phone');
  const [direction, setDirection] = useState<Direction>(existing?.direction ?? 'outbound');
  const [summary, setSummary] = useState<string>(existing?.summary ?? '');
  const [followUpAt, setFollowUpAt] = useState<string>(
    existing?.followUpAt ? localIsoString(existing.followUpAt) : '',
  );
  const [templateUsed, setTemplateUsed] = useState<Template | null>(
    (existing?.templateUsed as Template | undefined) ?? null,
  );
  // Voice transcript is captured separately so an edit to summary doesn't
  // overwrite the rep's original raw utterance. Preserved on the row.
  const [voiceTranscript, setVoiceTranscript] = useState<string>(existing?.voiceTranscript ?? '');

  const voice = useVoiceTranscription();
  // Append committed transcript chunks into the summary as the rep speaks.
  // We diff against the previous final transcript so we only append the new
  // tail — otherwise the entire transcript gets re-pasted on every event.
  const previousFinalRef = useRef<string>('');
  useEffect(() => {
    const prev = previousFinalRef.current;
    if (voice.transcript === prev) return;
    const added = voice.transcript.slice(prev.length).trim();
    if (added.length === 0) {
      previousFinalRef.current = voice.transcript;
      return;
    }
    setSummary((prevSummary) => {
      const sep =
        prevSummary && !prevSummary.endsWith(' ') && !prevSummary.endsWith('\n') ? ' ' : '';
      return prevSummary + sep + added;
    });
    setVoiceTranscript((prev2) => (prev2 ? `${prev2} ${added}` : added));
    previousFinalRef.current = voice.transcript;
  }, [voice.transcript]);

  function applyTemplate(t: Template) {
    const seed = TEMPLATE_SEEDS[t];
    setChannel(seed.channel);
    setDirection(seed.direction);
    // Don't clobber if the rep already typed something — append a divider
    // so the template scaffolds the *next* block.
    setSummary((cur) => (cur.trim().length === 0 ? seed.summary : `${cur}\n\n${seed.summary}`));
    setTemplateUsed(t);
  }

  const mutation = useMutation({
    mutationFn: async () => {
      const body = {
        occurredAt: new Date(occurredAt).toISOString(),
        channel,
        direction,
        summary,
        voiceTranscript: voiceTranscript.trim().length > 0 ? voiceTranscript : null,
        templateUsed,
        followUpAt: followUpAt ? new Date(followUpAt).toISOString() : null,
      };
      if (isEdit) {
        return apiFetch(`/api/v1/contact-log/${existing!.id}`, {
          method: 'PATCH',
          body,
        });
      }
      return apiFetch(`/api/v1/interests/${interestId}/contact-log`, {
        method: 'POST',
        body,
      });
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['interests', interestId, 'contact-log'] });
      // Bump the parent interest cache so the "Last contact" header chip
      // updates without a refresh.
      queryClient.invalidateQueries({ queryKey: ['interests', interestId] });
      toast.success(isEdit ? 'Contact log entry updated.' : 'Contact logged.');
      onOpenChange(false);
    },
    onError: (err) => toastError(err),
  });

  return (
    <Sheet open={open} onOpenChange={onOpenChange}>
      <SheetContent side="right" className="w-3/4 sm:max-w-md overflow-y-auto">
        <SheetHeader>
          <SheetTitle>{isEdit ? 'Edit contact log entry' : 'Log a contact'}</SheetTitle>
          <SheetDescription>
            Record the channel, the direction, and what was discussed. Optionally schedule a
            follow-up — a reminder will be created automatically.
          </SheetDescription>
        </SheetHeader>

        <div className="space-y-3 py-1">
          {/* Quick-template buttons. Tap one to seed the channel + direction
              + a starter summary so the rep can focus on the substance.
              Hidden when editing — templates are a fresh-entry affordance. */}
          {!isEdit ? (
            <div className="flex flex-wrap gap-1.5">
              {(Object.keys(TEMPLATE_SEEDS) as Template[]).map((t) => {
                const seed = TEMPLATE_SEEDS[t];
                const Icon = seed.icon;
                const active = templateUsed === t;
                return (
                  <button
                    key={t}
                    type="button"
                    onClick={() => applyTemplate(t)}
                    className={cn(
                      'inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium transition-colors',
                      active
                        ? 'border-sky-300 bg-sky-50 text-sky-800'
                        : 'border-border bg-muted/40 text-foreground hover:bg-muted',
                    )}
                  >
                    <Icon className="size-3" aria-hidden />
                    {seed.label}
                  </button>
                );
              })}
            </div>
          ) : null}

          <div className="grid grid-cols-2 gap-2">
            <div className="space-y-1">
              <Label htmlFor="cl-channel">Channel</Label>
              <Select value={channel} onValueChange={(v) => setChannel(v as Channel)}>
                <SelectTrigger id="cl-channel">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  {(Object.keys(CHANNEL_META) as Channel[]).map((c) => (
                    <SelectItem key={c} value={c}>
                      {CHANNEL_META[c].label}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
            <div className="space-y-1">
              <Label htmlFor="cl-direction">Direction</Label>
              <Select value={direction} onValueChange={(v) => setDirection(v as Direction)}>
                <SelectTrigger id="cl-direction">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="outbound">Outbound (you reached out)</SelectItem>
                  <SelectItem value="inbound">Inbound (they reached out)</SelectItem>
                </SelectContent>
              </Select>
            </div>
          </div>

          <div className="space-y-1">
            <Label htmlFor="cl-occurred">When did the conversation happen?</Label>
            <Input
              id="cl-occurred"
              type="datetime-local"
              value={occurredAt}
              onChange={(e) => setOccurredAt(e.target.value)}
            />
          </div>

          <div className="space-y-1">
            <div className="flex items-center justify-between">
              <Label htmlFor="cl-summary">Summary</Label>
              {voice.supported ? (
                <button
                  type="button"
                  aria-label={
                    voice.isListening ? 'Stop voice transcription' : 'Start voice transcription'
                  }
                  onClick={() => (voice.isListening ? voice.stop() : voice.start())}
                  className={cn(
                    'inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[11px] font-medium transition-colors',
                    voice.isListening
                      ? 'border-rose-300 bg-rose-50 text-rose-800 animate-pulse'
                      : 'border-border bg-muted/40 text-muted-foreground hover:bg-muted',
                  )}
                >
                  {voice.isListening ? (
                    <>
                      <Mic className="size-3" aria-hidden />
                      Recording…
                    </>
                  ) : (
                    <>
                      <MicOff className="size-3" aria-hidden />
                      Voice
                    </>
                  )}
                </button>
              ) : (
                <span
                  title="Voice transcription isn't supported in this browser."
                  className="inline-flex items-center gap-1 text-[11px] text-muted-foreground"
                >
                  <MicOff className="size-3" aria-hidden />
                  Voice unavailable
                </span>
              )}
            </div>
            <Textarea
              id="cl-summary"
              placeholder="e.g. Confirmed yacht size, asked about tax structure, said they'll respond after their accountant reviews."
              rows={4}
              value={summary}
              onChange={(e) => setSummary(e.target.value)}
            />
            {voice.isListening && voice.interim ? (
              <p className="text-[11px] italic text-muted-foreground">{voice.interim}…</p>
            ) : null}
            {voice.error ? (
              <p className="text-[11px] text-rose-700">Voice error: {voice.error}</p>
            ) : null}
          </div>

          <div className="space-y-2 rounded-md border bg-muted/30 p-3">
            <label
              className="flex items-center gap-2 text-sm font-medium cursor-pointer select-none"
              htmlFor="cl-followup-toggle"
            >
              <Checkbox
                id="cl-followup-toggle"
                checked={!!followUpAt}
                onCheckedChange={(v) => {
                  if (v) {
                    // Default to a week from now @ 09:00 local so reps get a
                    // usable cadence without having to type a date.
                    const d = new Date();
                    d.setDate(d.getDate() + 7);
                    d.setHours(9, 0, 0, 0);
                    const tz = d.getTimezoneOffset() * 60_000;
                    setFollowUpAt(new Date(d.getTime() - tz).toISOString().slice(0, 16));
                  } else {
                    setFollowUpAt('');
                  }
                }}
              />
              Add follow-up reminder?
            </label>
            {followUpAt ? (
              <div className="space-y-1 pl-6">
                <Label htmlFor="cl-followup" className="text-xs text-muted-foreground">
                  Remind me on
                </Label>
                <Input
                  id="cl-followup"
                  type="datetime-local"
                  value={followUpAt}
                  onChange={(e) => setFollowUpAt(e.target.value)}
                  className="max-w-xs"
                />
                <p className="text-[11px] text-muted-foreground">
                  A reminder is created on this interest for the time above.
                </p>
              </div>
            ) : null}
          </div>
        </div>

        <SheetFooter>
          <Button
            variant="outline"
            onClick={() => onOpenChange(false)}
            disabled={mutation.isPending}
          >
            Cancel
          </Button>
          <Button
            onClick={() => mutation.mutate()}
            disabled={mutation.isPending || summary.trim().length === 0}
          >
            {mutation.isPending ? 'Saving…' : isEdit ? 'Save changes' : 'Log contact'}
          </Button>
        </SheetFooter>
      </SheetContent>
    </Sheet>
  );
}

/**
 * Convert an ISO string into the `YYYY-MM-DDTHH:mm` format that
 * `<input type="datetime-local">` expects, in the user's local
 * timezone. (Browsers don't accept the trailing `Z` in this input
 * type and reject anything with a timezone offset.)
 */
function localIsoString(iso: string): string {
  const d = new Date(iso);
  const pad = (n: number) => String(n).padStart(2, '0');
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
