import { Head } from '@inertiajs/react';
import {
    BookOpen,
    Bot,
    MessageSquare,
    Plus,
    Send,
    Sparkles,
    Trash2,
    UserRound,
    X,
} from 'lucide-react';
import type { CSSProperties, FormEvent, KeyboardEvent } from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';

import { LoadingIndicator } from '@/components/ui/loading-indicator';
import { Spinner } from '@/components/ui/spinner';
import type {
    ChatMessageResponse,
    ChatSessionResponse,
    CourseResponse,
} from '@/lib/rag';
import { createLocalUserMessage } from '@/lib/rag';
import type { AuthUser } from '@/lib/rag-client';
import {
    createChatSessionEmbed,
    deleteChatSessionEmbed,
    getChatHistoryEmbed,
    listChatSessionsEmbed,
    listCoursesEmbed,
    loginWithEmbedApiKey,
    sendRestChatMessage,
} from '@/lib/rag-client';

type EmbedConfig = {
    background: string;
    backgroundProfile: string;
    border: string;
    courseId?: string | null;
    danger: string;
    primary: string;
    primaryGradient: string;
    radius: string;
    subtitle: string;
    textPrimary: string;
    textSecondary: string;
    title: string;
    userName: string;
};


const COLOR_DEFAULTS = {
    background: '#ffffff',
    backgroundProfile: '#ffffff',
    border: '#739af1',
    danger: '#ef4444',
    primary: '#2764eb',
    primaryGradient: '#ccdcff',
    textPrimary: '#0a0a0a',
    textSecondary: '#737373',
} as const;

const EMBED_TOKEN_KEY = 'sevima_raghub_embed_token';
const EMBED_USER_KEY = 'sevima_raghub_embed_user';

type EmbedTokenResult = {
    token: string;
    user?: AuthUser;
};

function parseColor(value: string | null, fallback: string): string {
    if (!value) {
        return fallback;
    }

    const normalized = value.startsWith('#') ? value : `#${value}`;

    return /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(normalized)
        ? normalized
        : fallback;
}

function parseRadius(value: string | null): string {
    if (!value) {
        return '20px';
    }

    return /^\d{1,2}(?:px|rem)$/.test(value) ? value : '20px';
}

function parseEmbedConfig(params: URLSearchParams): EmbedConfig {
    return {
        background: parseColor(params.get('background'), COLOR_DEFAULTS.background),
        backgroundProfile: parseColor(params.get('background_profile'), COLOR_DEFAULTS.backgroundProfile),
        border: parseColor(params.get('border'), COLOR_DEFAULTS.border),
        courseId: params.get('course_id') ?? params.get('courseId') ?? undefined,
        danger: parseColor(params.get('danger'), COLOR_DEFAULTS.danger),
        primary: parseColor(params.get('primary'), COLOR_DEFAULTS.primary),
        primaryGradient: parseColor(params.get('primary_gradient'), COLOR_DEFAULTS.primaryGradient),
        radius: parseRadius(params.get('radius')),
        subtitle: params.get('subtitle') ?? 'Pilih mata kuliah, lalu kirim pertanyaan pertama untuk membuat sesi baru',
        textPrimary: parseColor(params.get('text_primary'), COLOR_DEFAULTS.textPrimary),
        textSecondary: parseColor(params.get('text_secondary'), COLOR_DEFAULTS.textSecondary),
        title: params.get('title') ?? 'RAG Hub',
        userName: params.get('user_name') ?? 'User',
    };
}

const COOKIE_MAX_AGE = 60 * 60 * 24 * 7; // 7 days

function setEmbedCookie(name: string, value: string): void {
    const secure = window.location.protocol === 'https:';
    const parts = [
        `${name}=${encodeURIComponent(value)}`,
        'path=/',
        `max-age=${COOKIE_MAX_AGE}`,
        'SameSite=None',
    ];

    if (secure) {
        parts.push('Secure');
    }

    document.cookie = parts.join('; ');
}

function getEmbedCookie(name: string): string | null {
    const match = document.cookie.match(
        new RegExp(`(?:^|;\\s*)${name}=([^;]*)`),
    );

    return match ? decodeURIComponent(match[1]) : null;
}

function getStoredEmbedToken(): string | null {
    return getEmbedCookie(EMBED_TOKEN_KEY);
}

function getStoredEmbedUser(): AuthUser | null {
    try {
        const raw = getEmbedCookie(EMBED_USER_KEY);
        return raw ? (JSON.parse(raw) as AuthUser) : null;
    } catch {
        return null;
    }
}

function storeEmbedSession(token: string, user?: AuthUser): void {
    setEmbedCookie(EMBED_TOKEN_KEY, token);

    if (user) {
        setEmbedCookie(EMBED_USER_KEY, JSON.stringify(user));
    }
}

async function resolveEmbedToken(
    params: URLSearchParams,
): Promise<EmbedTokenResult | null> {
    const stored = getStoredEmbedToken();

    if (stored) {
        return { token: stored, user: getStoredEmbedUser() ?? undefined };
    }

    const apiKey = params.get('api_key');

    if (apiKey) {
        try {
            const { token, user } = await loginWithEmbedApiKey(apiKey);
            storeEmbedSession(token, user);

            return { token, user };
        } catch {
            return null;
        }
    }

    return null;
}

function appendUniqueMessages(
    current: ChatMessageResponse[],
    next: ChatMessageResponse[],
): ChatMessageResponse[] {
    const ids = new Set(current.map((m) => m.uuid_id));

    return [...current, ...next.filter((m) => !ids.has(m.uuid_id))];
}

function isUserMessage(role: string): boolean {
    return ['human', 'student', 'user'].includes(role.toLowerCase());
}

function formatMessageTime(value?: string): string {
    if (!value) {
        return '';
    }

    const date = new Date(value);

    if (Number.isNaN(date.getTime())) {
        return value;
    }

    return new Intl.DateTimeFormat('en-US', {
        hour: 'numeric',
        minute: '2-digit',
    }).format(date);
}

function TypingText({ text }: { text: string }) {
    const [visibleLength, setVisibleLength] = useState(0);

    useEffect(() => {
        const intervalId = window.setInterval(() => {
            setVisibleLength((currentLength) => {
                if (currentLength >= text.length) {
                    window.clearInterval(intervalId);

                    return currentLength;
                }

                return currentLength + 1;
            });
        }, 14);

        return () => window.clearInterval(intervalId);
    }, [text]);

    return (
        <>
            {text.slice(0, visibleLength)}
            {visibleLength < text.length && (
                <span className="embed-message-caret" aria-hidden />
            )}
        </>
    );
}

function EmbedMessage({
    message,
    shouldAnimateTyping,
}: {
    message: ChatMessageResponse;
    shouldAnimateTyping?: boolean;
}) {
    const isUser = isUserMessage(message.role);

    return (
        <article
            className={
                isUser
                    ? 'embed-message embed-message--user'
                    : 'embed-message embed-message--assistant'
            }
        >
            {!isUser && (
                <span className="embed-message-avatar embed-message-avatar--assistant">
                    <Bot className="size-4" />
                </span>
            )}

            <div className="embed-message-bubble">
                <p className="embed-message-content">
                    {!isUser && shouldAnimateTyping ? (
                        <TypingText
                            key={message.uuid_id}
                            text={message.content}
                        />
                    ) : (
                        message.content
                    )}
                </p>
                <span className="embed-message-time">
                    {formatMessageTime(message.created_at)}
                </span>
            </div>

            {isUser && (
                <span className="embed-message-avatar embed-message-avatar--user">
                    <UserRound className="size-4" />
                </span>
            )}
        </article>
    );
}

function EmbedComposer({
    courses,
    isProcessing,
    onCourseChange,
    onQuestionChange,
    onSubmit,
    question,
    selectedCourseId,
}: {
    courses: CourseResponse[];
    isProcessing: boolean;
    onCourseChange: (courseId: string) => void;
    onQuestionChange: (question: string) => void;
    onSubmit: (event: FormEvent<HTMLFormElement>) => void;
    question: string;
    selectedCourseId: string;
}) {
    const canSubmit =
        !isProcessing &&
        selectedCourseId.length > 0 &&
        question.trim().length > 0;

    function handleQuestionKeyDown(
        event: KeyboardEvent<HTMLTextAreaElement>,
    ): void {
        if (
            event.key !== 'Enter' ||
            event.shiftKey ||
            event.nativeEvent.isComposing
        ) {
            return;
        }

        event.preventDefault();

        if (canSubmit) {
            event.currentTarget.form?.requestSubmit();
        }
    }

    return (
        <form className="embed-composer" onSubmit={onSubmit}>
            <div className="embed-course-row">
                <BookOpen className="size-4" />
                <span>Mata Kuliah:</span>
                <select
                    aria-label="Mata kuliah"
                    disabled={courses.length === 0}
                    onChange={(event) => onCourseChange(event.target.value)}
                    value={selectedCourseId}
                >
                    <option value="" disabled>
                        Pilih mata kuliah
                    </option>
                    {courses.map((course) => (
                        <option key={course.id} value={String(course.id)}>
                            {course.title}
                        </option>
                    ))}
                </select>
            </div>

            <div className="embed-composer-divider" />

            <div className="embed-question-row">
                <Sparkles className="size-4" />
                <textarea
                    aria-label="Pertanyaan"
                    onChange={(event) => onQuestionChange(event.target.value)}
                    onKeyDown={handleQuestionKeyDown}
                    placeholder="Tulis pertanyaanmu disini..."
                    value={question}
                />
            </div>

            <button
                aria-label="Kirim pertanyaan"
                className="embed-send-button"
                disabled={!canSubmit}
                type="submit"
            >
                {isProcessing ? (
                    <Spinner className="size-4" />
                ) : (
                    <Send className="size-4" />
                )}
            </button>
        </form>
    );
}

function EmbedConfirmModal({
    message,
    onCancel,
    onConfirm,
}: {
    message: string;
    onCancel: () => void;
    onConfirm: () => void;
}) {
    return (
        <div className="embed-modal-backdrop" onClick={onCancel} role="dialog">
            <div
                className="embed-modal"
                onClick={(e) => e.stopPropagation()}
            >
                <p className="embed-modal-title">Konfirmasi</p>
                <p className="embed-modal-body">{message}</p>
                <div className="embed-modal-actions">
                    <button
                        className="embed-modal-btn embed-modal-btn--cancel"
                        onClick={onCancel}
                        type="button"
                    >
                        Batal
                    </button>
                    <button
                        className="embed-modal-btn embed-modal-btn--danger"
                        onClick={onConfirm}
                        type="button"
                    >
                        Hapus
                    </button>
                </div>
            </div>
        </div>
    );
}

function EmbedHistory({
    activeSessionId,
    chatSessions,
    isProcessing,
    onDeleteSession,
    onOpenSession,
}: {
    activeSessionId?: string | null;
    chatSessions: ChatSessionResponse[];
    isProcessing: boolean;
    onDeleteSession: (sessionId: string) => void;
    onOpenSession: (sessionId: string) => void;
}) {
    return (
        <section className="embed-history">
            {chatSessions.length === 0 ? (
                <p className="embed-empty-copy">Belum ada riwayat chat.</p>
            ) : (
                chatSessions.map((session) => (
                    <button
                        className="embed-history-item"
                        data-active={session.uuid_id === activeSessionId}
                        key={session.uuid_id}
                        onClick={() => onOpenSession(session.uuid_id)}
                        type="button"
                    >
                        <span>
                            <strong>{session.title}</strong>
                            <small>
                                {formatMessageTime(
                                    session.last_message_at ??
                                        session.updated_at ??
                                        session.created_at,
                                )}
                            </small>
                        </span>

                        <span
                            aria-disabled={isProcessing}
                            aria-label="Hapus sesi"
                            className="embed-history-delete"
                            onClick={(event) => {
                                event.stopPropagation();
                                onDeleteSession(session.uuid_id);
                            }}
                            role="button"
                            tabIndex={0}
                        >
                            <Trash2 className="size-4" />
                        </span>
                    </button>
                ))
            )}
        </section>
    );
}

export default function Embed() {
    const [config, setConfig] = useState<EmbedConfig>(() =>
        parseEmbedConfig(
            typeof window !== 'undefined'
                ? new URLSearchParams(window.location.search)
                : new URLSearchParams(),
        ),
    );
    const [token, setToken] = useState<string | null>(null);
    const [isAuthorized, setIsAuthorized] = useState(false);
    const [courses, setCourses] = useState<CourseResponse[]>([]);
    const [chatSessions, setChatSessions] = useState<ChatSessionResponse[]>([]);
    const [sessionId, setSessionId] = useState<string | null>(null);
    const [messages, setMessages] = useState<ChatMessageResponse[]>([]);
    const [optimisticMessages, setOptimisticMessages] = useState<
        ChatMessageResponse[]
    >([]);
    const [selectedCourseId, setSelectedCourseId] = useState(
        config.courseId ?? '',
    );
    const [question, setQuestion] = useState('');
    const [isProcessing, setIsProcessing] = useState(false);
    const [pendingDeleteId, setPendingDeleteId] = useState<string | null>(null);
    const [isLoading, setIsLoading] = useState(true);
    const [isLoadingHistory, setIsLoadingHistory] = useState(false);
    const [backendMessage, setBackendMessage] = useState<string | undefined>();
    const [activeTab, setActiveTab] = useState<'chat' | 'history'>('chat');
    const [animatedAssistantMessageId, setAnimatedAssistantMessageId] =
        useState<string | undefined>();
    const messagesEndRef = useRef<HTMLDivElement>(null);

    const allMessages = useMemo(
        () => appendUniqueMessages(messages, optimisticMessages),
        [messages, optimisticMessages],
    );

    const rootStyle = {
        '--embed-bg': config.background,
        '--embed-border': config.border,
        '--embed-danger': config.danger,
        '--embed-muted': config.textSecondary,
        '--embed-primary': config.primary,
        '--embed-primary-soft': config.primaryGradient,
        '--embed-radius': config.radius,
        '--embed-surface': config.backgroundProfile,
        '--embed-text': config.textPrimary,
    } as CSSProperties;

    useEffect(() => {
        const params = new URLSearchParams(window.location.search);
        const parsedConfig = parseEmbedConfig(params);
        const parsedSessionId = params.get('session_id');

        setConfig(parsedConfig);
        setSelectedCourseId(parsedConfig.courseId ?? '');

        void (async () => {
            try {
                const resolvedToken = await resolveEmbedToken(params);

                if (!resolvedToken) {
                    setBackendMessage('Token iframe tidak tersedia.');

                    return;
                }

                const { token, user } = resolvedToken;

                setToken(token);
                setIsAuthorized(true);

                if (user?.name && !params.get('user_name')) {
                    setConfig((prev: EmbedConfig) => ({ ...prev, userName: user.name as string }));
                }

                const [coursesResult, sessionsResult] = await Promise.all([
                    listCoursesEmbed(token),
                    listChatSessionsEmbed(
                        token,
                        parsedConfig.courseId ?? undefined,
                    ),
                ]);

                const loadedCourses = coursesResult.data;
                setCourses(loadedCourses);
                setChatSessions(sessionsResult.data);

                if (!parsedConfig.courseId && loadedCourses[0] !== undefined) {
                    setSelectedCourseId(String(loadedCourses[0].id));
                }

                if (parsedSessionId) {
                    setSessionId(parsedSessionId);
                    const historyResult = await getChatHistoryEmbed(
                        parsedSessionId,
                        token,
                    );
                    setMessages(historyResult.data);
                }
            } catch (err) {
                setBackendMessage(
                    err instanceof Error ? err.message : 'Gagal memuat data.',
                );
            } finally {
                setIsLoading(false);
            }
        })();
    }, []);

    useEffect(() => {
        if (typeof window === 'undefined') {
            return;
        }

        const params = new URLSearchParams(window.location.search);

        if (sessionId) {
            params.set('session_id', sessionId);
        } else {
            params.delete('session_id');
        }

        history.replaceState(null, '', `${window.location.pathname}?${params.toString()}`);
    }, [sessionId]);

    useEffect(() => {
        const frame = window.requestAnimationFrame(() => {
            messagesEndRef.current?.scrollIntoView({ block: 'end' });
        });

        return () => window.cancelAnimationFrame(frame);
    }, [allMessages, isProcessing]);

    async function handleQuestionSubmit(
        event: FormEvent<HTMLFormElement>,
    ): Promise<void> {
        event.preventDefault();

        if (!token) {
            return;
        }

        const trimmedQuestion = question.trim();

        if (!selectedCourseId || !trimmedQuestion) {
            return;
        }

        setBackendMessage(undefined);
        setIsProcessing(true);

        const userMessage = createLocalUserMessage(trimmedQuestion);
        setOptimisticMessages((prev) => [...prev, userMessage]);
        setQuestion('');

        try {
            let currentSessionId = sessionId;

            if (!currentSessionId) {
                const session = await createChatSessionEmbed(
                    { course_id: selectedCourseId, title: trimmedQuestion },
                    token,
                );
                currentSessionId = session.uuid_id;
                setSessionId(currentSessionId);
                setChatSessions((prev) => [session, ...prev]);
            }

            const assistantMessage = await sendRestChatMessage(
                currentSessionId,
                trimmedQuestion,
                token,
            );

            setAnimatedAssistantMessageId(assistantMessage.uuid_id);
            setMessages((prev) => [...prev, userMessage, assistantMessage]);
            setOptimisticMessages([]);
        } catch (err) {
            setOptimisticMessages([]);
            setQuestion(trimmedQuestion);
            setBackendMessage(
                err instanceof Error ? err.message : 'Gagal mengirim pesan.',
            );
        } finally {
            setIsProcessing(false);
        }
    }

    function handleNewSession(): void {
        setActiveTab('chat');
        setSessionId(null);
        setMessages([]);
        setOptimisticMessages([]);
        setQuestion('');
    }

    async function handleOpenSession(targetSessionId: string): Promise<void> {
        if (!token) {
            return;
        }

        setActiveTab('chat');
        setSessionId(targetSessionId);
        setMessages([]);
        setOptimisticMessages([]);
        setIsLoadingHistory(true);

        try {
            const result = await getChatHistoryEmbed(targetSessionId, token);
            setMessages(result.data);
        } catch (err) {
            setBackendMessage(
                err instanceof Error ? err.message : 'Gagal memuat riwayat.',
            );
        } finally {
            setIsLoadingHistory(false);
        }
    }

    function handleDeleteSession(targetSessionId: string): void {
        if (!token) {
            return;
        }

        setPendingDeleteId(targetSessionId);
    }

    async function handleConfirmDelete(): Promise<void> {
        if (!token || !pendingDeleteId) {
            return;
        }

        const targetId = pendingDeleteId;
        setPendingDeleteId(null);

        try {
            await deleteChatSessionEmbed(targetId, token);
            setChatSessions((prev) =>
                prev.filter((s) => s.uuid_id !== targetId),
            );

            if (targetId === sessionId) {
                handleNewSession();
            }
        } catch (err) {
            setBackendMessage(
                err instanceof Error ? err.message : 'Gagal menghapus sesi.',
            );
        }
    }

    function handleClose(): void {
        window.parent?.postMessage({ type: 'sevima-raghub:close' }, '*');
    }

    return (
        <>
            <Head title={config.title} />

            <main className="embed-shell" style={rootStyle}>
                {pendingDeleteId && (
                    <EmbedConfirmModal
                        message="Sesi chat ini akan dihapus permanen."
                        onCancel={() => setPendingDeleteId(null)}
                        onConfirm={() => { void handleConfirmDelete(); }}
                    />
                )}
                <header className="embed-toolbar">
                    <div className="embed-tabs" role="tablist">
                        <button
                            aria-selected={activeTab === 'chat'}
                            onClick={() => setActiveTab('chat')}
                            role="tab"
                            type="button"
                        >
                            Chat
                        </button>
                        <button
                            aria-selected={activeTab === 'history'}
                            onClick={() => setActiveTab('history')}
                            role="tab"
                            type="button"
                        >
                            Riwayat
                        </button>
                    </div>

                    <div className="embed-actions">
                        <button
                            aria-label="Sesi baru"
                            className="embed-icon-button embed-icon-button--primary"
                            onClick={handleNewSession}
                            type="button"
                        >
                            <Plus className="size-6" />
                        </button>
                        <button
                            aria-label="Tutup iframe"
                            className="embed-icon-button"
                            onClick={handleClose}
                            type="button"
                        >
                            <X className="size-6" />
                        </button>
                    </div>
                </header>

                {isLoading ? (
                    <section className="embed-chat">
                        <div className="embed-empty-state">
                            <Spinner className="size-6" />
                        </div>
                    </section>
                ) : activeTab === 'history' ? (
                    <EmbedHistory
                        activeSessionId={sessionId}
                        chatSessions={chatSessions}
                        isProcessing={isProcessing}
                        onDeleteSession={(id) => {
                            void handleDeleteSession(id);
                        }}
                        onOpenSession={(id) => {
                            void handleOpenSession(id);
                        }}
                    />
                ) : (
                    <section
                        className={
                            sessionId
                                ? 'embed-chat embed-chat--session'
                                : 'embed-chat'
                        }
                    >
                        {!isAuthorized ? (
                            <div className="embed-empty-state">
                                <span className="embed-empty-icon">
                                    <MessageSquare className="size-7" />
                                </span>
                                <h1>{config.title}</h1>
                                <p>{backendMessage}</p>
                            </div>
                        ) : sessionId ? (
                            <div className="embed-thread">
                                {backendMessage && (
                                    <p className="embed-status">
                                        {backendMessage}
                                    </p>
                                )}

                                {isLoadingHistory ? (
                                    <div className="embed-empty-state">
                                        <Spinner className="size-6" />
                                    </div>
                                ) : (
                                    <>
                                        {allMessages.map((message) => (
                                            <EmbedMessage
                                                key={message.uuid_id}
                                                message={message}
                                                shouldAnimateTyping={
                                                    message.uuid_id ===
                                                    animatedAssistantMessageId
                                                }
                                            />
                                        ))}

                                        {isProcessing && (
                                            <article className="embed-message embed-message--assistant">
                                                <span className="embed-message-avatar embed-message-avatar--assistant">
                                                    <Bot className="size-4" />
                                                </span>
                                                <div className="embed-message-bubble embed-message-bubble--typing">
                                                    <LoadingIndicator
                                                        label="Thinking"
                                                        showSpinner={false}
                                                    />
                                                </div>
                                            </article>
                                        )}

                                        <div ref={messagesEndRef} />
                                    </>
                                )}
                            </div>
                        ) : (
                            <div className="embed-empty-state">
                                <span className="embed-empty-icon">
                                    <MessageSquare className="size-7" />
                                </span>
                                <h1>Good Morning, {config.userName}</h1>
                                <p>{config.subtitle}</p>
                                {backendMessage && (
                                    <p className="embed-status">
                                        {backendMessage}
                                    </p>
                                )}
                            </div>
                        )}

                        {isAuthorized && (
                            <div className="embed-composer-dock">
                                <EmbedComposer
                                    courses={courses}
                                    isProcessing={isProcessing}
                                    onCourseChange={setSelectedCourseId}
                                    onQuestionChange={setQuestion}
                                    onSubmit={(e) => {
                                        void handleQuestionSubmit(e);
                                    }}
                                    question={question}
                                    selectedCourseId={selectedCourseId}
                                />
                            </div>
                        )}
                    </section>
                )}
            </main>
        </>
    );
}
