'use client';

/**
 * Chat input using the production-ready TipTap IMEditor.
 * Supports @mentions, file uploads, emoji picker, and rich message AST.
 */

import {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef} from 'react';
import type {IMEditorHandle} from './editor/IMEditor';
import {IMEditor} from './editor/IMEditor';
import {useRoom} from '@/contexts';
import type {EditorNode, MessageAST} from './editor/types';

export interface MessageInputProps {
    roomName: string;
    onSend: (content: string, attachmentIds?: string[]) => void;
    replyingTo?: { id: string; display_name?: string; content: string } | null;
    onCancelReply?: () => void;
}

export interface MessageInputHandle {
    focus: () => void;
    clearContent: () => void;
    getContent: () => string;
    insertMention: (type: string, id: string, label: string) => void;
    getAttachmentIds: () => string[];
}

// Slash commands available in the editor
const SLASH_COMMANDS = [
    {id: 'ai', label: '/ai', description: 'Ask AI a question', type: 'command' as const},
    {
        id: 'remind',
        label: '/remind',
        description: 'Set a reminder (e.g. /remind 10m Check CI)',
        type: 'command' as const
    },
    {id: 'poll', label: '/poll', description: 'Create a poll (e.g. /poll "Question?" A B C)', type: 'command' as const},
    {id: 'code-review', label: '/code-review', description: 'Request AI code review', type: 'command' as const},
];

// Special mention items — @here (online), @channel (all members)
const SPECIAL_MENTIONS = [
    {
        id: '__here__',
        label: 'here',
        description: 'Notify online members',
        type: 'special_here' as const,
    },
    {
        id: '__channel__',
        label: 'channel',
        description: 'Notify all members',
        type: 'special_channel' as const,
    },
];

/** Serialize tiptap AST to backend-parseable string format. */
function serializeMessageAst(ast: MessageAST): string {
    return ast.content.map(serializeNode).join('\n');
}

function serializeNode(node: EditorNode): string {
    if (node.type === 'text') return node.text;
    if (node.type === 'mention') return `@[${node.attrs.type}:${node.attrs.id}:${node.attrs.label}]`;
    if (node.type === 'hardBreak') return '\n';
    if (node.type === 'file') return ''; // files are sent separately via attachmentIds
    if (node.type === 'emoji') return `[emoji:${node.attrs.name}]`;
    // Recurse into container nodes (paragraph, bulletList, etc.)
    const children = (node as any).content as EditorNode[] | undefined;
    if (children) return children.map(serializeNode).join('');
    return '';
}

export const MessageInput = forwardRef<MessageInputHandle, MessageInputProps>(function MessageInput(
    {roomName, onSend, replyingTo, onCancelReply},
    ref,
) {
    const {members, activeRoomId, roomAiConfigs, projectRepos, wsClient} = useRoom();

    // Ref passed to the inner IMEditor
    const innerEditorRef = useRef<IMEditorHandle | null>(null);

    // Expose a subset of IMEditorHandle (plus getAttachmentIds) as MessageInputHandle
    useImperativeHandle(ref, () => ({
        focus: () => innerEditorRef.current?.focus(),
        clearContent: () => innerEditorRef.current?.clearContent(),
        getContent: () => innerEditorRef.current?.getContent() ?? '',
        insertMention: (type: string, id: string, label: string) =>
            innerEditorRef.current?.insertMention(type, id, label),
        getAttachmentIds: () => innerEditorRef.current?.getAttachmentIds() ?? [],
    }), []);

    // Typing indicator: debounce start/stop
    const typingStopTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

    const sendTypingStart = useCallback(() => {
        if (!wsClient || !activeRoomId) {
            console.debug('[MessageInput] sendTypingStart skipped: wsClient=', !!wsClient, 'activeRoomId=', activeRoomId);
            return;
        }
        console.debug('[MessageInput] sendTypingStart room:', activeRoomId);
        if (typingStopTimerRef.current) {
            clearTimeout(typingStopTimerRef.current);
            typingStopTimerRef.current = null;
        }
        wsClient.sendTyping(activeRoomId, 'start');
    }, [wsClient, activeRoomId]);

    const sendTypingStop = useCallback(() => {
        if (!wsClient || !activeRoomId) return;
        if (typingStopTimerRef.current) {
            clearTimeout(typingStopTimerRef.current);
            typingStopTimerRef.current = null;
        }
        wsClient.sendTyping(activeRoomId, 'stop');
    }, [wsClient, activeRoomId]);

    const handleEditorUpdate = useCallback((text: string) => {
        if (!text.trim()) {
            // Ignore empty updates (e.g. TipTap fires onUpdate("") on init).
            // Only stop typing on explicit clear or send.
            return;
        }
        console.debug('[MessageInput] handleEditorUpdate text_len:', text.length, 'ws:', !!wsClient, 'room:', activeRoomId);
        sendTypingStart();
        // Auto-stop after 1.5s of inactivity
        if (typingStopTimerRef.current) clearTimeout(typingStopTimerRef.current);
        typingStopTimerRef.current = setTimeout(sendTypingStop, 1500);
    }, [sendTypingStart, sendTypingStop]);

    // Stop typing on send or clear
    useEffect(() => {
        return () => {
            if (typingStopTimerRef.current) clearTimeout(typingStopTimerRef.current);
        };
    }, []);

    // Transform room data into MentionItems — memoized to prevent IMEditor re-creation
    const mentionItems = useMemo(() => ({
        users: members.map((m) => ({
            id: m.user,
            label: m.user_info?.username ?? m.user,
            type: 'user' as const,
            avatar: m.user_info?.avatar_url ?? undefined,
        })),
        channels: [] as { id: string; label: string; type: 'channel'; avatar?: string }[],
        ai: roomAiConfigs.map((cfg) => ({
            id: cfg.model,
            // Fallback: try modelName, then short model ID (no provider prefix), then 'AI'
            label: cfg.modelName
                || cfg.model?.split('/').pop()
                || 'AI',
            type: 'ai' as const,
        })),
        repos: projectRepos.map((r) => ({
            id: r.repo_name,
            label: r.repo_name,
            type: 'repo' as const,
            description: r.description ?? undefined,
        })),
        commands: SLASH_COMMANDS,
        specialMentions: SPECIAL_MENTIONS,
    }), [members, roomAiConfigs, projectRepos]);

    // File upload handler — POST to /rooms/{room_id}/upload
    const handleUploadFile = async (file: File): Promise<{ id: string; url: string }> => {
        if (!activeRoomId) throw new Error('No active room');
        const formData = new FormData();
        formData.append('file', file);
        const baseUrl = import.meta.env.VITE_API_BASE_URL ?? window.location.origin;
        const res = await fetch(`${baseUrl}/rooms/${activeRoomId}/upload`, {method: 'POST', body: formData});
        if (!res.ok) throw new Error('Upload failed');
        return res.json();
    };

    // onSend: serialize AST to backend-parseable format
    const handleSend = (_text: string, ast: MessageAST) => {
        sendTypingStop();
        const serialized = serializeMessageAst(ast);
        onSend(serialized);
    };

    return (
        <IMEditor
            ref={innerEditorRef}
            replyingTo={replyingTo}
            onCancelReply={onCancelReply}
            onSend={handleSend}
            mentionItems={mentionItems}
            onUploadFile={handleUploadFile}
            placeholder={`Message #${roomName}`}
            onUpdate={handleEditorUpdate}
        />
    );
});