'use client';

import { saveArticle } from '@/lib/feature/article/article.external';
import { uploadFile } from '@/lib/storage/storage.utils';
import { FileUploadField } from '@/ui/components/internal/file-upload-field';
import { Button } from '@/ui/components/shadcn/button';
import {
    Field,
    FieldError,
    FieldGroup,
    FieldLabel,
} from '@/ui/components/shadcn/field';
import { Input } from '@/ui/components/shadcn/input';
import {
    InputGroup,
    InputGroupTextarea,
} from '@/ui/components/shadcn/input-group';
import { zodResolver } from '@hookform/resolvers/zod';
import Image from 'next/image';
import { useCallback, useEffect, useRef, useState } from 'react';
import { Controller, useForm, useWatch } from 'react-hook-form';
import slugify from 'slugify';
import { toast } from 'sonner';
import { z } from 'zod';
import ImageLogo from '~/public/img/icons/cover-image.svg';
import MarkdownLogo from '~/public/img/icons/markdown-content.svg';

function isImageFile(file: File): boolean {
    return file.type.startsWith('image/');
}

function isContentFile(file: File): boolean {
    const extension = file.name.split('.').pop()?.toLowerCase() ?? '';
    return (
        file.type === 'text/markdown' ||
        file.type === 'text/plain' ||
        extension === 'md' ||
        extension === 'markdown' ||
        extension === 'txt'
    );
}

function validateImageFile(file: File): string | null {
    return isImageFile(file)
        ? null
        : 'Only image files are allowed for cover image';
}

function validateContentFile(file: File): string | null {
    return isContentFile(file)
        ? null
        : 'Only markdown or text files are allowed';
}

export const CreateArticleForm = () => {
    const [coverImageFile, setCoverImageFile] = useState<File | null>(null);
    const [coverImageUploading, setCoverImageUploading] = useState(false);
    const [contentFile, setContentFile] = useState<File | null>(null);
    const coverImageUrlRef = useRef<string | null>(null);

    const formSchema = z.object({
        title: z.string().min(3).max(255),
        slug: z.string().min(3),
        description: z.string().min(10),
        coverImageUrl: z.url('Cover image URL must be a valid URL'),
        content: z
            .string()
            .min(10, 'Article content must have at least 10 characters'),
    });

    const form = useForm<z.infer<typeof formSchema>>({
        resolver: zodResolver(formSchema),
        defaultValues: {
            title: '',
            slug: '',
            description: '',
            coverImageUrl: '',
            content: '',
        },
    });

    const title = useWatch({ control: form.control, name: 'title' });
    const coverImageUrl = useWatch({
        control: form.control,
        name: 'coverImageUrl',
    });
    useEffect(() => {
        if (!title) return;
        form.setValue('slug', slugify(title).toLowerCase());
    }, [form, title]);

    const resetFiles = useCallback(() => {
        if (coverImageUrlRef.current) {
            URL.revokeObjectURL(coverImageUrlRef.current);
            coverImageUrlRef.current = null;
        }
        setCoverImageFile(null);
        setCoverImageUploading(false);
        setContentFile(null);
    }, []);

    const handleFormSubmit = useCallback(
        async (data: z.infer<typeof formSchema>) => {
            try {
                const result = await saveArticle({ ...data });
                toast.success('Article created successfully!', {
                    description: `Article "${result.title}" has been created.`,
                    position: 'bottom-right',
                });
                form.reset();
                resetFiles();
            } catch (error) {
                toast.error('Failed to create article', {
                    description:
                        error instanceof Error
                            ? error.message
                            : 'An error occurred',
                    position: 'bottom-right',
                });
            }
        },
        [form, resetFiles]
    );

    const handleCoverImageFileChange = useCallback(
        async (file: File | null) => {
            if (coverImageUrlRef.current) {
                URL.revokeObjectURL(coverImageUrlRef.current);
                coverImageUrlRef.current = null;
            }
            setCoverImageFile(file);
            if (!file) {
                setCoverImageFile(null);
                setCoverImageUploading(false);
                form.setValue('coverImageUrl', '');
                return;
            }
            setCoverImageUploading(true);
            const fileMetadataResult = await uploadFile(file);
            setCoverImageUploading(false);
            if (!fileMetadataResult.ok) {
                setCoverImageFile(null);
                form.setValue('coverImageUrl', '');
                toast((fileMetadataResult.error as Error).message);
                return;
            }
            const fileMetadata = fileMetadataResult.value;
            coverImageUrlRef.current = fileMetadata.signedUrl;
            form.setValue('coverImageUrl', fileMetadata.signedUrl);
        },
        [form]
    );

    const handleContentFileChange = useCallback(
        async (file: File | null) => {
            setContentFile(file);
            if (file) {
                const content = await file.text();
                form.setValue('content', content);
            } else {
                form.setValue('content', '');
            }
        },
        [form]
    );

    const handleCoverImageReject = useCallback(
        (_file: File, message: string) => {
            toast.error(`Cover image rejected: ${message}`);
        },
        []
    );

    const handleContentFileReject = useCallback(
        (_file: File, message: string) => {
            toast.error(`Content file rejected: ${message}`);
        },
        []
    );

    return (
        <form
            id='form-create-article'
            // eslint-disable-next-line react-hooks/refs
            onSubmit={form.handleSubmit(handleFormSubmit)}
        >
            <FieldGroup className='gap-7'>
                <Controller
                    name='title'
                    control={form.control}
                    render={({ field, fieldState }) => (
                        <Field data-invalid={fieldState.invalid}>
                            <FieldLabel htmlFor='form-create-article-title'>
                                Title
                            </FieldLabel>
                            <Input
                                {...field}
                                id='form-create-article-title'
                                aria-invalid={fieldState.invalid}
                                placeholder='Article title'
                                autoComplete='off'
                            />
                            {fieldState.invalid && (
                                <FieldError errors={[fieldState.error]} />
                            )}
                        </Field>
                    )}
                />
                <Controller
                    name='slug'
                    control={form.control}
                    render={({ field, fieldState }) => (
                        <Field data-invalid={fieldState.invalid}>
                            <FieldLabel htmlFor='form-create-article-slug'>
                                Slug
                            </FieldLabel>
                            <Input
                                {...field}
                                id='form-create-article-slug'
                                aria-invalid={fieldState.invalid}
                                placeholder='article-slug'
                                autoComplete='off'
                            />
                            {fieldState.invalid && (
                                <FieldError errors={[fieldState.error]} />
                            )}
                        </Field>
                    )}
                />
                <Controller
                    name='description'
                    control={form.control}
                    render={({ field, fieldState }) => (
                        <Field data-invalid={fieldState.invalid}>
                            <FieldLabel htmlFor='form-create-article-description'>
                                Description
                            </FieldLabel>
                            <InputGroup>
                                <InputGroupTextarea
                                    {...field}
                                    id='form-create-article-description'
                                    placeholder='A simple but nice description of the article here.'
                                    rows={3}
                                    className='min-h-24 resize-none'
                                    aria-invalid={fieldState.invalid}
                                />
                            </InputGroup>
                            {fieldState.invalid && (
                                <FieldError errors={[fieldState.error]} />
                            )}
                        </Field>
                    )}
                />
                <div className='grid grid-cols-1 gap-4 sm:grid-cols-2'>
                    <FileUploadField
                        file={coverImageFile}
                        onFileChange={handleCoverImageFileChange}
                        accept='image/*'
                        validate={validateImageFile}
                        onFileReject={handleCoverImageReject}
                        label='Cover image'
                        description='PNG, JPG, GIF, WebP accepted'
                        error={form.formState.errors.coverImageUrl?.message}
                        previewUrl={coverImageUrl || undefined}
                        isUploading={coverImageUploading}
                        icon={
                            <Image
                                src={ImageLogo}
                                alt=''
                                aria-hidden='true'
                                className='size-6 shrink-0 opacity-60'
                            />
                        }
                    />
                    <FileUploadField
                        file={contentFile}
                        onFileChange={handleContentFileChange}
                        accept='.md,.markdown,.txt'
                        validate={validateContentFile}
                        onFileReject={handleContentFileReject}
                        label='Markdown content'
                        description='.md / .markdown / .txt accepted'
                        error={form.formState.errors.content?.message}
                        icon={
                            <Image
                                src={MarkdownLogo}
                                alt=''
                                aria-hidden='true'
                                className='size-6 shrink-0 opacity-60'
                            />
                        }
                    />
                </div>
            </FieldGroup>
            <div className='flex w-full justify-end'>
                <Button type='submit' className='mt-6'>
                    Create Article
                </Button>
            </div>
        </form>
    );
};

export default CreateArticleForm;
