'use client';

import { useState } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { ChevronDown, Loader2, Plus, Star, Trash2 } from 'lucide-react';
import { toast } from 'sonner';

import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { InlineEditableField } from '@/components/shared/inline-editable-field';
import { InlinePhoneField } from '@/components/shared/inline-phone-field';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { apiFetch } from '@/lib/api/client';
import { toastError } from '@/lib/api/toast-error';
import { cn } from '@/lib/utils';
import { parsePhone } from '@/lib/i18n/phone';

type Channel = 'email' | 'phone';

export interface ContactRow {
  id: string;
  channel: 'email' | 'phone' | 'whatsapp' | 'other';
  value: string;
  valueE164: string | null;
  label: string | null;
  isPrimary: boolean;
}

interface Props {
  clientId: string;
  /**
   * Channel filter - picker shows only `email` (or `phone` + `whatsapp` for
   * phone-style channels). Edits / promotions stay scoped to the chosen
   * channel.
   */
  channel: Channel;
  /** Server-resolved primary contact for this channel (drives the inline
   *  value rendering when the picker isn't open). */
  primaryContactId: string | null;
  primaryValue: string | null;
  /** Phone channel only - E.164 form + ISO-3166-1 alpha-2 country code so the
   *  inline phone editor can preserve the national-format roundtrip. */
  primaryValueE164?: string | null;
  primaryValueCountry?: string | null;
  /** Query keys to invalidate after any mutation succeeds - the parent
   *  detail view is usually keyed on `['interest', interestId]` or
   *  `['clients', clientId]` so the picker can't hard-code which to bump. */
  invalidateKeys?: ReadonlyArray<readonly unknown[]>;
}

/**
 * Combobox-style editor for the Email + Phone rows on the Interest /
 * Client Overview. Renders the current primary value with an inline
 * edit-on-click; the chevron opens a popover listing every contact in
 * the channel so the rep can:
 *   - promote a different contact to primary
 *   - edit any contact's value inline
 *   - add a new contact (defaults to non-primary; can be flagged primary
 *     at create time)
 *   - delete a non-primary contact
 *
 * Backed by:
 *   GET  /api/v1/clients/[id]/contacts
 *   POST /api/v1/clients/[id]/contacts
 *   PATCH /api/v1/clients/[id]/contacts/[contactId]
 *   DELETE /api/v1/clients/[id]/contacts/[contactId]
 *   POST /api/v1/clients/[id]/contacts/[contactId]/promote-to-primary
 *
 * Phone channel shows `whatsapp` rows alongside `phone` so the picker
 * works as the rep's "all the ways I can call this client" surface.
 */
export function ClientChannelEditor({
  clientId,
  channel,
  primaryContactId,
  primaryValue,
  primaryValueE164,
  primaryValueCountry,
  invalidateKeys = [],
}: Props) {
  const qc = useQueryClient();
  const [open, setOpen] = useState(false);

  const acceptedChannels: ContactRow['channel'][] =
    channel === 'email' ? ['email'] : ['phone', 'whatsapp'];

  const { data, isLoading } = useQuery<{ data: ContactRow[] }>({
    queryKey: ['client-contacts', clientId, channel],
    queryFn: () => apiFetch<{ data: ContactRow[] }>(`/api/v1/clients/${clientId}/contacts`),
    enabled: open,
    staleTime: 30_000,
  });

  const contacts = (data?.data ?? []).filter((c) => acceptedChannels.includes(c.channel));

  function invalidate() {
    void qc.invalidateQueries({ queryKey: ['client-contacts', clientId, channel] });
    for (const key of invalidateKeys) {
      void qc.invalidateQueries({ queryKey: key as unknown[] });
    }
  }

  const formatPhone = (v: string) => parsePhone(v).international ?? v;
  const displayValue = (row: ContactRow) =>
    row.channel === 'phone' || row.channel === 'whatsapp' ? formatPhone(row.value) : row.value;

  return (
    <div className="flex items-center gap-1">
      {primaryContactId ? (
        channel === 'phone' ? (
          // Phone-specific editor: country picker + national-format input so
          // the rep doesn't have to type the +44 / +1 prefix manually. The
          // service auto-derives `valueE164` + `valueCountry` from the
          // submitted shape, then the `value` (display) form is updated.
          <InlinePhoneField
            e164={primaryValueE164 ?? primaryValue ?? null}
            country={primaryValueCountry ?? null}
            onSave={async (next) => {
              try {
                await apiFetch(`/api/v1/clients/${clientId}/contacts/${primaryContactId}`, {
                  method: 'PATCH',
                  body: {
                    value: next.e164 ?? '',
                    valueE164: next.e164 ?? null,
                    valueCountry: next.country,
                  },
                });
                invalidate();
              } catch (err) {
                toastError(err);
              }
            }}
          />
        ) : (
          <InlineEditableField
            variant="text"
            value={primaryValue ?? ''}
            onSave={async (next) => {
              try {
                await apiFetch(`/api/v1/clients/${clientId}/contacts/${primaryContactId}`, {
                  method: 'PATCH',
                  body: { value: next },
                });
                invalidate();
              } catch (err) {
                toastError(err);
              }
            }}
          />
        )
      ) : (
        <span className="text-muted-foreground">-</span>
      )}
      <Popover open={open} onOpenChange={setOpen}>
        <PopoverTrigger asChild>
          <Button
            type="button"
            variant="ghost"
            size="icon"
            className="h-6 w-6 shrink-0 text-muted-foreground hover:text-foreground"
            aria-label={`More ${channel} contacts`}
          >
            <ChevronDown className="h-3.5 w-3.5" aria-hidden />
          </Button>
        </PopoverTrigger>
        <PopoverContent align="start" className="w-80 p-2">
          <div className="mb-1 px-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground">
            {channel === 'email' ? 'Email contacts' : 'Phone & WhatsApp contacts'}
          </div>
          {isLoading ? (
            <div className="flex items-center gap-2 px-2 py-3 text-sm text-muted-foreground">
              <Loader2 className="h-3.5 w-3.5 animate-spin" aria-hidden />
              Loading…
            </div>
          ) : contacts.length === 0 ? (
            <div className="px-2 py-3 text-sm text-muted-foreground">
              No {channel} contacts yet.
            </div>
          ) : (
            <ul className="divide-y">
              {contacts.map((c) => (
                <ContactRowItem
                  key={c.id}
                  clientId={clientId}
                  row={c}
                  invalidate={invalidate}
                  displayValue={displayValue(c)}
                />
              ))}
            </ul>
          )}
          <AddContactRow
            clientId={clientId}
            channel={channel}
            existingCount={contacts.length}
            invalidate={invalidate}
          />
        </PopoverContent>
      </Popover>
    </div>
  );
}

function ContactRowItem({
  clientId,
  row,
  displayValue,
  invalidate,
}: {
  clientId: string;
  row: ContactRow;
  displayValue: string;
  invalidate: () => void;
}) {
  const promote = useMutation({
    mutationFn: async () => {
      await apiFetch(`/api/v1/clients/${clientId}/contacts/${row.id}/promote-to-primary`, {
        method: 'POST',
      });
    },
    onSuccess: () => {
      toast.success('Primary updated');
      invalidate();
    },
    onError: (err) => toastError(err),
  });
  const remove = useMutation({
    mutationFn: async () => {
      await apiFetch(`/api/v1/clients/${clientId}/contacts/${row.id}`, { method: 'DELETE' });
    },
    onSuccess: () => {
      toast.success('Contact removed');
      invalidate();
    },
    onError: (err) => toastError(err),
  });

  return (
    <li className="px-1 py-1.5">
      <div className="flex items-center justify-between gap-2">
        <div className="flex min-w-0 items-center gap-1.5">
          {row.isPrimary ? (
            <Star
              className="h-3 w-3 shrink-0 text-amber-500"
              aria-label="Primary"
              aria-hidden={false}
            />
          ) : (
            <span className="h-3 w-3 shrink-0" aria-hidden />
          )}
          <span className="truncate text-sm">{displayValue}</span>
          {row.label ? (
            <span className="shrink-0 rounded-sm bg-muted px-1 text-[10px] text-muted-foreground">
              {row.label}
            </span>
          ) : null}
        </div>
        <div className="flex items-center gap-0.5">
          {!row.isPrimary ? (
            <Button
              type="button"
              variant="ghost"
              size="sm"
              onClick={() => promote.mutate()}
              disabled={promote.isPending}
              title="Set as primary"
              className={cn('h-6 px-1.5 text-[11px]')}
            >
              Make primary
            </Button>
          ) : null}
          {!row.isPrimary ? (
            <Button
              type="button"
              variant="ghost"
              size="icon"
              className="h-6 w-6 text-muted-foreground hover:text-destructive"
              onClick={() => remove.mutate()}
              disabled={remove.isPending}
              aria-label="Remove contact"
              title="Remove this contact"
            >
              <Trash2 className="h-3 w-3" aria-hidden />
            </Button>
          ) : null}
        </div>
      </div>
    </li>
  );
}

function AddContactRow({
  clientId,
  channel,
  existingCount,
  invalidate,
}: {
  clientId: string;
  channel: Channel;
  existingCount: number;
  invalidate: () => void;
}) {
  const [adding, setAdding] = useState(false);
  const [value, setValue] = useState('');
  const [setPrimary, setSetPrimary] = useState(false);

  const create = useMutation({
    mutationFn: async () => {
      const v = value.trim();
      if (!v) throw new Error(`Enter a${channel === 'email' ? 'n email' : ' phone number'}.`);
      await apiFetch(`/api/v1/clients/${clientId}/contacts`, {
        method: 'POST',
        body: { channel, value: v, isPrimary: setPrimary || existingCount === 0 },
      });
    },
    onSuccess: () => {
      toast.success(`${channel === 'email' ? 'Email' : 'Phone'} added`);
      setValue('');
      setSetPrimary(false);
      setAdding(false);
      invalidate();
    },
    onError: (err) => toastError(err),
  });

  if (!adding) {
    return (
      <Button
        type="button"
        variant="ghost"
        size="sm"
        className="mt-1 w-full justify-start gap-1.5 text-xs"
        onClick={() => setAdding(true)}
      >
        <Plus className="h-3.5 w-3.5" aria-hidden />
        Add {channel === 'email' ? 'an email' : 'a phone number'}
      </Button>
    );
  }
  return (
    <div className="mt-1 space-y-2 rounded-md border bg-muted/30 p-2">
      <div className="space-y-1">
        <Label htmlFor={`new-${channel}`} className="text-[11px] uppercase tracking-wide">
          New {channel}
        </Label>
        <Input
          id={`new-${channel}`}
          type={channel === 'email' ? 'email' : 'tel'}
          value={value}
          onChange={(e) => setValue(e.target.value)}
          placeholder={channel === 'email' ? 'name@example.com' : '+1 555 123 4567'}
          className="h-8 text-sm"
        />
      </div>
      <label className="flex items-center gap-2 text-xs text-muted-foreground">
        <input
          type="checkbox"
          checked={setPrimary}
          onChange={(e) => setSetPrimary(e.target.checked)}
        />
        Set as primary
      </label>
      <div className="flex items-center justify-end gap-1">
        <Button
          type="button"
          variant="ghost"
          size="sm"
          onClick={() => {
            setAdding(false);
            setValue('');
            setSetPrimary(false);
          }}
          disabled={create.isPending}
        >
          Cancel
        </Button>
        <Button
          type="button"
          size="sm"
          onClick={() => create.mutate()}
          disabled={create.isPending || !value.trim()}
        >
          {create.isPending ? <Loader2 className="mr-1 h-3 w-3 animate-spin" aria-hidden /> : null}
          Add
        </Button>
      </div>
    </div>
  );
}
