import { Head, Link, router } from '@inertiajs/react';
import { Menu, MessageSquarePlus, Sparkles, Trash2 } from 'lucide-react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { ChatComposer } from '@/components/agent/chat-composer';
import { ChatMessages } from '@/components/agent/chat-messages';
import { Button } from '@/components/ui/button';
import {
    Sheet,
    SheetContent,
    SheetDescription,
    SheetHeader,
    SheetTitle,
    SheetTrigger,
} from '@/components/ui/sheet';
import {
    dedupeArtifacts,
    extractArtifacts,
    streamAgentMessage,
} from '@/lib/agent-stream';
import type { ChatMessage } from '@/lib/agent-stream';
import { cn } from '@/lib/utils';
import { dashboard } from '@/routes';
import agentRoutes from '@/routes/agent';

type ConversationSummary = {
    id: string;
    title: string;
    updated_at?: string;
};

type Props = {
    company: { id: number; name: string; base_currency: string } | null;
    conversations: ConversationSummary[];
    activeConversation: ConversationSummary | null;
    messages: Array<{
        id: string;
        role: string;
        content: string;
        tool_results?: unknown;
        created_at?: string;
    }>;
};

const EXAMPLES = [
    'Show revenue vs expenses for the last 5 years',
    'Analyze my growth score',
    'Analyze the growth score for a client',
];

function artifactsFromToolResults(
    toolResults: unknown,
): ReturnType<typeof extractArtifacts> {
    if (!Array.isArray(toolResults)) {
        return [];
    }

    return dedupeArtifacts(
        toolResults.flatMap((result) => extractArtifacts(result)),
    );
}

function localMessageId(prefix: string): string {
    return `${prefix}-${crypto.randomUUID()}`;
}

function ConversationList({
    conversations,
    conversationId,
    onNew,
    onSelect,
    onDelete,
    deletingDisabled,
}: {
    conversations: ConversationSummary[];
    conversationId: string | null;
    onNew: () => void;
    onSelect?: () => void;
    onDelete: (conversation: ConversationSummary) => void;
    deletingDisabled?: boolean;
}) {
    return (
        <>
            <div className="flex items-center justify-between gap-2 border-b border-border p-3">
                <div className="text-sm font-medium">Conversations</div>
                <Button
                    size="icon"
                    variant="ghost"
                    onClick={onNew}
                    aria-label="Start a new conversation"
                    title="New chat"
                >
                    <MessageSquarePlus className="size-4" />
                </Button>
            </div>
            <div className="flex-1 overflow-y-auto p-2">
                {conversations.length === 0 ? (
                    <p className="px-2 py-4 text-xs text-muted-foreground">
                        No conversations yet.
                    </p>
                ) : (
                    conversations.map((conversation) => (
                        <div
                            key={conversation.id}
                            className={cn(
                                'group mb-1 flex items-center gap-1 rounded-lg hover:bg-muted',
                                conversation.id === conversationId &&
                                    'bg-muted font-medium',
                            )}
                        >
                            <Link
                                href={agentRoutes.show.url(conversation.id)}
                                prefetch
                                onClick={onSelect}
                                className="min-w-0 flex-1 px-3 py-2 text-sm"
                            >
                                <div className="truncate">
                                    {conversation.title}
                                </div>
                            </Link>
                            <Button
                                size="icon"
                                variant="ghost"
                                className="mr-1 size-8 shrink-0 opacity-0 group-hover:opacity-100 focus-visible:opacity-100"
                                onClick={() => onDelete(conversation)}
                                disabled={deletingDisabled}
                                aria-label={`Delete ${conversation.title}`}
                                title="Delete chat"
                            >
                                <Trash2 className="size-4" />
                            </Button>
                        </div>
                    ))
                )}
            </div>
        </>
    );
}

export default function AgentIndex({
    company,
    conversations,
    activeConversation,
    messages: initialMessages,
}: Props) {
    const [input, setInput] = useState(() => {
        if (typeof window === 'undefined') {
            return '';
        }

        return new URLSearchParams(window.location.search).get('prompt') ?? '';
    });
    const [streaming, setStreaming] = useState(false);
    const [error, setError] = useState<string | null>(null);
    const [localMessages, setLocalMessages] = useState<ChatMessage[]>([]);
    const abortControllerRef = useRef<AbortController | null>(null);
    const [mobileConversationsOpen, setMobileConversationsOpen] =
        useState(false);
    // Set right before navigating to a freshly-created conversation so the
    // reset effect below doesn't clobber the just-streamed local messages
    // with the (stale, empty) `messages` prop that wasn't re-fetched.
    const skipNextResetForConversationId = useRef<string | null>(null);

    useEffect(() => {
        if (skipNextResetForConversationId.current === activeConversation?.id) {
            skipNextResetForConversationId.current = null;

            return;
        }

        queueMicrotask(() => {
            setLocalMessages(
                initialMessages.map((message) => ({
                    id: message.id,
                    role: message.role,
                    content: message.content,
                    artifacts: artifactsFromToolResults(message.tool_results),
                })),
            );
        });
    }, [initialMessages, activeConversation?.id]);

    const conversationId = activeConversation?.id ?? null;

    const title = useMemo(
        () => activeConversation?.title ?? 'AI Agent',
        [activeConversation?.title],
    );

    async function sendMessage(text: string) {
        const trimmed = text.trim();

        if (!trimmed || streaming) {
            return;
        }

        setError(null);
        setInput('');
        setStreaming(true);
        const abortController = new AbortController();
        abortControllerRef.current = abortController;

        const userMessage: ChatMessage = {
            id: localMessageId('local-user'),
            role: 'user',
            content: trimmed,
        };
        const assistantId = localMessageId('local-assistant');

        setLocalMessages((current) => [
            ...current,
            userMessage,
            { id: assistantId, role: 'assistant', content: '', artifacts: [] },
        ]);

        let resolvedConversationId = conversationId;

        try {
            await streamAgentMessage({
                message: trimmed,
                conversationId,
                onText: (_delta, full) => {
                    setLocalMessages((current) =>
                        current.map((message) =>
                            message.id === assistantId
                                ? { ...message, content: full }
                                : message,
                        ),
                    );
                },
                onArtifacts: (artifacts) => {
                    setLocalMessages((current) =>
                        current.map((message) =>
                            message.id === assistantId
                                ? { ...message, artifacts }
                                : message,
                        ),
                    );
                },
                onError: (message) => setError(message),
                onConversationId: (id) => {
                    resolvedConversationId = id;
                },
                signal: abortController.signal,
            });

            if (!conversationId && resolvedConversationId) {
                // Keep the just-streamed local messages (already rendered
                // correctly) instead of re-fetching them from the server,
                // which risked replacing correct artifacts with a
                // reconstruction mismatch right after the chat completed.
                skipNextResetForConversationId.current = resolvedConversationId;
                router.visit(agentRoutes.show.url(resolvedConversationId), {
                    preserveScroll: true,
                    preserveState: true,
                    only: ['conversations', 'activeConversation'],
                });
            } else {
                router.reload({
                    only: ['conversations', 'activeConversation'],
                });
            }
        } catch (err) {
            if (err instanceof DOMException && err.name === 'AbortError') {
                setLocalMessages((current) =>
                    current.filter((message) => message.id !== assistantId),
                );
            } else {
                setError(
                    err instanceof Error
                        ? err.message
                        : 'Something went wrong. Please try again.',
                );
                setLocalMessages((current) =>
                    current
                        .filter((message) => message.id !== assistantId)
                        .map((message) =>
                            message.id === userMessage.id
                                ? { ...message, failed: true }
                                : message,
                        ),
                );
            }
        } finally {
            abortControllerRef.current = null;
            setStreaming(false);
        }
    }

    function cancelStreaming() {
        abortControllerRef.current?.abort();
    }

    function deleteConversation(conversation: ConversationSummary) {
        if (
            !window.confirm(
                `Delete "${conversation.title}"? This cannot be undone.`,
            )
        ) {
            return;
        }

        router.delete(agentRoutes.conversations.destroy.url(conversation.id));
    }

    return (
        <>
            <Head title={title} />
            <div className="flex min-h-0 flex-1 overflow-hidden rounded-xl border border-sidebar-border/70 bg-background dark:border-sidebar-border">
                <aside className="hidden min-h-0 w-72 shrink-0 flex-col border-r border-border md:flex">
                    <ConversationList
                        conversations={conversations}
                        conversationId={conversationId}
                        onNew={() => router.visit(agentRoutes.index.url())}
                        onDelete={deleteConversation}
                        deletingDisabled={streaming}
                    />
                    {company && (
                        <div className="border-t border-border p-3 text-xs text-muted-foreground">
                            Analyzing {company.name} ({company.base_currency})
                        </div>
                    )}
                </aside>

                <section className="flex min-w-0 flex-1 flex-col">
                    <div className="flex items-center gap-2 border-b border-border px-4 py-3">
                        <Sheet
                            open={mobileConversationsOpen}
                            onOpenChange={setMobileConversationsOpen}
                        >
                            <SheetTrigger asChild>
                                <Button
                                    size="icon"
                                    variant="ghost"
                                    className="md:hidden"
                                    aria-label="Open conversations"
                                >
                                    <Menu className="size-4" />
                                </Button>
                            </SheetTrigger>
                            <SheetContent
                                side="left"
                                className="w-80 max-w-[85vw]"
                            >
                                <SheetHeader>
                                    <SheetTitle>Conversations</SheetTitle>
                                    <SheetDescription>
                                        Switch between your financial analyses.
                                    </SheetDescription>
                                </SheetHeader>
                                <div className="flex min-h-0 flex-1 flex-col">
                                    <ConversationList
                                        conversations={conversations}
                                        conversationId={conversationId}
                                        onNew={() => {
                                            setMobileConversationsOpen(false);
                                            router.visit(
                                                agentRoutes.index.url(),
                                            );
                                        }}
                                        onSelect={() =>
                                            setMobileConversationsOpen(false)
                                        }
                                        onDelete={(conversation) => {
                                            setMobileConversationsOpen(false);
                                            deleteConversation(conversation);
                                        }}
                                        deletingDisabled={streaming}
                                    />
                                </div>
                            </SheetContent>
                        </Sheet>
                        <Sparkles className="size-4 text-teal-700 dark:text-teal-400" />
                        <div>
                            <div className="text-sm font-medium">{title}</div>
                            <div className="text-xs text-muted-foreground">
                                Financial analysis agent
                            </div>
                        </div>
                    </div>

                    <div className="min-h-0 flex-1 overflow-hidden">
                        {localMessages.length === 0 ? (
                            <div className="mx-auto flex h-full max-w-2xl flex-col items-center justify-center gap-6 px-4 py-16 text-center">
                                <div>
                                    <h1 className="text-2xl font-semibold tracking-tight">
                                        Ask Monily about the books
                                    </h1>
                                    <p className="mt-2 text-sm text-muted-foreground">
                                        Query income, expenses, balance sheet,
                                        cash flow, and forecasts from seeded
                                        financial history.
                                    </p>
                                </div>
                                <div className="flex w-full flex-col gap-2">
                                    {EXAMPLES.map((example) => (
                                        <button
                                            key={example}
                                            type="button"
                                            className="rounded-xl border border-border px-4 py-3 text-left text-sm hover:bg-muted"
                                            onClick={() => sendMessage(example)}
                                            disabled={streaming || !company}
                                        >
                                            {example}
                                        </button>
                                    ))}
                                </div>
                                {!company && (
                                    <p className="text-sm text-destructive">
                                        No company linked. Run{' '}
                                        <code>php artisan db:seed</code> first.
                                    </p>
                                )}
                            </div>
                        ) : (
                            <ChatMessages
                                messages={localMessages}
                                streaming={streaming}
                                currency={company?.base_currency}
                                onRetry={sendMessage}
                            />
                        )}
                    </div>

                    {error && (
                        <div className="px-4 pb-2 text-center text-sm text-destructive">
                            {error}
                        </div>
                    )}

                    <ChatComposer
                        value={input}
                        onChange={setInput}
                        onSubmit={() => sendMessage(input)}
                        onCancel={cancelStreaming}
                        streaming={streaming}
                        disabled={!company}
                    />
                </section>
            </div>
        </>
    );
}

AgentIndex.layout = {
    breadcrumbs: [
        { title: 'Dashboard', href: dashboard() },
        { title: 'AI Agent', href: '/agent' },
    ],
};
