import { useForm } from '@inertiajs/react';
import { FormEvent, useEffect, useState } from 'react';
import { toast } from 'sonner';

import RelatedGamesPicker, { type PickerGame } from '@/components/dashboard/RelatedGamesPicker';
import TipTapEditor from '@/components/dashboard/TipTapEditor';
import { Button } from '@/components/ui/button';
import { FileInput } from '@/components/ui/file-input';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
    Select,
    SelectContent,
    SelectItem,
    SelectTrigger,
    SelectValue,
} from '@/components/ui/select';
import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';
import { useTranslation } from '@/contexts/LanguageContext';

export interface ArticleCategoryOption {
    id: number;
    name: string;
    slug: string;
}

/**
 * Raw related-game shape as it arrives from the backend. `thumbnail` is a JSON
 * array cast (`{thumbnail,standard,large}`) on most rows but is sometimes a
 * plain string, so it has to be normalized into the picker's `string | null`.
 */
interface RawRelatedGame {
    id: number;
    title: string;
    slug: string;
    thumbnail?: string | { thumbnail?: string | null; standard?: string | null; large?: string | null } | null;
}

function resolveThumbnail(thumbnail: RawRelatedGame['thumbnail']): string | null {
    if (typeof thumbnail === 'string') return thumbnail;
    return thumbnail?.thumbnail ?? thumbnail?.standard ?? thumbnail?.large ?? null;
}

function normalizeRelatedGames(games?: RawRelatedGame[] | null): PickerGame[] {
    return (games ?? []).map((g) => ({
        id: g.id,
        title: g.title,
        slug: g.slug,
        thumbnail: resolveThumbnail(g.thumbnail),
    }));
}

export interface ArticleFormData {
    id?: number;
    title?: string | null;
    slug?: string | null;
    excerpt?: string | null;
    content?: string | null;
    article_category_id?: number | null;
    tags?: string[] | null;
    is_featured?: boolean | null;
    status?: string | null;
    published_at?: string | null;
    featured_image?: Record<string, string> | null;
    games?: RawRelatedGame[] | null;
}

interface Props {
    mode: 'create' | 'update';
    initialData?: ArticleFormData;
    categories: ArticleCategoryOption[];
    isAdmin: boolean;
    onSuccess?: () => void;
}

const NO_CATEGORY = '__none__';

function slugify(value: string): string {
    return value
        .toLowerCase()
        .trim()
        .replace(/[^\w\s-]/g, '')
        .replace(/[\s_-]+/g, '-')
        .replace(/^-+|-+$/g, '');
}

/** Datetime-local needs `YYYY-MM-DDTHH:mm`; tomorrow at the current time. */
function defaultScheduleValue(): string {
    const d = new Date(Date.now() + 24 * 60 * 60 * 1000);
    const pad = (n: number) => String(n).padStart(2, '0');
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}

export default function ArticleForm({ mode, initialData, categories, isAdmin, onSuccess }: Props) {
    const { t } = useTranslation();
    const initialGames = normalizeRelatedGames(initialData?.games);

    const { data, setData, post, transform, processing, errors, reset } = useForm<{
        _method: string;
        title: string;
        slug: string;
        excerpt: string;
        content: string;
        article_category_id: string;
        tags: string;
        is_featured: boolean;
        status: string;
        published_at: string;
        featured_image: File | null;
        related_game_ids: number[];
    }>({
        _method: mode === 'update' ? 'put' : 'post',
        title: initialData?.title ?? '',
        slug: initialData?.slug ?? '',
        excerpt: initialData?.excerpt ?? '',
        content: initialData?.content ?? '',
        article_category_id: initialData?.article_category_id ? String(initialData.article_category_id) : '',
        tags: (initialData?.tags ?? []).join(', '),
        is_featured: initialData?.is_featured ?? false,
        status: initialData?.status ?? 'draft',
        published_at: initialData?.published_at
            ? initialData.published_at.slice(0, 16)
            : defaultScheduleValue(),
        featured_image: null,
        related_game_ids: initialGames.map((g) => g.id),
    });

    const [slugDirty, setSlugDirty] = useState<boolean>(Boolean(initialData?.slug));
    const [relatedGames, setRelatedGames] = useState<PickerGame[]>(initialGames);
    const [showSchedule, setShowSchedule] = useState(false);

    useEffect(() => {
        if (!slugDirty && data.title) {
            setData('slug', slugify(data.title));
        }
    }, [data.title, slugDirty, setData]);

    useEffect(() => {
        setData('related_game_ids', relatedGames.map((g) => g.id));
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [relatedGames]);

    const existingImage = initialData?.featured_image?.standard ?? initialData?.featured_image?.thumbnail;

    function submitWithStatus(status: string, scheduledAt?: string) {
        const url = mode === 'create' ? '/dashboard/articles' : `/dashboard/articles/${initialData?.id}`;
        transform((current) => ({
            ...current,
            status,
            published_at: status === 'scheduled' ? (scheduledAt ?? current.published_at) : '',
            // Tags ship as a comma list; the backend normalizes into an array.
            tags: current.tags,
            // Drop the category sentinel before posting.
            article_category_id: current.article_category_id,
        }));
        post(url, {
            forceFormData: true,
            preserveScroll: true,
            onSuccess: () => {
                reset();
                setSlugDirty(false);
                setRelatedGames([]);
                onSuccess?.();
            },
            onError: (errs) => {
                const first = Object.values(errs)[0];
                if (first) toast.error(first as string);
            },
        });
    }

    function onSubmit(e: FormEvent) {
        e.preventDefault();
        // Default submit = save draft (authors) / keep current status intent.
        submitWithStatus('draft');
    }

    return (
        <form onSubmit={onSubmit} className="space-y-5">
            {Object.keys(errors).length > 0 && (
                <div className="rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive">
                    <p className="font-medium">{t('Please fix the highlighted fields and try again.')}</p>
                    <ul className="mt-1 list-disc ps-5 text-xs">
                        {Object.entries(errors).map(([field, message]) => (
                            <li key={field}>
                                <span className="font-medium">{field}:</span> {message as string}
                            </li>
                        ))}
                    </ul>
                </div>
            )}

            <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
                <div className="space-y-1.5">
                    <Label htmlFor="art-title">{t('Title')}</Label>
                    <Input
                        id="art-title"
                        value={data.title}
                        onChange={(e) => setData('title', e.target.value)}
                        placeholder={t('e.g. Best Retro Platformers of 2026')}
                        aria-invalid={!!errors.title}
                    />
                    {errors.title && <p className="text-xs text-destructive">{errors.title}</p>}
                </div>
                <div className="space-y-1.5">
                    <Label htmlFor="art-slug">{t('Slug')}</Label>
                    <Input
                        id="art-slug"
                        value={data.slug}
                        onChange={(e) => {
                            setSlugDirty(true);
                            setData('slug', e.target.value);
                        }}
                        placeholder={t('Auto-generated from the title')}
                        aria-invalid={!!errors.slug}
                    />
                    {errors.slug ? (
                        <p className="text-xs text-destructive">{errors.slug}</p>
                    ) : (
                        <p className="text-xs text-muted-foreground">
                            {t('Auto-generated from the title until edited.')}
                        </p>
                    )}
                </div>
            </div>

            <div className="space-y-1.5">
                <Label htmlFor="art-excerpt">{t('Excerpt')}</Label>
                <Textarea
                    id="art-excerpt"
                    value={data.excerpt}
                    onChange={(e) => setData('excerpt', e.target.value)}
                    rows={2}
                    placeholder={t('Short summary shown on cards and in search results.')}
                />
                {errors.excerpt && <p className="text-xs text-destructive">{errors.excerpt}</p>}
            </div>

            <div className="space-y-1.5">
                <Label>{t('Content')}</Label>
                <TipTapEditor
                    value={data.content}
                    onChange={(html) => setData('content', html)}
                    variant="full"
                    gameEmbed
                    imageUpload
                    placeholder={t('Write your article…')}
                />
                {errors.content && <p className="text-xs text-destructive">{errors.content}</p>}
            </div>

            <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
                <div className="space-y-1.5">
                    <Label htmlFor="art-category">{t('Category')}</Label>
                    <Select
                        value={data.article_category_id || NO_CATEGORY}
                        onValueChange={(v) => setData('article_category_id', v === NO_CATEGORY ? '' : v)}
                    >
                        <SelectTrigger id="art-category">
                            <SelectValue placeholder={t('Uncategorized')} />
                        </SelectTrigger>
                        <SelectContent>
                            <SelectItem value={NO_CATEGORY}>{t('Uncategorized')}</SelectItem>
                            {categories.map((c) => (
                                <SelectItem key={c.id} value={String(c.id)}>
                                    {c.name}
                                </SelectItem>
                            ))}
                        </SelectContent>
                    </Select>
                    {errors.article_category_id && (
                        <p className="text-xs text-destructive">{errors.article_category_id}</p>
                    )}
                </div>
                <div className="space-y-1.5">
                    <Label htmlFor="art-tags">{t('Tags')}</Label>
                    <Input
                        id="art-tags"
                        value={data.tags}
                        onChange={(e) => setData('tags', e.target.value)}
                        placeholder={t('Comma-separated, e.g. retro, tips')}
                    />
                    {errors.tags && <p className="text-xs text-destructive">{errors.tags}</p>}
                </div>
            </div>

            <div className="space-y-1.5">
                <Label htmlFor="art-image">{t('Featured image')}</Label>
                {existingImage && !data.featured_image && (
                    <img
                        src={existingImage}
                        alt={t('Current image')}
                        className="h-24 rounded border bg-background object-cover"
                    />
                )}
                <FileInput
                    id="art-image"
                    accept="image/png,image/jpeg,image/webp"
                    value={data.featured_image}
                    onChange={(f) => setData('featured_image', f)}
                    emptyHint={existingImage ? t('Keep current image') : t('No image selected')}
                />
                {errors.featured_image && <p className="text-xs text-destructive">{errors.featured_image}</p>}
            </div>

            <div className="space-y-1.5">
                <Label>{t('Related games')}</Label>
                <p className="text-xs text-muted-foreground">
                    {t('Linked games appear below the article. Inline embeds are added automatically too.')}
                </p>
                <RelatedGamesPicker value={relatedGames} onChange={setRelatedGames} />
            </div>

            <div className="flex items-start gap-3 rounded-md border bg-muted/30 p-4">
                <Switch
                    id="art-featured"
                    checked={data.is_featured}
                    onCheckedChange={(c) => setData('is_featured', c)}
                />
                <div className="space-y-0.5">
                    <Label htmlFor="art-featured" className="cursor-pointer">{t('Featured')}</Label>
                    <p className="text-xs text-muted-foreground">
                        {t('Featured articles are highlighted at the top of the blog.')}
                    </p>
                </div>
            </div>

            {isAdmin && showSchedule && (
                <div className="space-y-1.5 rounded-md border bg-muted/30 p-4">
                    <Label htmlFor="art-schedule">{t('Publish date & time')}</Label>
                    <Input
                        id="art-schedule"
                        type="datetime-local"
                        value={data.published_at}
                        onChange={(e) => setData('published_at', e.target.value)}
                    />
                    <div className="flex justify-end gap-2 pt-1">
                        <Button
                            type="button"
                            variant="ghost"
                            size="sm"
                            onClick={() => setShowSchedule(false)}
                        >
                            {t('Cancel')}
                        </Button>
                        <Button
                            type="button"
                            size="sm"
                            disabled={processing}
                            onClick={() => submitWithStatus('scheduled', data.published_at)}
                        >
                            {t('Schedule')}
                        </Button>
                    </div>
                </div>
            )}

            <div className="flex flex-wrap justify-end gap-2 pt-2">
                <Button
                    type="button"
                    variant="outline"
                    disabled={processing}
                    onClick={() => submitWithStatus('draft')}
                >
                    {t('Save draft')}
                </Button>
                {isAdmin ? (
                    <>
                        <Button
                            type="button"
                            variant="secondary"
                            disabled={processing}
                            onClick={() => setShowSchedule((s) => !s)}
                        >
                            {t('Schedule…')}
                        </Button>
                        <Button
                            type="button"
                            disabled={processing}
                            onClick={() => submitWithStatus('published')}
                        >
                            {t('Publish now')}
                        </Button>
                    </>
                ) : (
                    <Button
                        type="button"
                        disabled={processing}
                        onClick={() => submitWithStatus('pending')}
                    >
                        {t('Submit for review')}
                    </Button>
                )}
            </div>
        </form>
    );
}
