import { Save } from 'lucide-react';
import type { FormEvent } from 'react';
import { useState } from 'react';

import {
    ConfigError,
    Field,
    RetrievalNumberField,
} from '@/components/admin/admin-form-parts';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input';
import { Spinner } from '@/components/ui/spinner';
import { stringValue } from '@/lib/admin';
import { patchRetrievalConfig, patchVectorDbConfig } from '@/lib/rag-client';
import type {
    RetrievalConfig,
    RetrievalForm,
    VectorDbConfig,
    VectorDbForm,
} from '@/types/admin';

export function AdminVectorDbForm({
    vectorDb,
}: {
    vectorDb?: VectorDbConfig | null;
}) {
    const cfg = vectorDb ?? {};
    const [formData, setFormData] = useState<VectorDbForm>({
        chunk_overlap: stringValue(cfg.chunk_overlap),
        chunk_size: stringValue(cfg.chunk_size),
        embedding_model: stringValue(cfg.embedding_model),
        persist_path: stringValue(cfg.persist_path),
    });
    const [isSubmitting, setIsSubmitting] = useState(false);
    const [error, setError] = useState<string | undefined>();

    function setField<K extends keyof VectorDbForm>(
        key: K,
        value: VectorDbForm[K],
    ): void {
        setFormData((prev) => ({ ...prev, [key]: value }));
    }

    async function handleSubmit(
        event: FormEvent<HTMLFormElement>,
    ): Promise<void> {
        event.preventDefault();
        setIsSubmitting(true);
        setError(undefined);

        try {
            const payload: Record<string, unknown> = {};
            if (formData.embedding_model) payload.embedding_model = formData.embedding_model;
            if (formData.persist_path) payload.persist_path = formData.persist_path;
            if (formData.chunk_size) payload.chunk_size = parseInt(formData.chunk_size, 10);
            if (formData.chunk_overlap) payload.chunk_overlap = parseInt(formData.chunk_overlap, 10);

            const result = await patchVectorDbConfig(payload);
            setFormData({
                chunk_overlap: stringValue(result.chunk_overlap),
                chunk_size: stringValue(result.chunk_size),
                embedding_model: stringValue(result.embedding_model),
                persist_path: stringValue(result.persist_path),
            });
        } catch (e) {
            setError(
                e instanceof Error
                    ? e.message
                    : 'Gagal menyimpan konfigurasi Vector DB.',
            );
        } finally {
            setIsSubmitting(false);
        }
    }

    return (
        <Card className="border-(--lecturer-border) bg-(--lecturer-surface)">
            <CardContent className="pt-6">
                <form
                    className="grid gap-4"
                    onSubmit={(e) => {
                        void handleSubmit(e);
                    }}
                >
                    <ConfigError message={error} />

                    <div className="grid gap-3 md:grid-cols-2">
                        <Field
                            hint="Model yang mengubah teks menjadi vektor untuk pencarian semantik, contoh: text-embedding-3-small."
                            label="Embedding Model"
                        >
                            <Input
                                value={formData.embedding_model}
                                onChange={(event) =>
                                    setField(
                                        'embedding_model',
                                        event.target.value,
                                    )
                                }
                            />
                        </Field>
                        <Field
                            hint="Lokasi folder penyimpanan database vektor di server."
                            label="Persist Path"
                        >
                            <Input
                                value={formData.persist_path}
                                onChange={(event) =>
                                    setField('persist_path', event.target.value)
                                }
                            />
                        </Field>
                        <Field
                            hint="Ukuran potongan teks dokumen saat diindeks (dalam karakter). Makin besar, makin banyak konteks per potongan."
                            label="Chunk Size"
                        >
                            <Input
                                min="1"
                                type="number"
                                value={formData.chunk_size}
                                onChange={(event) =>
                                    setField('chunk_size', event.target.value)
                                }
                            />
                        </Field>
                        <Field
                            hint="Jumlah karakter yang tumpang tindih antar potongan agar konteks tidak terputus di batas potongan."
                            label="Chunk Overlap"
                        >
                            <Input
                                min="0"
                                type="number"
                                value={formData.chunk_overlap}
                                onChange={(event) =>
                                    setField('chunk_overlap', event.target.value)
                                }
                            />
                        </Field>
                    </div>

                    <div className="flex justify-end">
                        <Button disabled={isSubmitting} type="submit">
                            {isSubmitting ? (
                                <Spinner className="size-4" />
                            ) : (
                                <Save className="size-4" />
                            )}
                            Simpan Vector
                        </Button>
                    </div>
                </form>
            </CardContent>
        </Card>
    );
}

export function AdminRetrievalForm({
    retrieval,
}: {
    retrieval?: RetrievalConfig | null;
}) {
    const cfg = retrieval ?? {};
    const [formData, setFormData] = useState<RetrievalForm>({
        bm25_weight: stringValue(cfg.bm25_weight),
        candidate_pool_size: stringValue(cfg.candidate_pool_size),
        dense_weight: stringValue(cfg.dense_weight),
        enable_reranker: Boolean(cfg.enable_reranker),
        history_turns: stringValue(cfg.history_turns),
        lexical_weight: stringValue(cfg.lexical_weight),
        max_context_chars: stringValue(cfg.max_context_chars),
        neighbor_window: stringValue(cfg.neighbor_window),
        reranker_model: stringValue(cfg.reranker_model),
        similarity_threshold: stringValue(cfg.similarity_threshold),
        top_k: stringValue(cfg.top_k),
    });
    const [isSubmitting, setIsSubmitting] = useState(false);
    const [error, setError] = useState<string | undefined>();

    function setField<K extends keyof RetrievalForm>(
        key: K,
        value: RetrievalForm[K],
    ): void {
        setFormData((prev) => ({ ...prev, [key]: value }));
    }

    async function handleSubmit(
        event: FormEvent<HTMLFormElement>,
    ): Promise<void> {
        event.preventDefault();
        setIsSubmitting(true);
        setError(undefined);

        try {
            const payload: Record<string, unknown> = {
                enable_reranker: formData.enable_reranker,
            };
            if (formData.top_k) payload.top_k = parseInt(formData.top_k, 10);
            if (formData.max_context_chars) payload.max_context_chars = parseInt(formData.max_context_chars, 10);
            if (formData.bm25_weight) payload.bm25_weight = parseFloat(formData.bm25_weight);
            if (formData.dense_weight) payload.dense_weight = parseFloat(formData.dense_weight);
            if (formData.lexical_weight) payload.lexical_weight = parseFloat(formData.lexical_weight);
            if (formData.similarity_threshold) payload.similarity_threshold = parseFloat(formData.similarity_threshold);
            if (formData.history_turns) payload.history_turns = parseInt(formData.history_turns, 10);
            if (formData.candidate_pool_size) payload.candidate_pool_size = parseInt(formData.candidate_pool_size, 10);
            if (formData.neighbor_window) payload.neighbor_window = parseInt(formData.neighbor_window, 10);
            if (formData.reranker_model) payload.reranker_model = formData.reranker_model;

            const result = await patchRetrievalConfig(payload);
            setFormData({
                bm25_weight: stringValue(result.bm25_weight),
                candidate_pool_size: stringValue(result.candidate_pool_size),
                dense_weight: stringValue(result.dense_weight),
                enable_reranker: Boolean(result.enable_reranker),
                history_turns: stringValue(result.history_turns),
                lexical_weight: stringValue(result.lexical_weight),
                max_context_chars: stringValue(result.max_context_chars),
                neighbor_window: stringValue(result.neighbor_window),
                reranker_model: stringValue(result.reranker_model),
                similarity_threshold: stringValue(result.similarity_threshold),
                top_k: stringValue(result.top_k),
            });
        } catch (e) {
            setError(
                e instanceof Error
                    ? e.message
                    : 'Gagal menyimpan konfigurasi retrieval.',
            );
        } finally {
            setIsSubmitting(false);
        }
    }

    return (
        <Card className="border-(--lecturer-border) bg-(--lecturer-surface)">
            <CardContent className="pt-6">
                <form
                    className="grid gap-4"
                    onSubmit={(e) => {
                        void handleSubmit(e);
                    }}
                >
                    <ConfigError message={error} />

                    <div className="grid gap-3 md:grid-cols-2">
                        <RetrievalNumberField
                            hint="Jumlah dokumen relevan yang diambil dari database per pertanyaan."
                            label="Top K"
                            value={formData.top_k}
                            onChange={(v) => setField('top_k', v)}
                        />
                        <RetrievalNumberField
                            hint="Batas maksimum karakter konteks dokumen yang dikirim ke model AI."
                            label="Max Context Chars"
                            value={formData.max_context_chars}
                            onChange={(v) => setField('max_context_chars', v)}
                        />
                        <RetrievalNumberField
                            hint="Bobot pencarian berbasis kata kunci (0–1). Tinggi = lebih mengutamakan kecocokan kata."
                            label="BM25 Weight"
                            step="0.01"
                            value={formData.bm25_weight}
                            onChange={(v) => setField('bm25_weight', v)}
                        />
                        <RetrievalNumberField
                            hint="Bobot pencarian berbasis makna/semantik (0–1). Tinggi = lebih mengutamakan kesamaan makna."
                            label="Dense Weight"
                            step="0.01"
                            value={formData.dense_weight}
                            onChange={(v) => setField('dense_weight', v)}
                        />
                        <RetrievalNumberField
                            hint="Bobot pencarian leksikal untuk keseimbangan antara kata kunci dan semantik."
                            label="Lexical Weight"
                            step="0.01"
                            value={formData.lexical_weight}
                            onChange={(v) => setField('lexical_weight', v)}
                        />
                        <RetrievalNumberField
                            hint="Ambang batas skor kemiripan minimum (0–1). Dokumen di bawah nilai ini diabaikan."
                            label="Similarity Threshold"
                            step="0.01"
                            value={formData.similarity_threshold}
                            onChange={(v) =>
                                setField('similarity_threshold', v)
                            }
                        />
                        <RetrievalNumberField
                            hint="Jumlah riwayat percakapan sebelumnya yang disertakan sebagai konteks pertanyaan."
                            label="History Turns"
                            value={formData.history_turns}
                            onChange={(v) => setField('history_turns', v)}
                        />
                        <RetrievalNumberField
                            hint="Jumlah kandidat dokumen awal sebelum proses reranking dilakukan."
                            label="Candidate Pool"
                            value={formData.candidate_pool_size}
                            onChange={(v) =>
                                setField('candidate_pool_size', v)
                            }
                        />
                        <RetrievalNumberField
                            hint="Jumlah dokumen tetangga yang disertakan untuk memperluas konteks jawaban."
                            label="Neighbor Window"
                            value={formData.neighbor_window}
                            onChange={(v) => setField('neighbor_window', v)}
                        />
                        <Field
                            hint="Nama model yang mengurutkan ulang hasil pencarian agar paling relevan di urutan teratas."
                            label="Reranker Model"
                        >
                            <Input
                                value={formData.reranker_model}
                                onChange={(event) =>
                                    setField(
                                        'reranker_model',
                                        event.target.value,
                                    )
                                }
                            />
                        </Field>
                    </div>

                    <label className="flex items-center gap-3 text-sm">
                        <Checkbox
                            checked={formData.enable_reranker}
                            onCheckedChange={(value) =>
                                setField('enable_reranker', value === true)
                            }
                        />
                        <span>
                            Enable reranker
                            <span className="ml-1 text-xs text-muted-foreground">
                                — aktifkan untuk mengurutkan ulang hasil pencarian agar lebih akurat
                            </span>
                        </span>
                    </label>

                    <div className="flex justify-end">
                        <Button disabled={isSubmitting} type="submit">
                            {isSubmitting ? (
                                <Spinner className="size-4" />
                            ) : (
                                <Save className="size-4" />
                            )}
                            Simpan Retrieval
                        </Button>
                    </div>
                </form>
            </CardContent>
        </Card>
    );
}

export function AdminVectorRetrievalForms({
    retrieval,
    vectorDb,
}: {
    retrieval?: RetrievalConfig | null;
    vectorDb?: VectorDbConfig | null;
}) {
    return (
        <section className="grid gap-6 xl:grid-cols-2">
            <AdminVectorDbForm vectorDb={vectorDb} />
            <AdminRetrievalForm retrieval={retrieval} />
        </section>
    );
}
