import { ImagePlus, Loader2, MessageSquarePlus, X } from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
import { Button } from '@/components/ui/button';
import { Dialog } from '@/components/ui/dialog';
import { Textarea } from '@/components/ui/input';
import { Field } from '@/components/ui/label';
import { HttpError, httpForm } from '@/lib/http';
import { cn } from '@/lib/utils';

/** Top-bar "Send feedback": a bug report or suggestion from any page (Claude structures it for the admin inbox). */
export function FeedbackButton() {
    const [open, setOpen] = useState(false);
    const [type, setType] = useState<'bug' | 'suggestion'>('bug');
    const [message, setMessage] = useState('');
    const [busy, setBusy] = useState(false);
    const [err, setErr] = useState<string | null>(null);
    const [done, setDone] = useState<string | null>(null);
    const [images, setImages] = useState<File[]>([]);
    const [previews, setPreviews] = useState<string[]>([]);
    const input = useRef<HTMLInputElement>(null);
    const MAX = 5;
    const MAX_MB = 10;
    useEffect(() => {
        const urls = images.map((f) => URL.createObjectURL(f));
        setPreviews(urls);
        return () => urls.forEach((u) => URL.revokeObjectURL(u));
    }, [images]);
    const addFiles = (files: File[]) => {
        setErr(null);
        const ok = files.filter((f) => /^image\/(png|jpe?g|gif|webp)$/.test(f.type));
        if (ok.length < files.length) setErr('Only JPG, PNG, GIF or WebP images can be attached.');
        const big = ok.find((f) => f.size > MAX_MB * 1024 * 1024);
        if (big) { setErr(`${big.name} is over ${MAX_MB} MB.`); return; }
        setImages((cur) => [...cur, ...ok].slice(0, MAX));
        if (images.length + ok.length > MAX) setErr(`Up to ${MAX} images.`);
    };
    // Paste a screenshot straight into the box.
    const onPaste = (e: React.ClipboardEvent) => {
        const files = Array.from(e.clipboardData.files ?? []).filter((f) => f.type.startsWith('image/'));
        if (files.length) { e.preventDefault(); addFiles(files.map((f, i) => new File([f], f.name && f.name !== 'image.png' ? f.name : `screenshot-${Date.now()}-${i}.png`, { type: f.type }))); }
    };
    const send = async () => {
        setBusy(true); setErr(null);
        try {
            const r = await httpForm<{ message: string }>('/feedback', { type, message, page_url: window.location.pathname + window.location.search, images });
            setDone(r.message); setMessage(''); setImages([]);
        } catch (e) {
            const errs = e instanceof HttpError ? (e.body?.errors ?? {}) as Record<string, string[]> : {};
            setErr(Object.values(errs)[0]?.[0] ?? (e instanceof HttpError ? e.message : 'Couldn’t send it. Please try again.'));
        }
        finally { setBusy(false); }
    };
    return (
        <>
            <button type="button" onClick={() => { setOpen(true); setDone(null); setErr(null); }} data-testid="feedback-open"
                className="hidden items-center gap-1.5 rounded-[5px] px-2.5 py-2 text-sm text-ink hover:bg-accent-soft hover:text-link md:inline-flex" aria-label="Send feedback">
                <MessageSquarePlus className="h-5 w-5" strokeWidth={1.75} /> <span className="hidden lg:inline">Feedback</span>
            </button>
            <Dialog open={open} onOpenChange={setOpen} title="Send feedback" description="Report a bug or suggest an improvement. It goes to the admins with the page you’re on.">
                {done ? (
                    <div className="space-y-4">
                        <p role="status" className="rounded-md bg-teal/10 px-3 py-2 text-sm text-[#05705f]" data-testid="feedback-sent">{done}</p>
                        <div className="flex justify-end"><Button type="button" onClick={() => setOpen(false)}>Close</Button></div>
                    </div>
                ) : (
                    <div className="space-y-4">
                        <div className="inline-flex rounded-md border border-line p-0.5 text-sm" role="radiogroup" aria-label="Type">
                            {(['bug', 'suggestion'] as const).map((t) => (
                                <button key={t} type="button" role="radio" aria-checked={type === t} onClick={() => setType(t)}
                                    className={cn('rounded px-3 py-1', type === t ? 'bg-ink text-white' : 'text-ink-muted hover:bg-stone-100')}>{t === 'bug' ? 'Something’s wrong' : 'Suggestion'}</button>
                            ))}
                        </div>
                        <Field label={type === 'bug' ? 'What happened?' : 'What would you like?'} htmlFor="feedback-message" error={err ?? undefined}
                            hint={type === 'bug' ? 'What you did, what you expected, and what happened instead.' : undefined}>
                            <Textarea id="feedback-message" rows={6} value={message} onChange={(e) => setMessage(e.target.value)} onPaste={onPaste} data-testid="feedback-message" />
                        </Field>
                        <div>
                            <input ref={input} type="file" accept="image/png,image/jpeg,image/gif,image/webp" multiple className="sr-only" aria-label="Attach images" data-testid="feedback-images"
                                onChange={(e) => { addFiles(Array.from(e.target.files ?? [])); e.target.value = ''; }} />
                            <div className="flex flex-wrap items-center gap-2">
                                <Button type="button" size="sm" variant="secondary" onClick={() => input.current?.click()} disabled={images.length >= MAX}><ImagePlus className="h-4 w-4" /> Add images</Button>
                                <span className="text-xs text-ink-muted">Screenshots help. Up to {MAX} images, {MAX_MB} MB each. You can also paste a screenshot into the box.</span>
                            </div>
                            {images.length > 0 && (
                                <ul className="mt-2 flex flex-wrap gap-2" data-testid="feedback-previews">
                                    {images.map((f, i) => (
                                        <li key={`${f.name}-${i}`} className="relative h-20 w-28 overflow-hidden rounded-md border border-line bg-stone-50" title={f.name}>
                                            {previews[i] && <img src={previews[i]} alt={f.name} className="h-full w-full object-cover" />}
                                            <button type="button" onClick={() => setImages((cur) => cur.filter((_, j) => j !== i))} aria-label={`Remove ${f.name}`}
                                                className="absolute right-1 top-1 rounded-full bg-white/90 p-0.5 text-ink shadow hover:bg-white"><X className="h-3.5 w-3.5" /></button>
                                        </li>
                                    ))}
                                </ul>
                            )}
                        </div>
                        <div className="flex justify-end gap-2">
                            <Button type="button" variant="secondary" onClick={() => setOpen(false)}>Cancel</Button>
                            <Button type="button" onClick={() => void send()} disabled={busy || message.trim().length < 5} data-testid="feedback-send">{busy && <Loader2 className="h-4 w-4 animate-spin" />} Send</Button>
                        </div>
                    </div>
                )}
            </Dialog>
        </>
    );
}
