/**
 * "Never suggest" button — used on Recipes cards + RecipeDetail page.
 *
 * Two reasons (matching the NeverSuggestReason enum on the server):
 *   - "allergy" → red, requires window.confirm (irreversible to the user)
 *   - "dislike" → neutral, no confirm (undo toast is the escape hatch)
 *
 * On success:
 *   - Optimistically removes the recipe from the list (via queryKey
 *     invalidation in the parent).
 *   - Pops a Sprint-3-style undo toast. Clicking Undo calls
 *     DELETE /api/never-suggest/{id} and re-invalidates the queries.
 *
 * The button is `position: absolute` on the card overlay. On the
 * detail page, the parent passes `variant="detail"` to render it as
 * a row of two text buttons in the top bar.
 */
import { useEffect, useRef, useState } from 'react'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { Ban, X, AlertTriangle, ThumbsDown } from 'lucide-react'
import { mealPlannerApi } from '../api'
import { showToast, showApiError } from '../lib/toast'

interface NeverSuggestButtonProps {
  recipeId: string
  recipeName: string
  /** "card" overlays the recipe image; "detail" renders inline as a button group. */
  variant: 'card' | 'detail'
}

interface NeverSuggestRow {
  id: string
  recipe_id: string | null
  reason: string | null
}

export function NeverSuggestButton({ recipeId, recipeName, variant }: NeverSuggestButtonProps) {
  const qc = useQueryClient()
  const [open, setOpen] = useState(false)
  const [familyId, setFamilyId] = useState<string | null>(null)
  const popoverRef = useRef<HTMLDivElement | null>(null)
  const buttonRef = useRef<HTMLButtonElement | null>(null)

  // Pull family id from the profile query (same pattern as Recommended.tsx).
  // Doing it inline (rather than via a context) keeps the dependency
  // surface flat and matches the rest of the codebase.
  const { data: profile } = useQuery({
    queryKey: ['profile'],
    queryFn: () => mealPlannerApi.profile.get().then(r => r.data),
  })
  useEffect(() => {
    if (profile?.id && familyId !== profile.id) setFamilyId(profile.id)
  }, [profile, familyId])

  // Find the existing NeverSuggest row for this recipe (if any) so the
  // button can show a "Remove block" state. Without this, re-clicking
  // the button on a recipe that's already blocked would 201 (idempotent
  // on the server) but the toast would say "added" which is confusing.
  const { data: existing } = useQuery<NeverSuggestRow[]>({
    queryKey: ['neverSuggest', familyId],
    queryFn: () =>
      familyId
        ? mealPlannerApi.neverSuggest.list(familyId).then(r => r.data)
        : Promise.resolve([]),
    enabled: !!familyId,
  })
  const existingRow = existing?.find(r => r.recipe_id === recipeId)

  const invalidateAll = () => {
    qc.invalidateQueries({ queryKey: ['neverSuggest', familyId] })
    qc.invalidateQueries({ queryKey: ['recipes'] })
    qc.invalidateQueries({ queryKey: ['recommendedRecipes', familyId] })
    qc.invalidateQueries({ queryKey: ['mealPlan'] })
  }

  const addMutation = useMutation({
    mutationFn: (reason: 'allergy' | 'dislike') => {
      if (!familyId) throw new Error('family not loaded')
      return mealPlannerApi.neverSuggest
        .add({ family_profile_id: familyId, recipe_id: recipeId, reason })
        .then(r => r.data)
    },
    onSuccess: (data, reason) => {
      invalidateAll()
      setOpen(false)
      const reasonLabel = reason === 'allergy' ? 'allergy' : "won't suggest"
      showToast.undo(
        `Marked ${recipeName} as ${reasonLabel} for your family`,
        () => {
          // Best-effort undo. The toast dismisses itself on success.
          if (data?.id) {
            mealPlannerApi.neverSuggest
              .remove(data.id)
              .then(() => invalidateAll())
              .catch(() => {
                // The toast is already gone; show a fresh error toast.
                showToast.error('Could not undo — open NeverSuggest API to remove manually.')
              })
          }
        },
        6000,
      )
    },
    onError: (err: unknown) => {
      // Pull the FastAPI detail string for the toast (Sprint 4 F7 helper).
      showApiError(err, 'Could not mark recipe')
      setOpen(false)
    },
  })

  const removeMutation = useMutation({
    mutationFn: (nsId: string) => mealPlannerApi.neverSuggest.remove(nsId),
    onSuccess: () => {
      invalidateAll()
      showToast.success(`Removed block on ${recipeName}`)
    },
  })

  // Close popover on outside click + Escape.
  useEffect(() => {
    if (!open) return
    const onDown = (e: MouseEvent) => {
      const t = e.target as Node
      if (popoverRef.current?.contains(t)) return
      if (buttonRef.current?.contains(t)) return
      setOpen(false)
    }
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') setOpen(false)
    }
    document.addEventListener('mousedown', onDown)
    document.addEventListener('keydown', onKey)
    return () => {
      document.removeEventListener('mousedown', onDown)
      document.removeEventListener('keydown', onKey)
    }
  }, [open])

  const onPickReason = (reason: 'allergy' | 'dislike') => {
    if (reason === 'allergy') {
      const ok = window.confirm(
        `Mark "${recipeName}" as an allergy for your family?\n\n` +
        `This permanently blocks the recipe. The planner will avoid it in all future plans. ` +
        `You can undo this from the toast that appears, but it will not appear in any subsequent meal plan.`,
      )
      if (!ok) return
    }
    addMutation.mutate(reason)
  }

  const onRemoveExisting = () => {
    if (!existingRow) return
    if (!window.confirm(`Stop blocking "${recipeName}"? The planner may suggest it again.`)) return
    removeMutation.mutate(existingRow.id)
  }

  // ----- Render -----

  if (existingRow) {
    // Recipe is already blocked — show an "unblock" affordance.
    if (variant === 'card') {
      return (
        <button
          ref={buttonRef}
          type="button"
          onClick={(e) => { e.preventDefault(); e.stopPropagation(); onRemoveExisting() }}
          aria-label={`${recipeName} is blocked (${existingRow.reason || 'no reason'}). Click to unblock.`}
          className="absolute top-2 right-2 z-10 inline-flex items-center justify-center w-8 h-8 rounded-full bg-danger-100 text-danger-700 hover:bg-danger-200 focus:outline-none focus:ring-2 focus:ring-danger-400 shadow-sm"
          title="Recipe is blocked — click to unblock"
        >
          <Ban className="w-4 h-4" aria-hidden="true" />
        </button>
      )
    }
    return (
      <button
        ref={buttonRef}
        type="button"
        onClick={onRemoveExisting}
        className="text-xs font-medium px-2.5 py-1.5 rounded-lg border border-danger-300 text-danger-700 hover:bg-danger-50 focus:outline-none focus:ring-2 focus:ring-danger-400 inline-flex items-center gap-1.5"
        aria-label={`${recipeName} is blocked (${existingRow.reason || 'no reason'}). Click to unblock.`}
      >
        <Ban className="w-3.5 h-3.5" aria-hidden="true" />
        Unblock
      </button>
    )
  }

  if (variant === 'card') {
    return (
      <div className="absolute top-2 right-2 z-10">
        <button
          ref={buttonRef}
          type="button"
          onClick={(e) => { e.preventDefault(); e.stopPropagation(); setOpen(o => !o) }}
          aria-label={`Never suggest ${recipeName} again`}
          aria-expanded={open}
          aria-haspopup="menu"
          className="inline-flex items-center justify-center w-8 h-8 rounded-full bg-white/90 backdrop-blur-sm text-surface-700 hover:bg-white hover:text-danger-600 focus:outline-none focus:ring-2 focus:ring-danger-400 shadow-sm opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity"
          title="Never suggest this recipe"
        >
          <Ban className="w-4 h-4" aria-hidden="true" />
        </button>
        {open && (
          <div
            ref={popoverRef}
            role="menu"
            aria-label="Choose a reason"
            className="absolute top-10 right-0 bg-white rounded-xl shadow-lg border border-surface-200 p-2 w-52 animate-fade-in"
          >
            <div className="px-2 py-1.5 text-xs text-surface-500 font-medium">Never suggest this recipe</div>
            <button
              type="button"
              role="menuitem"
              onClick={() => onPickReason('allergy')}
              disabled={addMutation.isPending}
              className="w-full text-left flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-danger-50 text-danger-700 focus:outline-none focus:bg-danger-50 disabled:opacity-50"
            >
              <AlertTriangle className="w-4 h-4 flex-shrink-0" aria-hidden="true" />
              <span>Allergy</span>
            </button>
            <button
              type="button"
              role="menuitem"
              onClick={() => onPickReason('dislike')}
              disabled={addMutation.isPending}
              className="w-full text-left flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-surface-100 text-surface-700 focus:outline-none focus:bg-surface-100 disabled:opacity-50"
            >
              <ThumbsDown className="w-4 h-4 flex-shrink-0" aria-hidden="true" />
              <span>Dislike</span>
            </button>
            <div className="border-t border-surface-100 mt-1 pt-1 px-2">
              <button
                type="button"
                onClick={() => setOpen(false)}
                className="text-[11px] text-surface-500 hover:text-surface-700 focus:outline-none focus:underline"
              >
                Cancel
              </button>
            </div>
          </div>
        )}
      </div>
    )
  }

  // detail variant — text buttons in a row
  return (
    <div className="relative inline-flex items-center gap-1.5">
      <button
        ref={buttonRef}
        type="button"
        onClick={() => setOpen(o => !o)}
        aria-expanded={open}
        aria-haspopup="menu"
        aria-label={`Never suggest ${recipeName} again`}
        className="inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium rounded-lg border border-surface-300 text-surface-700 hover:bg-surface-50 focus:outline-none focus:ring-2 focus:ring-primary-400"
      >
        <Ban className="w-3.5 h-3.5" aria-hidden="true" />
        Deny forever
      </button>
      {open && (
        <div
          ref={popoverRef}
          role="menu"
          aria-label="Choose a reason"
          className="absolute top-10 right-0 bg-white rounded-xl shadow-lg border border-surface-200 p-2 w-52 z-20 animate-fade-in"
        >
          <div className="px-2 py-1.5 text-xs text-surface-500 font-medium">Mark as</div>
          <button
            type="button"
            role="menuitem"
            onClick={() => onPickReason('allergy')}
            disabled={addMutation.isPending}
            className="w-full text-left flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-danger-50 text-danger-700 focus:outline-none focus:bg-danger-50 disabled:opacity-50"
          >
            <AlertTriangle className="w-4 h-4 flex-shrink-0" aria-hidden="true" />
            <span>Allergy</span>
          </button>
          <button
            type="button"
            role="menuitem"
            onClick={() => onPickReason('dislike')}
            disabled={addMutation.isPending}
            className="w-full text-left flex items-center gap-2 px-2 py-1.5 text-sm rounded hover:bg-surface-100 text-surface-700 focus:outline-none focus:bg-surface-100 disabled:opacity-50"
          >
            <ThumbsDown className="w-4 h-4 flex-shrink-0" aria-hidden="true" />
            <span>Dislike</span>
          </button>
          <div className="border-t border-surface-100 mt-1 pt-1 px-2 flex justify-end">
            <button
              type="button"
              onClick={() => setOpen(false)}
              className="text-[11px] text-surface-500 hover:text-surface-700 focus:outline-none focus:underline inline-flex items-center gap-1"
            >
              <X className="w-3 h-3" />
              Close
            </button>
          </div>
        </div>
      )}
    </div>
  )
}
