import { Link, useForm } from '@inertiajs/react';
import { Check, Plus, X } from 'lucide-react';
import { useState } from 'react';
import type { FormEvent, KeyboardEvent } from 'react';
import InputError from '@/components/input-error';
import RichTextEditor from '@/components/rich-text-editor';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
    Select,
    SelectContent,
    SelectItem,
    SelectTrigger,
    SelectValue,
} from '@/components/ui/select';
import { Textarea } from '@/components/ui/textarea';
import { slugify } from '@/lib/utils';

export type TaxonomyOption = {
    id: number;
    name: string;
};

export type PostItem = {
    id: number;
    title: string;
    slug: string;
    feature_image_url: string | null;
    excerpt: string | null;
    content: string;
    status: 'draft' | 'published';
    published_at: string | null;
    categories: TaxonomyOption[];
    tags: TaxonomyOption[];
};

type PostFormData = {
    title: string;
    slug: string;
    feature_image: File | null;
    excerpt: string;
    content: string;
    status: 'draft' | 'published';
    published_at: string;
    category_ids: number[];
    tag_ids: number[];
    new_tags: string;
};

type PostFormProps = {
    post?: PostItem;
    postCategories: TaxonomyOption[];
    tags: TaxonomyOption[];
};

const currentDateTimeLocal = () => {
    const date = new Date();
    date.setMinutes(date.getMinutes() - date.getTimezoneOffset());

    return date.toISOString().slice(0, 16);
};

export default function PostForm({
    post,
    postCategories,
    tags,
}: PostFormProps) {
    const form = useForm<PostFormData>({
        title: post?.title ?? '',
        slug: post?.slug ?? '',
        feature_image: null,
        excerpt: post?.excerpt ?? '',
        content: post?.content ?? '',
        status: post?.status ?? 'draft',
        published_at:
            post?.published_at?.slice(0, 16) ?? currentDateTimeLocal(),
        category_ids: post?.categories.map((category) => category.id) ?? [],
        tag_ids: post?.tags.map((tag) => tag.id) ?? [],
        new_tags: '',
    });
    const [tagSearch, setTagSearch] = useState('');
    const [newTags, setNewTags] = useState<string[]>([]);

    const selectedSavedTags = tags.filter((tag) =>
        form.data.tag_ids.includes(tag.id),
    );
    const normalizedTagSearch = tagSearch.trim().toLowerCase();
    const matchingTags = tags
        .filter(
            (tag) =>
                !form.data.tag_ids.includes(tag.id) &&
                tag.name.toLowerCase().includes(normalizedTagSearch),
        )
        .slice(0, 8);
    const exactSavedTag = tags.find(
        (tag) => tag.name.toLowerCase() === normalizedTagSearch,
    );
    const canCreateTag =
        normalizedTagSearch !== '' &&
        !exactSavedTag &&
        !newTags.some((tag) => tag.toLowerCase() === normalizedTagSearch);

    const toggleId = (
        field: 'category_ids' | 'tag_ids',
        id: number,
        checked: boolean,
    ) => {
        const values = form.data[field];

        form.setData(
            field,
            checked ? [...values, id] : values.filter((value) => value !== id),
        );
    };

    const updateNewTags = (values: string[]) => {
        setNewTags(values);
        form.setData('new_tags', values.join(', '));
    };

    const selectSavedTag = (tag: TaxonomyOption) => {
        if (!form.data.tag_ids.includes(tag.id)) {
            form.setData('tag_ids', [...form.data.tag_ids, tag.id]);
        }

        setTagSearch('');
    };

    const addTagFromSearch = () => {
        const name = tagSearch.trim().replace(/,+$/, '').trim();

        if (!name) {
            return;
        }

        const savedTag = tags.find(
            (tag) => tag.name.toLowerCase() === name.toLowerCase(),
        );

        if (savedTag) {
            selectSavedTag(savedTag);

            return;
        }

        if (!newTags.some((tag) => tag.toLowerCase() === name.toLowerCase())) {
            updateNewTags([...newTags, name]);
        }

        setTagSearch('');
    };

    const handleTagKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
        if (event.key !== 'Enter' && event.key !== ',') {
            return;
        }

        event.preventDefault();
        addTagFromSearch();
    };

    const submit = (event: FormEvent) => {
        event.preventDefault();

        if (post) {
            form.transform((data) => ({ ...data, _method: 'put' }));
            form.post(`/posts/${post.id}`, {
                forceFormData: true,
                preserveScroll: true,
            });

            return;
        }

        form.post('/posts', {
            forceFormData: true,
            preserveScroll: true,
        });
    };

    return (
        <form onSubmit={submit} className="space-y-6 p-4">
            <div className="grid gap-4 sm:grid-cols-2">
                <div className="grid gap-2">
                    <Label htmlFor="title">Title</Label>
                    <Input
                        id="title"
                        value={form.data.title}
                        onChange={(event) => {
                            const title = event.target.value;
                            form.setData('title', title);
                            form.setData('slug', slugify(title));
                        }}
                    />
                    <InputError message={form.errors.title} />
                </div>

                <div className="grid gap-2">
                    <Label htmlFor="slug">Slug</Label>
                    <Input
                        id="slug"
                        value={form.data.slug}
                        readOnly
                        className="bg-muted"
                    />
                    <InputError message={form.errors.slug} />
                </div>
            </div>

            <div className="grid gap-4 sm:grid-cols-2">
                <div className="grid gap-2">
                    <Label>Status</Label>
                    <Select
                        value={form.data.status}
                        onValueChange={(value) =>
                            form.setData(
                                'status',
                                value as PostFormData['status'],
                            )
                        }
                    >
                        <SelectTrigger className="w-full">
                            <SelectValue />
                        </SelectTrigger>
                        <SelectContent>
                            <SelectItem value="draft">Draft</SelectItem>
                            <SelectItem value="published">Published</SelectItem>
                        </SelectContent>
                    </Select>
                    <InputError message={form.errors.status} />
                </div>

                <div className="grid gap-2">
                    <Label htmlFor="published_at">Publish date</Label>
                    <Input
                        id="published_at"
                        type="datetime-local"
                        value={form.data.published_at}
                        onChange={(event) =>
                            form.setData('published_at', event.target.value)
                        }
                    />
                    <InputError message={form.errors.published_at} />
                </div>
            </div>

            <div className="grid gap-2">
                <Label htmlFor="feature_image">Feature image</Label>
                <Input
                    id="feature_image"
                    type="file"
                    accept="image/*"
                    onChange={(event) =>
                        form.setData(
                            'feature_image',
                            event.target.files?.[0] ?? null,
                        )
                    }
                />
                {post?.feature_image_url && (
                    <div className="flex items-center gap-3 text-sm text-muted-foreground">
                        <img
                            src={post.feature_image_url}
                            alt={post.title}
                            className="size-16 rounded-md border object-cover"
                        />
                        Current image will be kept unless you upload a new one.
                    </div>
                )}
                <InputError message={form.errors.feature_image} />
            </div>

            <div className="grid gap-2">
                <Label htmlFor="excerpt">Excerpt</Label>
                <Textarea
                    id="excerpt"
                    value={form.data.excerpt}
                    onChange={(event) =>
                        form.setData('excerpt', event.target.value)
                    }
                    rows={4}
                />
                <InputError message={form.errors.excerpt} />
            </div>

            <div className="grid gap-4 sm:grid-cols-2">
                <div className="grid gap-2">
                    <Label>Categories</Label>
                    <div className="max-h-44 space-y-2 overflow-y-auto rounded-md border p-3">
                        {postCategories.length ? (
                            postCategories.map((category) => (
                                <label
                                    key={category.id}
                                    className="flex items-center gap-2 text-sm"
                                >
                                    <Checkbox
                                        checked={form.data.category_ids.includes(
                                            category.id,
                                        )}
                                        onCheckedChange={(checked) =>
                                            toggleId(
                                                'category_ids',
                                                category.id,
                                                checked === true,
                                            )
                                        }
                                    />
                                    {category.name}
                                </label>
                            ))
                        ) : (
                            <div className="text-sm text-muted-foreground">
                                No post categories saved.
                            </div>
                        )}
                    </div>
                    <InputError message={form.errors.category_ids} />
                </div>

                <div className="grid gap-2">
                    <Label htmlFor="tag_search">Tags</Label>
                    <div className="rounded-md border p-3">
                        {(selectedSavedTags.length > 0 ||
                            newTags.length > 0) && (
                            <div className="mb-3 flex flex-wrap gap-2">
                                {selectedSavedTags.map((tag) => (
                                    <span
                                        key={tag.id}
                                        className="inline-flex items-center gap-1 rounded-full bg-secondary px-2.5 py-1 text-xs"
                                    >
                                        {tag.name}
                                        <button
                                            type="button"
                                            onClick={() =>
                                                toggleId(
                                                    'tag_ids',
                                                    tag.id,
                                                    false,
                                                )
                                            }
                                            className="rounded-full hover:text-destructive"
                                            aria-label={`Remove ${tag.name}`}
                                        >
                                            <X className="size-3" />
                                        </button>
                                    </span>
                                ))}
                                {newTags.map((tag) => (
                                    <span
                                        key={tag}
                                        className="inline-flex items-center gap-1 rounded-full bg-primary px-2.5 py-1 text-xs text-primary-foreground"
                                    >
                                        {tag}
                                        <button
                                            type="button"
                                            onClick={() =>
                                                updateNewTags(
                                                    newTags.filter(
                                                        (value) =>
                                                            value !== tag,
                                                    ),
                                                )
                                            }
                                            className="rounded-full"
                                            aria-label={`Remove ${tag}`}
                                        >
                                            <X className="size-3" />
                                        </button>
                                    </span>
                                ))}
                            </div>
                        )}

                        <Input
                            id="tag_search"
                            value={tagSearch}
                            onChange={(event) =>
                                setTagSearch(event.target.value)
                            }
                            onKeyDown={handleTagKeyDown}
                            placeholder="Search or add a tag..."
                            autoComplete="off"
                        />

                        {tagSearch.trim() && (
                            <div className="mt-2 max-h-40 overflow-y-auto rounded-md border">
                                {matchingTags.map((tag) => (
                                    <button
                                        key={tag.id}
                                        type="button"
                                        onClick={() => selectSavedTag(tag)}
                                        className="flex w-full items-center justify-between px-3 py-2 text-left text-sm hover:bg-muted"
                                    >
                                        {tag.name}
                                        {exactSavedTag?.id === tag.id && (
                                            <Check className="size-4" />
                                        )}
                                    </button>
                                ))}

                                {canCreateTag && (
                                    <button
                                        type="button"
                                        onClick={addTagFromSearch}
                                        className="flex w-full items-center gap-2 border-t px-3 py-2 text-left text-sm hover:bg-muted"
                                    >
                                        <Plus className="size-4" />
                                        Add “{tagSearch.trim()}”
                                    </button>
                                )}

                                {matchingTags.length === 0 && !canCreateTag && (
                                    <div className="px-3 py-2 text-sm text-muted-foreground">
                                        Tag already selected.
                                    </div>
                                )}
                            </div>
                        )}
                    </div>
                    <InputError message={form.errors.tag_ids} />
                    <InputError message={form.errors.new_tags} />
                    <p className="text-xs text-muted-foreground">
                        Search saved tags, or type a new tag and press Enter.
                    </p>
                </div>
            </div>

            <div className="grid gap-2">
                <Label>Content</Label>
                <RichTextEditor
                    value={form.data.content}
                    onChange={(value) => form.setData('content', value)}
                />
                <InputError message={form.errors.content} />
            </div>

            <div className="flex justify-end gap-2">
                <Button variant="outline" asChild>
                    <Link href="/posts">Cancel</Link>
                </Button>
                <Button disabled={form.processing}>Save</Button>
            </div>
        </form>
    );
}
