'use client';

import { useEffect, useRef, useState, useCallback } from 'react';
import { useLoaderData, useSubmit, useNavigation, useActionData, useSearchParams, useNavigate, redirect } from 'react-router';
import type { Route } from './+types/dashboard';
import QRCode from 'qrcode';
import { toast } from 'sonner';
import { pb } from '~/lib/pocketbase';
import { ImageOff, LogOut, FileCode, Edit2, Trash2, X, Eye, RefreshCw } from 'lucide-react';
import { FileOrUrlField, type InputMode } from '~/components/FileOrUrlField';
import { Button } from '~/components/ui/button';
import { Input } from '~/components/ui/input';
import { Badge } from '~/components/ui/badge';
import { Label } from '~/components/ui/label';
import { Card, CardHeader, CardTitle, CardContent } from '~/components/ui/card';
import { ConfirmDialog } from '~/components/ui/confirm-dialog';
import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

const DEFAULT_FORM_VALUES = {
    id: '',
    name: '',
    glbMode: 'file' as const,
    glbUrl: '',
    glbFile: null as File | null,
    existingGlbName: '',
    usdzMode: 'file' as const,
    usdzUrl: '',
    usdzFile: null as File | null,
    existingUsdzName: '',
};

function getPaginationRange(current: number, total: number) {
    const range: (number | string)[] = [];
    const delta = 1;

    for (let i = 1; i <= total; i++) {
        if (
            i === 1 ||
            i === total ||
            (i >= current - delta && i <= current + delta)
        ) {
            range.push(i);
        } else if (range[range.length - 1] !== '...') {
            range.push('...');
        }
    }
    return range;
}

const formSchema = z.object({
    id: z.string().optional(),
    name: z.string().min(1, 'Asset name is required'),
    glbMode: z.enum(['file', 'url']),
    glbUrl: z.string().optional(),
    glbFile: z.any().nullable().optional(),
    existingGlbName: z.string().optional(),
    usdzMode: z.enum(['file', 'url']),
    usdzUrl: z.string().optional(),
    usdzFile: z.any().nullable().optional(),
    existingUsdzName: z.string().optional(),
});

const assetSchema = formSchema.superRefine((data, ctx) => {
    // GLB Validation rules
    if (data.glbMode === 'url') {
        if (!data.glbUrl || data.glbUrl.trim() === '') {
            ctx.addIssue({
                code: 'custom',
                message: 'GLB URL is required when URL mode is active',
                path: ['glbUrl'],
            });
        } else {
            try {
                new URL(data.glbUrl);
            } catch (_) {
                ctx.addIssue({
                    code: 'custom',
                    message: 'GLB URL must be a valid absolute URL',
                    path: ['glbUrl'],
                });
            }
        }
    } else {
        if (!data.glbFile && !data.existingGlbName) {
            ctx.addIssue({
                code: 'custom',
                message: 'GLB file is required when Upload mode is active',
                path: ['glbFile'],
            });
        }
    }

    // USDZ optional URL Validation rules
    if (data.usdzMode === 'url' && data.usdzUrl && data.usdzUrl.trim() !== '') {
        try {
            new URL(data.usdzUrl);
        } catch (_) {
            ctx.addIssue({
                code: 'custom',
                message: 'USDZ URL must be a valid absolute URL',
                path: ['usdzUrl'],
            });
        }
    }
});

type FormValues = z.infer<typeof assetSchema>;

export async function clientLoader({ request }: Route.ClientLoaderArgs) {
    if (!pb.authStore.isValid) {
        return redirect('/login');
    }
    const url = new URL(request.url);
    const page = parseInt(url.searchParams.get('page') || '1', 10);

    try {
        const resultList = await pb.collection('ar_assets').getList(page, 10, {
            sort: '-created',
        });
        return {
            assets: resultList.items || [],
            page: resultList.page,
            totalPages: resultList.totalPages,
            totalItems: resultList.totalItems,
            error: null,
        };
    } catch (err: any) {
        console.error('Failed to load assets in clientLoader:', err);
        return {
            assets: [],
            page: 1,
            totalPages: 1,
            totalItems: 0,
            error: err?.message || 'Failed to load assets',
        };
    }
}

export async function clientAction({ request }: Route.ClientActionArgs) {
    if (!pb.authStore.isValid) {
        return redirect('/login');
    }

    const formData = await request.formData();
    const intent = formData.get('intent') as string;

    try {
        // --- DELETE INTENT ---
        if (intent === 'delete') {
            const id = formData.get('id') as string;
            if (!id) throw new Error('Missing asset ID');
            await pb.collection('ar_assets').delete(id);
            return { success: true, intent, message: 'Asset deleted successfully' };
        }

        // Parse fields for validation
        const glbFileVal = formData.get('glb_file');
        const usdzFileVal = formData.get('usdz_file');

        const rawFields = {
            id: (formData.get('id') as string) || undefined,
            name: (formData.get('name') as string) || undefined,
            glbMode: (formData.get('glbMode') as 'file' | 'url') || undefined,
            glbUrl: (formData.get('glb_url') as string) || undefined,
            glbFile: (glbFileVal instanceof File && glbFileVal.size > 0) ? glbFileVal : undefined,
            existingGlbName: (formData.get('existingGlbName') as string) || undefined,
            usdzMode: (formData.get('usdzMode') as 'file' | 'url') || undefined,
            usdzUrl: (formData.get('usdz_url') as string) || undefined,
            usdzFile: (usdzFileVal instanceof File && usdzFileVal.size > 0) ? usdzFileVal : undefined,
            existingUsdzName: (formData.get('existingUsdzName') as string) || undefined,
        };

        const validatedResult = assetSchema.safeParse(rawFields);
        if (!validatedResult.success) {
            return { success: false, error: validatedResult.error.issues[0].message };
        }

        const data = validatedResult.data;

        // --- CREATE INTENT ---
        if (intent === 'create') {
            const pbFormData = new FormData();
            pbFormData.append('name', data.name.trim());

            if (data.glbMode === 'file') {
                if (data.glbFile) pbFormData.append('glb_file', data.glbFile);
            } else {
                pbFormData.append('glb_url', (data.glbUrl || '').trim());
            }

            if (data.usdzMode === 'file') {
                if (data.usdzFile) pbFormData.append('usdz_file', data.usdzFile);
            } else {
                pbFormData.append('usdz_url', (data.usdzUrl || '').trim());
            }

            // Create record
            const record = await pb.collection('ar_assets').create(pbFormData);

            // Generate QR Code
            const qrUrl = `${import.meta.env.VITE_FRONTEND_URL}/ar/${record.id}`;
            const qrImage = await QRCode.toDataURL(qrUrl, {
                width: 512,
                margin: 2,
            });

            const qrBlob = await (await fetch(qrImage)).blob();
            const qrFormData = new FormData();
            qrFormData.append('qr_url', qrUrl);
            qrFormData.append('qr_image', new File([qrBlob], `${record.id}.png`, { type: 'image/png' }));

            // Update QR code fields
            await pb.collection('ar_assets').update(record.id, qrFormData);
            return { success: true, intent, message: 'New AR Asset created successfully' };
        }

        // --- UPDATE INTENT ---
        if (intent === 'update') {
            const id = data.id;
            if (!id) throw new Error('Missing asset ID');

            const pbFormData = new FormData();
            pbFormData.append('name', data.name.trim());

            if (data.glbMode === 'file') {
                if (data.glbFile) {
                    pbFormData.append('glb_file', data.glbFile);
                }
                pbFormData.append('glb_url', '');
            } else {
                pbFormData.append('glb_url', (data.glbUrl || '').trim());
                pbFormData.append('glb_file', '');
            }

            if (data.usdzMode === 'file') {
                if (data.usdzFile) {
                    pbFormData.append('usdz_file', data.usdzFile);
                } else if (!data.existingUsdzName) {
                    pbFormData.append('usdz_file', '');
                }
                pbFormData.append('usdz_url', '');
            } else {
                pbFormData.append('usdz_url', (data.usdzUrl || '').trim());
                pbFormData.append('usdz_file', '');
            }

            await pb.collection('ar_assets').update(id, pbFormData);
            return { success: true, intent, message: 'Asset updated successfully' };
        }

        return { success: false, error: 'Invalid intent' };
    } catch (err: any) {
        console.error('Action failure:', err);
        return { success: false, error: err?.message || 'Something went wrong' };
    }
}

export function HydrateFallback() {
    return (
        <div className="fixed inset-0 bg-[#0D0D0D] flex flex-col items-center justify-center">
            <div className="w-10 h-10 rounded-full border-2 border-orange-500/20 border-t-orange-500 animate-spin mb-4" />
            <p className="text-white/40 text-xs tracking-widest uppercase animate-pulse">Loading Dashboard…</p>
        </div>
    );
}

export default function Dashboard() {
    const navigate = useNavigate();
    const { assets, page, totalPages, totalItems, error } = useLoaderData<typeof clientLoader>();
    const actionData = useActionData<typeof clientAction>();
    const submit = useSubmit();
    const navigation = useNavigation();
    const [, setSearchParams] = useSearchParams();

    const [editingAsset, setEditingAsset] = useState<any | null>(null);
    const [deleteTargetId, setDeleteTargetId] = useState<string | null>(null);

    const glbInputRef = useRef<HTMLInputElement>(null);
    const usdzInputRef = useRef<HTMLInputElement>(null);

    const {
        register,
        handleSubmit,
        control,
        setValue,
        watch,
        reset,
        formState: { errors },
    } = useForm<FormValues>({
        resolver: zodResolver(assetSchema),
        defaultValues: DEFAULT_FORM_VALUES,
    });

    const glbMode = watch('glbMode');
    const usdzMode = watch('usdzMode');
    const existingGlbName = watch('existingGlbName');
    const existingUsdzName = watch('existingUsdzName');

    const isSubmitting = navigation.state !== 'idle';

    const handleLogout = useCallback(() => {
        pb.authStore.clear();
        toast.success('Logged out successfully');
        navigate('/login', { replace: true });
    }, [navigate]);

    const cancelEditing = useCallback(() => {
        setEditingAsset(null);
        reset(DEFAULT_FORM_VALUES);
        if (glbInputRef.current) glbInputRef.current.value = '';
        if (usdzInputRef.current) usdzInputRef.current.value = '';
    }, [reset]);

    const startEditing = useCallback((asset: any) => {
        if (!asset) return;
        setEditingAsset(asset);
        reset({
            id: asset.id,
            name: asset.name || '',
            glbMode: asset.glb_file ? 'file' : 'url',
            glbUrl: asset.glb_file ? '' : (asset.glb_url || ''),
            glbFile: null,
            existingGlbName: asset.glb_file || '',
            usdzMode: asset.usdz_file ? 'file' : 'url',
            usdzUrl: asset.usdz_file ? '' : (asset.usdz_url || ''),
            usdzFile: null,
            existingUsdzName: asset.usdz_file || '',
        });

        window.scrollTo({ top: 0, behavior: 'smooth' });
    }, [reset]);

    const handleDeleteConfirm = useCallback(() => {
        if (!deleteTargetId) return;
        const formData = new FormData();
        formData.append('intent', 'delete');
        formData.append('id', deleteTargetId);

        submit(formData, { method: 'post' });

        if (editingAsset?.id === deleteTargetId) {
            cancelEditing();
        }
        setDeleteTargetId(null);
    }, [deleteTargetId, submit, editingAsset, cancelEditing]);

    const onSubmit = useCallback((data: FormValues) => {
        const formData = new FormData();
        formData.append('intent', editingAsset ? 'update' : 'create');
        formData.append('name', data.name.trim());
        formData.append('glbMode', data.glbMode);
        formData.append('usdzMode', data.usdzMode);

        if (editingAsset) {
            formData.append('id', editingAsset.id);
        }

        // GLB
        if (data.glbMode === 'file') {
            if (data.glbFile) {
                formData.append('glb_file', data.glbFile);
            }
            formData.append('existingGlbName', data.existingGlbName || '');
        } else {
            formData.append('glb_url', (data.glbUrl || '').trim());
        }

        // USDZ
        if (data.usdzMode === 'file') {
            if (data.usdzFile) {
                formData.append('usdz_file', data.usdzFile);
            } else {
                formData.append('existingUsdzName', data.existingUsdzName || '');
            }
        } else {
            formData.append('usdz_url', (data.usdzUrl || '').trim());
        }

        submit(formData, { method: 'post', encType: 'multipart/form-data' });
    }, [editingAsset, submit]);

    const handlePageChange = useCallback((pageNum: number) => {
        setSearchParams({ page: pageNum.toString() });
    }, [setSearchParams]);

    useEffect(() => {
        if (error) {
            toast.error(error);
        }
    }, [error]);

    useEffect(() => {
        if (actionData) {
            if (actionData.success) {
                toast.success(actionData.message);
                if (actionData.intent === 'create' || actionData.intent === 'update') {
                    cancelEditing();
                }
            } else if (actionData.error) {
                toast.error(actionData.error);
            }
        }
    }, [actionData, cancelEditing]);

    return (
        <div className='min-h-screen bg-[#0D0D0D] text-white px-6 py-10 font-sans selection:bg-orange-500/20'>
            <div className='mx-auto max-w-5xl space-y-10'>
                <div className='flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 border-b border-neutral-900 pb-6'>
                    <div className='flex items-center gap-4'>
                        <div className="w-10 h-10 bg-black border border-neutral-800 rounded-xl flex items-center justify-center shrink-0">
                            <span className="text-orange-500 font-extrabold text-2xl pl-0.5 select-none">t.</span>
                        </div>
                        <div>
                            <h1 className='text-2xl font-bold tracking-tight text-white'>
                                AR Asset Dashboard
                            </h1>
                            <p className='text-xs text-neutral-500 mt-0.5 uppercase tracking-wider'>
                                Manage 3D assets and generate AR QR codes
                            </p>
                        </div>
                    </div>
                    <div className='flex items-center gap-3'>
                        {pb.authStore.model?.email && (
                            <Badge variant="orange" className="lowercase tracking-normal font-medium">
                                {pb.authStore.model.email}
                            </Badge>
                        )}
                        <Button
                            variant="outline"
                            size="sm"
                            onClick={handleLogout}
                            className="h-9 gap-1.5"
                        >
                            <LogOut size={14} />
                            <span>Sign Out</span>
                        </Button>
                    </div>
                </div>

                <form
                    onSubmit={handleSubmit(onSubmit)}
                    className='rounded-2xl bg-[#121212] border border-neutral-900 p-6 shadow-2xl transition-all duration-300'
                >
                    <div className='flex items-center justify-between mb-6'>
                        <h2 className='text-base font-bold text-white uppercase tracking-wider text-orange-500'>
                            {editingAsset ? `Edit Asset: ${editingAsset.name}` : 'Create New Asset'}
                        </h2>
                        {editingAsset && (
                            <Button
                                type="button"
                                variant="ghost"
                                size="sm"
                                onClick={cancelEditing}
                                className="h-8 hover:bg-neutral-800 text-neutral-400 hover:text-white"
                            >
                                <X size={13} />
                                <span>Cancel Edit</span>
                            </Button>
                        )}
                    </div>

                    <div className='space-y-5'>
                        <div className='space-y-1.5'>
                            <Label className='ml-1'>
                                Asset Name
                            </Label>
                            <Input
                                placeholder='Enter asset name'
                                {...register('name')}
                            />
                            {errors.name && (
                                <span className="text-[11px] font-semibold text-red-500 ml-1 block tracking-wide">
                                    {errors.name.message}
                                </span>
                            )}
                        </div>

                        {/* GLB — Android / Web */}
                        <div className='space-y-1.5'>
                            <Controller
                                name="glbFile"
                                control={control}
                                render={({ field }) => (
                                    <FileOrUrlField
                                        label='GLB File (Android / Web)'
                                        required
                                        mode={glbMode}
                                        onModeChange={(mode) => setValue('glbMode', mode, { shouldValidate: true })}
                                        accept='.glb'
                                        file={field.value}
                                        onFileChange={field.onChange}
                                        inputRef={glbInputRef}
                                        url={watch('glbUrl') || ''}
                                        onUrlChange={(val) => setValue('glbUrl', val, { shouldValidate: true })}
                                        urlPlaceholder='https://example.com/model.glb'
                                        existingFileName={existingGlbName}
                                        onClearExistingFile={() => setValue('existingGlbName', '', { shouldValidate: true })}
                                    />
                                )}
                            />
                            {errors.glbFile && (
                                <span className="text-[11px] font-semibold text-red-500 ml-1 block tracking-wide">
                                    {errors.glbFile.message as string}
                                </span>
                            )}
                            {errors.glbUrl && (
                                <span className="text-[11px] font-semibold text-red-500 ml-1 block tracking-wide">
                                    {errors.glbUrl.message}
                                </span>
                            )}
                        </div>

                        {/* USDZ — iOS */}
                        <div className='space-y-1.5'>
                            <Controller
                                name="usdzFile"
                                control={control}
                                render={({ field }) => (
                                    <FileOrUrlField
                                        label='USDZ File (iOS) — Optional'
                                        mode={usdzMode}
                                        onModeChange={(mode) => setValue('usdzMode', mode, { shouldValidate: true })}
                                        accept='.usdz,.reality'
                                        file={field.value}
                                        onFileChange={field.onChange}
                                        inputRef={usdzInputRef}
                                        url={watch('usdzUrl') || ''}
                                        onUrlChange={(val) => setValue('usdzUrl', val, { shouldValidate: true })}
                                        urlPlaceholder='https://example.com/model.usdz'
                                        existingFileName={existingUsdzName}
                                        onClearExistingFile={() => setValue('existingUsdzName', '', { shouldValidate: true })}
                                    />
                                )}
                            />
                            {errors.usdzFile && (
                                <span className="text-[11px] font-semibold text-red-500 ml-1 block tracking-wide">
                                    {errors.usdzFile.message as string}
                                </span>
                            )}
                            {errors.usdzUrl && (
                                <span className="text-[11px] font-semibold text-red-500 ml-1 block tracking-wide">
                                    {errors.usdzUrl.message}
                                </span>
                            )}
                        </div>
                    </div>

                    <div className='mt-6 flex items-center gap-3'>
                        <Button
                            type="submit"
                            disabled={isSubmitting}
                            className="w-full sm:w-auto h-11"
                        >
                            {isSubmitting ? (
                                <>
                                    <RefreshCw size={14} className="animate-spin text-black" />
                                    <span>Saving Asset…</span>
                                </>
                            ) : editingAsset ? (
                                'Save Changes'
                            ) : (
                                'Create Asset'
                            )}
                        </Button>
                        {editingAsset && (
                            <Button
                                type="button"
                                variant="secondary"
                                onClick={cancelEditing}
                                className="hidden sm:inline-flex h-11"
                            >
                                Cancel
                            </Button>
                        )}
                    </div>
                </form>

                <div>
                    <h2 className='mb-5 text-base font-bold text-neutral-400 uppercase tracking-wider ml-1'>
                        Generated Assets
                    </h2>

                    {assets.length === 0 ? (
                        <div className="text-center py-12 rounded-2xl bg-[#121212]/50 border border-dashed border-neutral-800">
                            <FileCode size={36} className="mx-auto text-neutral-600 mb-3" />
                            <p className='text-sm text-neutral-500'>
                                No assets created yet. Upload a model above to get started.
                            </p>
                        </div>
                    ) : (
                        <div className='space-y-6'>
                            <div className='grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3'>
                                {assets.map((asset) => {
                                    if (!asset) return null;
                                    return (
                                        <Card
                                            key={asset.id}
                                            className='group p-5 hover:border-orange-500/30 flex flex-col justify-between'
                                        >
                                            <div className='flex items-start justify-between gap-3 mb-4'>
                                                <h3 className='text-sm font-bold text-white group-hover:text-orange-500 transition-colors truncate flex-1 mt-1'>
                                                    {asset.name || 'Unnamed Asset'}
                                                </h3>
                                                <div className='flex items-center gap-1 select-none'>
                                                    <button
                                                        type="button"
                                                        onClick={() => startEditing(asset)}
                                                        className='p-1.5 rounded-lg text-neutral-500 hover:text-orange-500 hover:bg-neutral-800/80 transition-all cursor-pointer'
                                                        title='Edit Asset'
                                                    >
                                                        <Edit2 size={13} />
                                                    </button>
                                                    <button
                                                        type="button"
                                                        onClick={() => setDeleteTargetId(asset.id)}
                                                        className='p-1.5 rounded-lg text-neutral-500 hover:text-red-500 hover:bg-neutral-800/80 transition-all cursor-pointer'
                                                        title='Delete Asset'
                                                    >
                                                        <Trash2 size={13} />
                                                    </button>
                                                </div>
                                            </div>

                                            <div
                                                onClick={() => navigate('/preview/' + asset.id)}
                                                className="relative bg-white p-3.5 rounded-xl flex items-center justify-center max-w-[190px] w-full mx-auto shadow-md cursor-pointer hover:scale-[1.03] transition-transform duration-200 group/qr"
                                            >
                                                <div className="absolute inset-0 bg-black/60 rounded-xl opacity-0 group-hover/qr:opacity-100 flex flex-col items-center justify-center gap-1.5 transition-opacity duration-200">
                                                    <Eye size={20} className="text-orange-500 animate-bounce" />
                                                    <span className="text-[10px] font-bold text-white uppercase tracking-wider">Preview 3D & QR</span>
                                                </div>

                                                {asset.qr_image ? (
                                                    <img
                                                        src={pb.files.getURL(asset, asset.qr_image)}
                                                        alt={asset.name}
                                                        className='w-full aspect-square rounded-lg object-contain'
                                                    />
                                                ) : (
                                                    asset.qr_file ? (
                                                        <img
                                                            src={pb.files.getURL(asset, asset.qr_file)}
                                                            alt={asset.name}
                                                            className='w-full aspect-square rounded-lg object-contain'
                                                        />
                                                    ) : (
                                                        <div className='flex aspect-square w-full flex-col items-center justify-center space-y-2 text-neutral-400 bg-neutral-100 rounded-lg p-4'>
                                                            <ImageOff size={28} strokeWidth={1.5} className="text-neutral-400" />
                                                            <span className='text-[10px] font-semibold uppercase tracking-wider text-neutral-500'>No QR Image</span>
                                                        </div>
                                                    )
                                                )}
                                            </div>
                                        </Card>
                                    );
                                })}
                            </div>

                            {totalPages > 1 && (
                                <div className="mt-8 flex flex-col sm:flex-row items-center justify-between gap-4 border-t border-neutral-900 pt-6">
                                    <span className="text-xs text-neutral-500 select-none font-medium">
                                        Showing {Math.min((page - 1) * 10 + 1, totalItems)} to {Math.min(page * 10, totalItems)} of {totalItems} assets
                                    </span>
                                    <div className="flex items-center gap-2">
                                        <Button
                                            type="button"
                                            variant="outline"
                                            size="sm"
                                            disabled={page === 1}
                                            onClick={() => handlePageChange(page - 1)}
                                            className="px-3"
                                        >
                                            Previous
                                        </Button>
                                        {getPaginationRange(page, totalPages).map((pageNum, idx) => {
                                            if (pageNum === '...') {
                                                return (
                                                    <span key={`ellipsis-${idx}`} className="text-neutral-600 px-2 select-none">
                                                        …
                                                    </span>
                                                );
                                            }
                                            return (
                                                <Button
                                                    type="button"
                                                    key={pageNum}
                                                    variant={page === pageNum ? "default" : "outline"}
                                                    size="sm"
                                                    className="w-9 h-9 p-0"
                                                    onClick={() => handlePageChange(pageNum as number)}
                                                >
                                                    {pageNum}
                                                </Button>
                                            );
                                        })}
                                        <Button
                                            type="button"
                                            variant="outline"
                                            size="sm"
                                            disabled={page === totalPages}
                                            onClick={() => handlePageChange(page + 1)}
                                            className="px-3"
                                        >
                                            Next
                                        </Button>
                                    </div>
                                </div>
                            )}
                        </div>
                    )}
                </div>
            </div>

            <ConfirmDialog
                open={!!deleteTargetId}
                onOpenChange={(open) => !open && setDeleteTargetId(null)}
                title="Delete 3D Asset"
                description="Are you sure you want to delete this asset? This action cannot be undone and will permanently delete the model files."
                confirmText="Delete Asset"
                variant="destructive"
                onConfirm={handleDeleteConfirm}
            />
        </div>
    );
}
