'use client';

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

import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { CountryCombobox } from '@/components/shared/country-combobox';
import { SubdivisionCombobox } from '@/components/shared/subdivision-combobox';
import { InlineEditableField } from '@/components/shared/inline-editable-field';
import { apiFetch } from '@/lib/api/client';
import type { CountryCode } from '@/lib/i18n/countries';
import { getCountryName } from '@/lib/i18n/countries';
import { getSubdivisionName } from '@/lib/i18n/subdivisions';
import { cn } from '@/lib/utils';

export interface Address {
  id: string;
  label: string;
  streetAddress: string | null;
  city: string | null;
  subdivisionIso: string | null;
  postalCode: string | null;
  countryIso: string | null;
  isPrimary: boolean;
}

type AddressPatch = Partial<Omit<Address, 'id'>>;

interface AddressesEditorProps {
  /** Base API endpoint, e.g. `/api/v1/clients/abc/addresses` */
  endpoint: string;
  /** React-Query invalidation key for the parent entity. */
  invalidateKey: readonly unknown[];
  addresses: Address[];
}

export function AddressesEditor({ endpoint, invalidateKey, addresses }: AddressesEditorProps) {
  const qc = useQueryClient();
  const [adding, setAdding] = useState(false);

  function invalidate() {
    qc.invalidateQueries({ queryKey: invalidateKey });
  }

  const updateMutation = useMutation({
    mutationFn: async ({ id, patch }: { id: string; patch: AddressPatch }) =>
      apiFetch(`${endpoint}/${id}`, { method: 'PATCH', body: patch }),
    onSuccess: invalidate,
  });

  const addMutation = useMutation({
    mutationFn: async (data: AddressPatch) => apiFetch(endpoint, { method: 'POST', body: data }),
    onSuccess: invalidate,
  });

  const removeMutation = useMutation({
    mutationFn: async (id: string) => apiFetch(`${endpoint}/${id}`, { method: 'DELETE' }),
    onSuccess: invalidate,
  });

  return (
    <div className="space-y-2">
      {addresses.length === 0 && !adding && (
        <p className="text-sm text-muted-foreground">No addresses yet</p>
      )}

      {addresses.map((a) => (
        <AddressCard
          key={a.id}
          address={a}
          onUpdate={(patch) => updateMutation.mutateAsync({ id: a.id, patch })}
          onRemove={async () => {
            if (!confirm('Remove this address?')) return;
            await removeMutation.mutateAsync(a.id);
          }}
        />
      ))}

      {adding ? (
        <NewAddressForm
          isFirst={addresses.length === 0}
          onCancel={() => setAdding(false)}
          onSave={async (data) => {
            await addMutation.mutateAsync(data);
            setAdding(false);
          }}
        />
      ) : (
        <Button
          type="button"
          variant="outline"
          size="sm"
          onClick={() => setAdding(true)}
          className="w-full justify-center"
          data-testid="add-address-button"
        >
          <Plus className="h-3.5 w-3.5 mr-1.5" />
          Add address
        </Button>
      )}
    </div>
  );
}

function AddressCard({
  address,
  onUpdate,
  onRemove,
}: {
  address: Address;
  onUpdate: (patch: AddressPatch) => Promise<unknown>;
  onRemove: () => void;
}) {
  async function togglePrimary() {
    if (address.isPrimary) return; // already primary; demoting via toggle would orphan all
    try {
      await onUpdate({ isPrimary: true });
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'Failed to update');
    }
  }

  return (
    <div className="group rounded-lg border bg-muted/30 p-3 text-sm space-y-2">
      <div className="flex items-center gap-2">
        <MapPin className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
        <div className="flex-1 min-w-0">
          <InlineEditableField
            value={address.label}
            placeholder="Label (e.g. Home, Office)"
            onSave={async (v) => {
              if (!v) {
                toast.error('Label is required');
                return;
              }
              await onUpdate({ label: v });
            }}
          />
        </div>
        <button
          type="button"
          onClick={togglePrimary}
          title={address.isPrimary ? 'Primary address' : 'Make primary'}
          className={cn(
            'p-1 rounded hover:bg-background/60 transition-colors',
            address.isPrimary ? 'text-primary' : 'text-muted-foreground/50',
          )}
          data-testid="address-primary-toggle"
        >
          <Star className={cn('h-3.5 w-3.5', address.isPrimary && 'fill-current')} />
        </button>
        <button
          type="button"
          onClick={onRemove}
          title="Remove"
          className="p-1 rounded text-muted-foreground/50 hover:text-destructive hover:bg-background/60 opacity-0 group-hover:opacity-100 transition-all"
          data-testid="address-remove-button"
        >
          <Trash2 className="h-3.5 w-3.5" />
        </button>
      </div>

      <div className="grid grid-cols-1 md:grid-cols-2 gap-x-3 gap-y-1 pl-5">
        <Field label="Street">
          <InlineEditableField
            value={address.streetAddress}
            placeholder="123 Main St"
            onSave={async (v) => {
              await onUpdate({ streetAddress: v });
            }}
          />
        </Field>
        <Field label="City">
          <InlineEditableField
            value={address.city}
            placeholder="City"
            onSave={async (v) => {
              await onUpdate({ city: v });
            }}
          />
        </Field>
        <Field label="Country">
          <CountryFieldInline
            value={address.countryIso}
            onSave={async (iso) => {
              // Clear subdivision if country changes — codes are scoped per country.
              const patch: AddressPatch = { countryIso: iso };
              if (iso !== address.countryIso) patch.subdivisionIso = null;
              await onUpdate(patch);
            }}
          />
        </Field>
        <Field label="Region">
          <SubdivisionFieldInline
            value={address.subdivisionIso}
            country={(address.countryIso as CountryCode | null) ?? null}
            onSave={async (code) => {
              await onUpdate({ subdivisionIso: code });
            }}
          />
        </Field>
        <Field label="Postal Code">
          <InlineEditableField
            value={address.postalCode}
            placeholder="ZIP / Postal"
            onSave={async (v) => {
              await onUpdate({ postalCode: v });
            }}
          />
        </Field>
      </div>
    </div>
  );
}

function Field({ label, children }: { label: string; children: React.ReactNode }) {
  return (
    <div className="flex items-baseline gap-2">
      <span className="text-xs text-muted-foreground w-20 shrink-0">{label}</span>
      <span className="flex-1 min-w-0">{children}</span>
    </div>
  );
}

/** Regional-indicator emoji flag for an ISO alpha-2 code (e.g. 'FR' → 🇫🇷). */
function flagEmoji(code: string | null | undefined): string {
  if (!code || code.length !== 2) return '';
  const A = 0x1f1e6;
  const a = 'A'.charCodeAt(0);
  return String.fromCodePoint(A + code.charCodeAt(0) - a, A + code.charCodeAt(1) - a);
}

function CountryFieldInline({
  value,
  onSave,
}: {
  value: string | null;
  onSave: (iso: string | null) => Promise<void>;
}) {
  const [editing, setEditing] = useState(false);
  // Tracks whether a value was picked this edit cycle so the open-change
  // handler doesn't double-exit while commit is still in flight.
  const pickedRef = useRef(false);

  if (editing) {
    return (
      <CountryCombobox
        value={value ?? null}
        onChange={async (iso) => {
          pickedRef.current = true;
          setEditing(false);
          await onSave(iso ?? null);
        }}
        clearable
        className="w-full"
        // Drop the user straight into the picker — no extra click on the
        // trigger required.
        defaultOpen
        onOpenChange={(open) => {
          // Auto-exit edit mode when the popover closes without a pick so
          // the user isn't stuck staring at a "Select country…" trigger.
          if (!open && !pickedRef.current) setEditing(false);
          if (open) pickedRef.current = false;
        }}
      />
    );
  }
  const display = value ? `${flagEmoji(value)} ${getCountryName(value, 'en')}` : null;
  return (
    <button
      type="button"
      onClick={() => setEditing(true)}
      className="text-left w-full hover:bg-background/60 rounded px-1 py-0.5 -mx-1 -my-0.5 truncate"
    >
      {display ?? <span className="text-muted-foreground italic">Not set</span>}
    </button>
  );
}

function SubdivisionFieldInline({
  value,
  country,
  onSave,
}: {
  value: string | null;
  country: CountryCode | null;
  onSave: (code: string | null) => Promise<void>;
}) {
  const [editing, setEditing] = useState(false);
  const pickedRef = useRef(false);

  if (editing) {
    return (
      <SubdivisionCombobox
        value={value ?? null}
        country={country}
        onChange={async (code) => {
          pickedRef.current = true;
          setEditing(false);
          await onSave(code ?? null);
        }}
        clearable
        className="w-full"
        defaultOpen
        onOpenChange={(open) => {
          if (!open && !pickedRef.current) setEditing(false);
          if (open) pickedRef.current = false;
        }}
      />
    );
  }
  if (!country) {
    return <span className="text-muted-foreground italic text-xs">Pick country first</span>;
  }
  const display = value ? getSubdivisionName(value) : null;
  return (
    <button
      type="button"
      onClick={() => setEditing(true)}
      className="text-left w-full hover:bg-background/60 rounded px-1 py-0.5 -mx-1 -my-0.5 truncate"
    >
      {display ?? <span className="text-muted-foreground italic">Not set</span>}
    </button>
  );
}

function NewAddressForm({
  onSave,
  onCancel,
  isFirst,
}: {
  onSave: (data: AddressPatch) => Promise<void>;
  onCancel: () => void;
  isFirst: boolean;
}) {
  const [label, setLabel] = useState('Primary');
  const [streetAddress, setStreet] = useState('');
  const [city, setCity] = useState('');
  const [countryIso, setCountryIso] = useState<string | null>(null);
  const [subdivisionIso, setSubdivisionIso] = useState<string | null>(null);
  const [postalCode, setPostal] = useState('');
  const [makePrimary, setMakePrimary] = useState(isFirst);
  const [saving, setSaving] = useState(false);

  async function submit() {
    if (!label.trim()) {
      toast.error('Label is required');
      return;
    }
    setSaving(true);
    try {
      await onSave({
        label: label.trim(),
        streetAddress: streetAddress.trim() || null,
        city: city.trim() || null,
        countryIso,
        subdivisionIso,
        postalCode: postalCode.trim() || null,
        isPrimary: makePrimary,
      });
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'Failed to add address');
    } finally {
      setSaving(false);
    }
  }

  return (
    <div className="rounded-lg border bg-muted/30 p-3 text-sm space-y-2">
      <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
        <Input
          value={label}
          onChange={(e) => setLabel(e.target.value)}
          placeholder="Label (Home, Office)"
          className="h-8"
          autoFocus
          disabled={saving}
        />
        <Input
          value={streetAddress}
          onChange={(e) => setStreet(e.target.value)}
          placeholder="Street address"
          className="h-8"
          disabled={saving}
        />
        <Input
          value={city}
          onChange={(e) => setCity(e.target.value)}
          placeholder="City"
          className="h-8"
          disabled={saving}
        />
        <CountryCombobox
          value={countryIso}
          onChange={(iso) => {
            setCountryIso(iso ?? null);
            setSubdivisionIso(null);
          }}
          clearable
          placeholder="Country"
        />
        <SubdivisionCombobox
          value={subdivisionIso}
          country={(countryIso as CountryCode | null) ?? null}
          onChange={(code) => setSubdivisionIso(code ?? null)}
          clearable
          placeholder="Region (optional)"
        />
        <Input
          value={postalCode}
          onChange={(e) => setPostal(e.target.value)}
          placeholder="Postal code"
          className="h-8"
          disabled={saving}
        />
      </div>
      <div className="flex items-center justify-between">
        <label className="flex items-center gap-2 text-xs text-muted-foreground">
          <input
            type="checkbox"
            checked={makePrimary}
            onChange={(e) => setMakePrimary(e.target.checked)}
            disabled={saving}
          />
          Set as primary address
        </label>
        <div className="flex gap-2">
          <Button type="button" size="sm" variant="ghost" onClick={onCancel} disabled={saving}>
            Cancel
          </Button>
          <Button type="button" size="sm" onClick={submit} disabled={saving}>
            {saving ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : 'Save'}
          </Button>
        </div>
      </div>
    </div>
  );
}
