import { useForm, usePage } from '@inertiajs/react';
import { FormEvent, ReactNode, useEffect, useState } from 'react';

import { useTranslation } from '@/contexts/LanguageContext';
import ArcadeCoreSelect from '@/components/dashboard/ArcadeCoreSelect';
import RomSystemSelect from '@/components/dashboard/RomSystemSelect';
import TagChipsInput from '@/components/dashboard/TagChipsInput';
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 { Separator } from '@/components/ui/separator';
import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';

type GameType = 'html5' | 'flash' | 'rom' | 'tic80' | 'embed';

interface CategoryOption {
    id: number;
    name: string;
}

interface GameData {
    id?: number;
    title?: string | null;
    slug?: string | null;
    description?: string | null;
    short_description?: string | null;
    category_id?: number | null;
    game_type?: GameType | null;
    rom_system?: string | null;
    arcade_core?: string | null;
    embed_url?: string | null;
    width?: number | null;
    height?: number | null;
    controls?: string | null;
    tags?: string | null;
    sort_order?: number | null;
    is_featured?: boolean | null;
    is_active?: boolean | null;
    is_mobile_compatible?: boolean | null;
    api_enabled?: boolean | null;
    is_vip_only?: boolean | null;
    is_arcade_tv_compatible?: boolean | null;
    thumbnail?: Record<string, string> | null;
    preview_video?: string | null;
    game_file?: string | null;
}

interface Props {
    mode: 'create' | 'update';
    initialData?: GameData;
    categories: CategoryOption[];
    onSuccess?: () => void;
}

const ACCEPT_BY_TYPE: Record<GameType, string> = {
    html5: '.html,.zip',
    flash: '.swf,.zip',
    tic80: '.tic',
    rom: '.zip,.nes,.snes,.sfc,.smc,.gba,.gb,.gbc,.md,.gen,.smd,.n64,.z64,.v64,.bin,.cue,.iso,.pce,.nds,.vb,.a52,.lnx,.j64,.3do,.d64,.adf,.col,.ngp,.ws,.wsc,.pbp,.cso,.32x,.gg,.sms',
    embed: '',
};

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

function Section({ title, children }: { title: string; children: ReactNode }) {
    return (
        <div className="space-y-3">
            <h3 className="text-sm font-semibold text-foreground">{title}</h3>
            <div className="space-y-3">{children}</div>
        </div>
    );
}

function ToggleRow({
    id,
    label,
    description,
    checked,
    onChange,
}: {
    id: string;
    label: string;
    description: string;
    checked: boolean;
    onChange: (v: boolean) => void;
}) {
    return (
        <div className="flex items-start gap-3">
            <Switch id={id} checked={checked} onCheckedChange={onChange} />
            <div className="space-y-0.5">
                <Label htmlFor={id} className="cursor-pointer">
                    {label}
                </Label>
                <p className="text-xs text-muted-foreground">{description}</p>
            </div>
        </div>
    );
}

export default function GameForm({ mode, initialData, categories, onSuccess }: Props) {
    const { t } = useTranslation();
    // Plugin gate — Arcade TV toggle only renders when the arcade-tv
    // plugin is installed + activated (HandleInertiaRequests shares the
    // boolean as `arcadeTvEnabled`). Hides the toggle entirely on
    // installs that don't have the plugin so admins don't see a dead
    // option that does nothing.
    const arcadeTvEnabled = Boolean(
        (usePage().props as { arcadeTvEnabled?: boolean }).arcadeTvEnabled,
    );
    const { data, setData, post, processing, errors, reset } = useForm<{
        _method: string;
        title: string;
        slug: string;
        short_description: string;
        description: string;
        category_id: string;
        game_type: GameType;
        rom_system: string;
        arcade_core: string | null;
        embed_url: string;
        width: number;
        height: number;
        controls: string;
        tags: string;
        is_featured: boolean;
        is_active: boolean;
        is_mobile_compatible: boolean;
        api_enabled: boolean;
        is_vip_only: boolean;
        is_arcade_tv_compatible: boolean;
        game_file: File | null;
        game_image: File | null;
        game_preview: File | null;
    }>({
        _method: mode === 'update' ? 'put' : 'post',
        title: initialData?.title ?? '',
        slug: initialData?.slug ?? '',
        short_description: initialData?.short_description ?? '',
        description: initialData?.description ?? '',
        category_id: initialData?.category_id
            ? String(initialData.category_id)
            : (categories[0]?.id ? String(categories[0].id) : ''),
        game_type: (initialData?.game_type ?? 'html5') as GameType,
        rom_system: initialData?.rom_system ?? 'automatic',
        arcade_core: initialData?.arcade_core ?? null,
        embed_url: initialData?.embed_url ?? '',
        width: initialData?.width ?? 800,
        height: initialData?.height ?? 600,
        controls: initialData?.controls ?? '',
        tags: initialData?.tags ?? '',
        is_featured: initialData?.is_featured ?? false,
        is_active: initialData?.is_active ?? true,
        is_mobile_compatible: initialData?.is_mobile_compatible ?? false,
        api_enabled: initialData?.api_enabled ?? false,
        is_vip_only: initialData?.is_vip_only ?? false,
        is_arcade_tv_compatible: initialData?.is_arcade_tv_compatible ?? false,
        game_file: null,
        game_image: null,
        game_preview: null,
    });

    const [slugDirty, setSlugDirty] = useState<boolean>(Boolean(initialData?.slug));

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

    function submit(e: FormEvent) {
        e.preventDefault();
        const url = mode === 'create' ? '/dashboard/games' : `/dashboard/games/${initialData?.id}`;
        post(url, {
            forceFormData: true,
            preserveScroll: true,
            onSuccess: () => {
                reset();
                setSlugDirty(false);
                onSuccess?.();
            },
        });
    }

    const showFileUpload = ['html5', 'flash', 'tic80', 'rom'].includes(data.game_type);
    const showRomSystem = data.game_type === 'rom';
    const showEmbedUrl = data.game_type === 'embed';
    const existingThumbnail = initialData?.thumbnail?.thumbnail;
    // The admin Games paginator emits the raw column path
    // (`uploads/games/previews/foo.mp4`); other surfaces emit the prefixed URL.
    // Normalise both shapes to a `/storage/...` URL for the inline preview.
    const rawPreview = initialData?.preview_video ?? null;
    const existingPreview = rawPreview
        ? rawPreview.startsWith('/storage/') || rawPreview.startsWith('http')
            ? rawPreview
            : `/storage/${rawPreview}`
        : null;

    return (
        <form onSubmit={submit} className="space-y-6">
            <Section title={t('Basics')}>
                <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
                    <div className="space-y-1.5">
                        <Label htmlFor="title">{t('Title')}</Label>
                        <Input
                            id="title"
                            value={data.title}
                            onChange={(e) => setData('title', e.target.value)}
                            placeholder={t('e.g. Super Dodge Ball')}
                            aria-invalid={!!errors.title}
                        />
                        {errors.title && <p className="text-xs text-destructive">{errors.title}</p>}
                    </div>
                    <div className="space-y-1.5">
                        <Label htmlFor="slug">{t('Slug')}</Label>
                        <Input
                            id="slug"
                            value={data.slug}
                            onChange={(e) => {
                                setSlugDirty(true);
                                setData('slug', e.target.value);
                            }}
                            placeholder="super-dodge-ball"
                            aria-invalid={!!errors.slug}
                        />
                        {errors.slug ? (
                            <p className="text-xs text-destructive">{errors.slug}</p>
                        ) : (
                            <p className="text-xs text-muted-foreground">
                                {t('URL-safe identifier. Auto-generated from the title until edited.')}
                            </p>
                        )}
                    </div>
                </div>

                <div className="space-y-1.5">
                    <Label htmlFor="short_description">{t('Short description')}</Label>
                    <Textarea
                        id="short_description"
                        value={data.short_description}
                        onChange={(e) => setData('short_description', e.target.value)}
                        rows={2}
                        placeholder={t('One-line summary shown in game cards (max 500 chars).')}
                    />
                    {errors.short_description && (
                        <p className="text-xs text-destructive">{errors.short_description}</p>
                    )}
                </div>

                <div className="space-y-1.5">
                    <Label>{t('Description')}</Label>
                    <TipTapEditor
                        value={data.description}
                        onChange={(html) => setData('description', html)}
                    />
                    {errors.description && (
                        <p className="text-xs text-destructive">{errors.description}</p>
                    )}
                </div>
            </Section>

            <Separator />

            <Section title={t('Classification')}>
                <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
                    <div className="space-y-1.5">
                        <Label htmlFor="category_id">{t('Category')}</Label>
                        <Select
                            value={data.category_id}
                            onValueChange={(v) => setData('category_id', v)}
                        >
                            <SelectTrigger id="category_id">
                                <SelectValue placeholder={t('Pick a category')} />
                            </SelectTrigger>
                            <SelectContent>
                                {categories.map((c) => (
                                    <SelectItem key={c.id} value={String(c.id)}>
                                        {c.name}
                                    </SelectItem>
                                ))}
                            </SelectContent>
                        </Select>
                        {errors.category_id && (
                            <p className="text-xs text-destructive">{errors.category_id}</p>
                        )}
                    </div>
                    <div className="space-y-1.5">
                        <Label htmlFor="game_type">{t('Game type')}</Label>
                        <Select
                            value={data.game_type}
                            onValueChange={(v) => setData('game_type', v as GameType)}
                        >
                            <SelectTrigger id="game_type">
                                <SelectValue />
                            </SelectTrigger>
                            <SelectContent>
                                <SelectItem value="html5">HTML5</SelectItem>
                                <SelectItem value="flash">Flash</SelectItem>
                                <SelectItem value="rom">ROM</SelectItem>
                                <SelectItem value="tic80">TIC-80</SelectItem>
                                <SelectItem value="embed">Embed</SelectItem>
                            </SelectContent>
                        </Select>
                    </div>
                </div>

                {showRomSystem && (
                    <div className="space-y-1.5">
                        <Label htmlFor="rom_system">{t('ROM system')}</Label>
                        <RomSystemSelect
                            value={data.rom_system}
                            onChange={(v) => setData('rom_system', v)}
                        />
                        {errors.rom_system && (
                            <p className="text-xs text-destructive">{errors.rom_system}</p>
                        )}
                    </div>
                )}

                {showRomSystem && ['arcade', 'neogeo', 'mame'].includes(data.rom_system) && (
                    <div className="space-y-1.5">
                        <Label htmlFor="arcade_core">{t('Arcade core')}</Label>
                        <ArcadeCoreSelect
                            value={data.arcade_core}
                            onChange={(v) => setData('arcade_core', v)}
                        />
                        <p className="text-xs text-muted-foreground">
                            {t('If FBNeo reports "Romset is unknown" for this game, try MAME 2003 or MAME 2003 Plus — they cover different ROM-set versions.')}
                        </p>
                        {errors.arcade_core && (
                            <p className="text-xs text-destructive">{errors.arcade_core}</p>
                        )}
                    </div>
                )}

                <div className="space-y-1.5">
                    <Label htmlFor="tags">{t('Tags')}</Label>
                    <TagChipsInput
                        value={data.tags}
                        onChange={(v) => setData('tags', v)}
                        placeholder={t('Press Enter or comma to add (e.g. action, multiplayer)')}
                    />
                    {errors.tags && <p className="text-xs text-destructive">{errors.tags}</p>}
                </div>
            </Section>

            <Separator />

            <Section title={t('Source')}>
                {showFileUpload && (
                    <div className="space-y-1.5">
                        <Label htmlFor="game_file">
                            {t('Game file')}
                            {mode === 'update' && (
                                <span className="ms-1 text-xs font-normal text-muted-foreground">
                                    {t('— leave blank to keep the current file')}
                                </span>
                            )}
                        </Label>
                        {initialData?.game_file && !data.game_file && (
                            <p className="text-xs text-muted-foreground">
                                {t('Current:')} <code className="rounded bg-muted px-1">{initialData.game_file}</code>
                            </p>
                        )}
                        <FileInput
                            id="game_file"
                            accept={ACCEPT_BY_TYPE[data.game_type]}
                            value={data.game_file}
                            onChange={(f) => setData('game_file', f)}
                            emptyHint={initialData?.game_file ? t('Keep current file') : t('No file selected')}
                        />
                        {errors.game_file && (
                            <p className="text-xs text-destructive">{errors.game_file}</p>
                        )}
                    </div>
                )}

                {showEmbedUrl && (
                    <div className="space-y-1.5">
                        <Label htmlFor="embed_url">{t('Embed URL')}</Label>
                        <Input
                            id="embed_url"
                            type="url"
                            value={data.embed_url}
                            onChange={(e) => setData('embed_url', e.target.value)}
                            placeholder="https://example.com/embed/super-dodge-ball"
                            aria-invalid={!!errors.embed_url}
                        />
                        {errors.embed_url && (
                            <p className="text-xs text-destructive">{errors.embed_url}</p>
                        )}
                    </div>
                )}

                <div className="space-y-1.5">
                    <Label htmlFor="game_image">{t('Thumbnail')}</Label>
                    {existingThumbnail && !data.game_image && (
                        <img
                            src={existingThumbnail}
                            alt={t('Current thumbnail')}
                            className="h-20 rounded border bg-background object-cover"
                        />
                    )}
                    <FileInput
                        id="game_image"
                        accept="image/png,image/jpeg,image/webp,image/svg+xml"
                        value={data.game_image}
                        onChange={(f) => setData('game_image', f)}
                        emptyHint={existingThumbnail ? t('Keep current thumbnail') : t('No image selected')}
                    />
                    {errors.game_image ? (
                        <p className="text-xs text-destructive">{errors.game_image}</p>
                    ) : (
                        <p className="text-xs text-muted-foreground">
                            {t('PNG, JPG, or WEBP. We generate thumbnail, standard, and large variants.')}
                        </p>
                    )}
                </div>

                <div className="space-y-1.5">
                    <Label htmlFor="game_preview">{t('Hover preview clip')}</Label>
                    {existingPreview && !data.game_preview && (
                        // No autoPlay — admin-only edit screen, a looping clip
                        // in the periphery is distracting during long sessions.
                        // `controls` lets them tap play to verify the upload.
                        <video
                            src={existingPreview}
                            muted
                            loop
                            playsInline
                            controls
                            preload="metadata"
                            className="h-20 w-32 rounded border bg-black object-cover"
                            aria-label={t('Current preview')}
                        />
                    )}
                    <FileInput
                        id="game_preview"
                        accept="video/mp4,video/webm"
                        value={data.game_preview}
                        onChange={(f) => setData('game_preview', f)}
                        emptyHint={existingPreview ? t('Keep current preview') : t('No preview selected')}
                    />
                    {errors.game_preview ? (
                        <p className="text-xs text-destructive">{errors.game_preview}</p>
                    ) : (
                        <p className="text-xs text-muted-foreground">
                            {t('MP4 or WebM, up to 10 MB. Recommended: under 10 seconds, muted, around 480p. The clip plays on hover across the site.')}
                        </p>
                    )}
                </div>
            </Section>

            <Separator />

            <Section title={t('Display')}>
                <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
                    <div className="space-y-1.5">
                        <Label htmlFor="width">{t('Width')}</Label>
                        <Input
                            id="width"
                            type="number"
                            min={1}
                            value={data.width}
                            onChange={(e) => setData('width', Number(e.target.value))}
                            placeholder="800"
                        />
                        {errors.width && <p className="text-xs text-destructive">{errors.width}</p>}
                    </div>
                    <div className="space-y-1.5">
                        <Label htmlFor="height">{t('Height')}</Label>
                        <Input
                            id="height"
                            type="number"
                            min={1}
                            value={data.height}
                            onChange={(e) => setData('height', Number(e.target.value))}
                            placeholder="600"
                        />
                        {errors.height && <p className="text-xs text-destructive">{errors.height}</p>}
                    </div>
                </div>

                <div className="space-y-1.5">
                    <Label htmlFor="controls">{t('Controls')}</Label>
                    <Textarea
                        id="controls"
                        value={data.controls}
                        onChange={(e) => setData('controls', e.target.value)}
                        rows={2}
                        placeholder={t('e.g. Arrow keys to move, Space to jump.')}
                    />
                </div>
            </Section>

            <Separator />

            <Section title={t('Flags')}>
                <div className="grid grid-cols-1 sm:grid-cols-2 gap-4 rounded-md border bg-muted/30 p-4">
                    <ToggleRow
                        id="is_active"
                        label={t('Active')}
                        description={t('Visible on the public site.')}
                        checked={data.is_active}
                        onChange={(v) => setData('is_active', v)}
                    />
                    <ToggleRow
                        id="is_featured"
                        label={t('Featured')}
                        description={t('Promote to the Featured grid on the home page.')}
                        checked={data.is_featured}
                        onChange={(v) => setData('is_featured', v)}
                    />
                    <ToggleRow
                        id="is_mobile_compatible"
                        label={t('Mobile compatible')}
                        description={t('Render touch controls on phones and tablets.')}
                        checked={data.is_mobile_compatible}
                        onChange={(v) => setData('is_mobile_compatible', v)}
                    />
                    {/* Score API only works for engines that can post
                      * `{type:'score'}` to the parent — html5 and embed.
                      * Hide for rom/flash/tic80 since their carts have no
                      * postMessage hook (matches the author form gating). */}
                    {(data.game_type === 'html5' || data.game_type === 'embed') && (
                        <ToggleRow
                            id="api_enabled"
                            label={t('Scoring enabled')}
                            description={t('Allow this game to submit scores via the score API and appear on the per-game leaderboard. Turn off for games with no built-in score hook.')}
                            checked={data.api_enabled}
                            onChange={(v) => setData('api_enabled', v)}
                        />
                    )}
                    <ToggleRow
                        id="is_vip_only"
                        label={t('VIP only')}
                        description={t('Hide from non-VIP users.')}
                        checked={data.is_vip_only}
                        onChange={(v) => setData('is_vip_only', v)}
                    />
                    {/* Arcade TV — only meaningful for ROM/TIC-80 game types
                      * (the only engines wired through the TV iframe wrappers)
                      * AND only when the arcade-tv plugin is installed +
                      * activated. Hidden entirely otherwise so admins on
                      * installs without the plugin don't see a dead toggle. */}
                    {arcadeTvEnabled && ['rom', 'tic80'].includes(data.game_type) && (
                        <ToggleRow
                            id="is_arcade_tv_compatible"
                            label={t('Arcade TV compatible')}
                            description={t('Show this game on the /tv surface so it can be played on a TV with a phone-as-controller. Requires a controller-friendly game.')}
                            checked={data.is_arcade_tv_compatible}
                            onChange={(v) => setData('is_arcade_tv_compatible', v)}
                        />
                    )}
                </div>
            </Section>

            <div className="flex justify-end gap-2 pt-2">
                <Button type="submit" disabled={processing}>
                    {mode === 'create' ? t('Create game') : t('Save changes')}
                </Button>
            </div>
        </form>
    );
}
