'use client';

import { useEffect, useState, useCallback } from 'react';
import {
  Megaphone,
  Plus,
  Loader2,
  Trash2,
  Pencil,
  RefreshCw,
  Users as UsersIcon,
  Crown,
  TrendingUp,
  RotateCcw,
  UserPlus,
} from 'lucide-react';
import Link from 'next/link';
import FeatureGuard from '@client/components/FeatureGuard';
import { isFeatureLockedError } from '@client/utils/isFeatureLockedError';
import { isForbiddenError } from '@client/utils/isForbiddenError';
import PermissionDenied from '@client/components/ui/PermissionDenied';
import { usePageHeader } from '@client/contexts/PageHeaderContext';
import { useLanguage } from '@client/contexts/LanguageContext';
import { useCurrency } from '@client/hooks/useCurrency';
import { toast } from 'sonner';
import {
  listSegments,
  createSegment,
  updateSegment,
  deleteSegment,
  previewSegment,
  listLinkedActiveCampaigns,
  ApiError,
  type Segment,
  type AudienceRules,
  type PreviewResult,
  type LinkedActiveCampaign,
} from '@client/api/marketing';
import { listBranches, type Branch } from '@client/api/branches';
import { RuleEditor } from '@client/components/marketing/RuleEditor';

const EMPTY_RULES: AudienceRules = { channel: 'any' };

interface Template {
  labelKey: string;
  label: string;
  suggestedNameKey: string;
  suggestedName: string;
  icon: React.ElementType;
  rules: AudienceRules;
}

const TEMPLATES: Template[] = [
  {
    labelKey: 'marketing.audience.tplVip',
    label: 'VIP regulars',
    suggestedNameKey: 'marketing.audience.tplVipName',
    suggestedName: 'VIP regulars',
    icon: Crown,
    rules: { channel: 'any', orderCountMin: 5, orderRecencyDays: 90 },
  },
  {
    labelKey: 'marketing.audience.tplSpenders',
    label: 'Big spenders',
    suggestedNameKey: 'marketing.audience.tplSpendersName',
    suggestedName: 'Big spenders',
    icon: TrendingUp,
    rules: { channel: 'any', lifetimeSpendMin: 250 },
  },
  {
    labelKey: 'marketing.audience.tplLapsed',
    label: 'Win back lapsed',
    suggestedNameKey: 'marketing.audience.tplLapsedName',
    suggestedName: 'Win back lapsed',
    icon: RotateCcw,
    rules: { channel: 'any', orderCountMin: 1, orderRecencyDays: 180 },
  },
  {
    labelKey: 'marketing.audience.tplNewcomers',
    label: 'Newcomers',
    suggestedNameKey: 'marketing.audience.tplNewcomersName',
    suggestedName: 'Newcomers',
    icon: UserPlus,
    rules: { channel: 'any', orderCountMin: 1, orderRecencyDays: 30 },
  },
];

// ─── Preview card ────────────────────────────────────────────────────────────

interface PreviewCardProps {
  preview: PreviewResult | null;
  previewing: boolean;
  hasBeenPreviewed: boolean;
  t: (key: string, fallback?: string) => string;
}

function PreviewCard({ preview, previewing, hasBeenPreviewed, t }: PreviewCardProps) {
  const count = preview?.count ?? 0;
  const showZeroHint = hasBeenPreviewed && !previewing && count === 0;

  return (
    <div className="flex items-center gap-3 p-4 rounded-xl bg-primary/5 border border-primary/20 min-w-[160px]">
      <UsersIcon
        size={22}
        className={`shrink-0 ${previewing ? 'text-primary/40' : 'text-primary'}`}
      />
      <div>
        <div className="flex items-baseline gap-1">
          {previewing ? (
            <span className="text-2xl font-bold text-primary/40 animate-pulse">…</span>
          ) : (
            <span className="text-2xl font-bold text-primary">{count.toLocaleString()}</span>
          )}
          <span className="text-xs text-gray-500 leading-tight">
            {t('marketing.audience.previewSuffix', 'reachable customers')}
          </span>
        </div>
        {showZeroHint && (
          <p className="text-xs text-gray-400 mt-0.5 max-w-[220px]">
            {t('marketing.audience.zeroHint', 'No matches yet — loosen a filter, or')}{' '}
            <Link href="/customers" className="underline hover:text-primary">
              {t('marketing.audience.zeroHintLink', 'add customers')}
            </Link>
            .
          </p>
        )}
      </div>
    </div>
  );
}

// ─── Main page ───────────────────────────────────────────────────────────────

function AudiencesPageInner() {
  const { t } = useLanguage();
  const { symbol: currencySymbol } = useCurrency();
  usePageHeader(
    t('marketing.audience.title', 'Audiences'),
    t('marketing.audience.subtitle', 'Saved customer segments for marketing campaigns')
  );

  const [segments, setSegments] = useState<Segment[]>([]);
  const [branches, setBranches] = useState<Branch[]>([]);
  const [loading, setLoading] = useState(true);
  const [forbidden, setForbidden] = useState(false);
  const [showForm, setShowForm] = useState(false);
  const [editingId, setEditingId] = useState<string | null>(null);
  const [name, setName] = useState('');
  const [description, setDescription] = useState('');
  const [rules, setRules] = useState<AudienceRules>(EMPTY_RULES);
  const [preview, setPreview] = useState<PreviewResult | null>(null);
  const [previewing, setPreviewing] = useState(false);
  const [hasBeenPreviewed, setHasBeenPreviewed] = useState(false);
  const [saving, setSaving] = useState(false);
  /** Inline error message for the Name field, e.g. when the backend rejects
   *  a duplicate name with a 400 ValidationError (Task #289). Cleared when
   *  the operator edits the field, opens/closes the form, or saves
   *  successfully. */
  const [nameError, setNameError] = useState<string | null>(null);

  const refresh = useCallback(async () => {
    setLoading(true);
    try {
      const [segs, br] = await Promise.all([listSegments(), listBranches({ limit: '100' })]);
      setSegments(segs);
      setBranches(br.branches);
    } catch (e) {
      if (isForbiddenError(e)) {
        setForbidden(true);
      } else if (!isFeatureLockedError(e)) {
        toast.error(
          e instanceof Error
            ? e.message
            : t('marketing.audience.loadFailed', 'Failed to load segments')
        );
      }
    } finally {
      setLoading(false);
    }
  }, [t]);

  useEffect(() => {
    void refresh();
  }, [refresh]);

  // Live preview — signal loading immediately, then debounce the actual request.
  useEffect(() => {
    if (!showForm) return;
    setPreviewing(true);
    const handle = setTimeout(async () => {
      try {
        const result = await previewSegment(rules, true);
        setPreview(result);
        setHasBeenPreviewed(true);
      } catch {
        /* preview fails silently */
      } finally {
        setPreviewing(false);
      }
    }, 400);
    return () => clearTimeout(handle);
  }, [rules, showForm]);

  function applyTemplate(tpl: Template) {
    setRules(tpl.rules);
    if (!name.trim()) setName(t(tpl.suggestedNameKey, tpl.suggestedName));
  }

  function resetForm() {
    setName('');
    setDescription('');
    setRules(EMPTY_RULES);
    setPreview(null);
    setHasBeenPreviewed(false);
    setEditingId(null);
    setNameError(null);
  }

  function openForm() {
    resetForm();
    setShowForm(true);
  }

  function openEditForm(segment: Segment) {
    setName(segment.name);
    setDescription(segment.description ?? '');
    setRules(segment.rules);
    setPreview(null);
    setHasBeenPreviewed(false);
    setEditingId(segment.id);
    // Clear any inline name error left over from a previous edit so the
    // freshly-loaded segment doesn't appear pre-flagged as invalid.
    setNameError(null);
    setShowForm(true);
  }

  function closeForm() {
    setShowForm(false);
    resetForm();
  }

  async function onSave() {
    if (!name.trim()) {
      const msg = t('marketing.audience.nameRequired', 'Audience name is required');
      setNameError(msg);
      return toast.error(msg);
    }
    setSaving(true);
    setNameError(null);
    try {
      if (editingId) {
        await updateSegment(editingId, { name: name.trim(), description: description.trim() || null, rules });
        toast.success(t('marketing.audience.updated', 'Audience updated'));
      } else {
        await createSegment({ name: name.trim(), description: description.trim() || null, rules });
        toast.success(t('marketing.audience.saved', 'Audience saved'));
      }
      closeForm();
      await refresh();
    } catch (e) {
      // Per-field validation errors (e.g. duplicate audience name from the
      // backend's `rethrowSegmentNameConflict`) are surfaced inline on the
      // offending input — keep the dialog open so the operator can fix the
      // name without losing the rest of their input. Anything else falls
      // through to the existing toast path.
      const fieldMessage =
        e instanceof ApiError ? e.fields?.name?.[0] : undefined;
      if (fieldMessage) {
        setNameError(fieldMessage);
      } else {
        toast.error(
          e instanceof Error
            ? e.message
            : t('marketing.audience.saveFailed', 'Failed to save audience')
        );
      }
    } finally {
      setSaving(false);
    }
  }

  async function onDelete(id: string) {
    let linkedActive: LinkedActiveCampaign[] = [];
    let preCheckFailed = false;
    try {
      linkedActive = await listLinkedActiveCampaigns(id);
    } catch {
      preCheckFailed = true;
    }

    let message: string;
    if (linkedActive.length === 0) {
      message = preCheckFailed
        ? t(
            'marketing.audience.confirmDelete',
            'Delete this audience? Campaigns that already launched are unaffected.'
          )
        : t(
            'marketing.audience.confirmDeleteNone',
            'Delete this audience? No active campaigns are using it. Campaigns that already launched are unaffected.'
          );
    } else {
      const shown = linkedActive.slice(0, 5);
      const namesBlock = shown.map((c) => `• ${c.name} (${c.status})`).join('\n');
      const moreSuffix =
        linkedActive.length > shown.length
          ? '\n' +
            t('marketing.audience.confirmDeleteMore', '…and {n} more').replace(
              '{n}',
              String(linkedActive.length - shown.length)
            )
          : '';
      message = t(
        'marketing.audience.confirmDeleteWithCampaigns',
        '{n} active campaign(s) will be detached and continue with a snapshot of these rules:\n\n{names}{more}\n\nDelete this audience?'
      )
        .replace('{n}', String(linkedActive.length))
        .replace('{names}', namesBlock)
        .replace('{more}', moreSuffix);
    }

    if (!confirm(message)) return;
    try {
      const { detachedCampaignIds } = await deleteSegment(id);
      if (detachedCampaignIds.length > 0) {
        toast.success(
          t(
            'marketing.audience.deletedWithDetach',
            'Audience deleted — {n} active campaign(s) detached and now use a snapshot of these rules.'
          ).replace('{n}', String(detachedCampaignIds.length))
        );
      } else {
        toast.success(t('marketing.audience.deleted', 'Audience deleted'));
      }
      await refresh();
    } catch (e) {
      toast.error(
        e instanceof Error
          ? e.message
          : t('marketing.audience.deleteFailed', 'Failed to delete audience')
      );
    }
  }

  if (forbidden) {
    return <PermissionDenied sectionName="the Marketing section" />;
  }

  return (
    <div className="p-6 space-y-6">
      <div className="flex justify-between items-center">
        <p className="text-sm text-gray-500">
          {t(
            'marketing.audience.intro',
            'Audiences define who receives a campaign. They re-evaluate at launch time, so the latest customer state is always used.'
          )}
        </p>
        <div className="flex gap-2">
          <button className="btn-ghost flex items-center gap-2" onClick={() => void refresh()}>
            <RefreshCw size={16} /> {t('common.refresh', 'Refresh')}
          </button>
          {!showForm && (
            <button className="btn-primary flex items-center gap-2" onClick={openForm}>
              <Plus size={16} /> {t('marketing.audience.new', 'New audience')}
            </button>
          )}
        </div>
      </div>

      {showForm && (
        <div className="rounded-xl border bg-white shadow-sm overflow-hidden">
          {/* ── Header row ──────────────────────────────────── */}
          <div className="px-5 pt-5 pb-4 border-b bg-gray-50/60">
            {editingId ? (
              <p className="text-sm font-semibold text-gray-700">
                {t('marketing.audience.editTitle', 'Edit audience')}
              </p>
            ) : (
              <>
                <p className="text-xs font-semibold text-gray-400 uppercase tracking-widest mb-3">
                  {t('marketing.audience.quickStart', 'Quick start')}
                </p>
                <div className="flex flex-wrap gap-2 items-center">
                  {TEMPLATES.map((tpl) => {
                    const Icon = tpl.icon;
                    return (
                      <button
                        key={tpl.label}
                        type="button"
                        onClick={() => applyTemplate(tpl)}
                        className="inline-flex items-center gap-1.5 text-xs px-3 py-2 rounded-lg border border-gray-200 bg-white font-medium text-gray-700 hover:border-primary/50 hover:text-primary hover:bg-primary/5 transition-all duration-150 shadow-xs"
                      >
                        <Icon size={12} className="text-primary/70" />
                        {t(tpl.labelKey, tpl.label)}
                      </button>
                    );
                  })}
                  <button
                    type="button"
                    onClick={resetForm}
                    className="text-xs text-gray-400 hover:text-gray-600 underline ml-1"
                  >
                    {t('marketing.audience.resetBlank', 'Reset to blank')}
                  </button>
                </div>
              </>
            )}
          </div>

          <div className="p-5 space-y-5">
            {/* ── Name / description ─────────────────────── */}
            <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
              <div>
                <label htmlFor="audience-name" className="text-sm font-medium block mb-1">
                  {t('common.name', 'Name')}
                </label>
                <input
                  id="audience-name"
                  className={`input-field w-full ${nameError ? 'border-red-500 focus:border-red-500 focus:ring-red-500' : ''}`}
                  value={name}
                  onChange={(e) => {
                    setName(e.target.value);
                    if (nameError) setNameError(null);
                  }}
                  placeholder={t('marketing.audience.namePlaceholder', 'e.g. VIP regulars')}
                  aria-invalid={nameError ? true : undefined}
                  aria-describedby={nameError ? 'audience-name-error' : undefined}
                />
                {nameError && (
                  <p id="audience-name-error" className="text-xs text-red-600 mt-1">
                    {nameError}
                  </p>
                )}
              </div>
              <div>
                <label className="text-sm font-medium block mb-1">
                  {t('marketing.audience.descriptionOptional', 'Description (optional)')}
                </label>
                <input
                  className="input-field w-full"
                  value={description}
                  onChange={(e) => setDescription(e.target.value)}
                />
              </div>
            </div>

            {/* ── Rule editor ───────────────────────────── */}
            <RuleEditor
              rules={rules}
              onChange={setRules}
              branches={branches}
              t={t}
              currencySymbol={currencySymbol}
            />

            {/* ── Footer ────────────────────────────────── */}
            <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 border-t pt-4">
              <PreviewCard
                preview={preview}
                previewing={previewing}
                hasBeenPreviewed={hasBeenPreviewed}
                t={t}
              />
              <div className="flex gap-2 self-end sm:self-auto">
                <button className="btn-ghost" onClick={closeForm}>
                  {t('common.cancel', 'Cancel')}
                </button>
                <button className="btn-primary" onClick={onSave} disabled={saving}>
                  {saving ? (
                    <Loader2 size={16} className="animate-spin" />
                  ) : editingId ? (
                    t('marketing.audience.update', 'Update audience')
                  ) : (
                    t('marketing.audience.save', 'Save audience')
                  )}
                </button>
              </div>
            </div>
          </div>
        </div>
      )}

      {/* ── Segments list ───────────────────────────────────── */}
      <div className="rounded-xl border bg-white">
        {loading ? (
          <div className="p-10 text-center">
            <Loader2 size={20} className="inline animate-spin" />
          </div>
        ) : segments.length === 0 ? (
          <div className="p-10 text-center text-gray-500">
            <Megaphone size={28} className="mx-auto mb-3 opacity-60" />
            {t(
              'marketing.audience.empty',
              'No audiences yet. Create one to start sending campaigns.'
            )}
          </div>
        ) : (
          <div className="divide-y">
            {segments.map((s) => (
              <div key={s.id} className="p-4 flex items-center justify-between">
                <div>
                  <div className="font-semibold">{s.name}</div>
                  {s.description && (
                    <div className="text-sm text-gray-500 mt-0.5">{s.description}</div>
                  )}
                  <div className="text-xs text-gray-400 mt-1 flex flex-wrap gap-2">
                    <span>
                      {t('marketing.audience.channel', 'Channel')}: {s.rules.channel ?? 'any'}
                    </span>
                    {s.rules.orderCountMin && (
                      <span>
                        ≥{s.rules.orderCountMin} {t('marketing.audience.visits', 'visits')}
                      </span>
                    )}
                    {s.rules.lifetimeSpendMin && (
                      <span>
                        {t('marketing.audience.spend', 'spend')} ≥{s.rules.lifetimeSpendMin}
                      </span>
                    )}
                    {s.rules.orderRecencyDays && (
                      <span>
                        {t('marketing.audience.lastDays', 'last')} {s.rules.orderRecencyDays}d
                      </span>
                    )}
                    {s.rules.branchIds && s.rules.branchIds.length > 0 && (
                      <span>
                        {s.rules.branchIds.length}{' '}
                        {t('marketing.audience.branchesShort', 'branches')}
                      </span>
                    )}
                    {s.rules.tags && s.rules.tags.length > 0 && (
                      <span>
                        {t('marketing.audience.tagsShort', 'tags')}: {s.rules.tags.join(', ')}
                      </span>
                    )}
                  </div>
                </div>
                <div className="flex items-center gap-1 shrink-0">
                  <button
                    onClick={() => openEditForm(s)}
                    className="text-gray-500 hover:text-primary hover:bg-primary/10 p-2 rounded"
                    title={t('common.edit', 'Edit')}
                  >
                    <Pencil size={15} />
                  </button>
                  <button
                    onClick={() => void onDelete(s.id)}
                    className="text-red-600 hover:bg-red-50 p-2 rounded"
                    title={t('common.delete', 'Delete')}
                  >
                    <Trash2 size={16} />
                  </button>
                </div>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

export default function AudiencesPage() {
  return (
    <FeatureGuard feature="marketing" featureName="Marketing">
      <AudiencesPageInner />
    </FeatureGuard>
  );
}
