import {createPortal, useEffect, useRef, useState, useCallback} from "@wordpress/element";
import {__} from '@wordpress/i18n';
import useClickOutside from "../../../../components/hooks/use-click-outside";
import {handleError} from "../../../helpers/notifications";
import {GetModel} from "../../../rest/models";
import {GetConfig} from "../../../rest/configs";
import Settings from "./_components/settings";
import AddEditConfig from "../../popups/ai-providers/models/add-edit-config";
import ContentEditable from "../../fields/contenteditable";
import {COPILOT_MENU, PROMPTS} from "./_data";
import {GetSetting} from "../../../rest/settings";
import OpenAI from "../../../../utilities/ai-providers/open-ai";
import Gemini from "../../../../utilities/ai-providers/gemini";
import DeepSeek from "../../../../utilities/ai-providers/deep-seek";
import Claude from "../../../../utilities/ai-providers/claude";
import Grok from "../../../../utilities/ai-providers/grok";

const AI_PROVIDER = {
    'open-ai': OpenAI,
    'gemini': Gemini,
    'deepseek': DeepSeek,
    'claude': Claude,
    'grok': Grok,
};

export default function Copilot({text, generated, direction = 'left', disabled, notifications}) {
    // States
    const [loading, setLoading] = useState(0);
    const [loadingToOpen, setLoadingToOpen] = useState(0);
    const [menuOpened, setMenuOpened] = useState(false);
    const [mode, setMode] = useState(false);
    const [generating, setGenerating] = useState(false);
    const [showAISettingsPopup, setShowAISettingsPopup] = useState(false);
    const [showAddConfigPopup, setShowAddConfigPopup] = useState(false);
    // Elements refs
    const menuRef = useRef(null);
    const textareaRef = useRef(null);
    const openerRef = useRef(null);
    const metaBoxRef = useRef(document.getElementById('chatbot-meta-box'));
    const fieldMenuRef = useRef(document.getElementById('lbaic-settings-field-menu'));
    // Position
    const [position, setPosition] = useState({
        top: 0,
        left: 0,
        bottom: 'unset',
    });
    const [aiTextareaPosition, setAITextareaPosition] = useState({
        top: 0,
        left: 0,
        bottom: 'unset',
    });
    // AI provider data
    const [defaultModel, setDefaultModel] = useState(null);
    const selectedModelRef = useRef(null);
    const selectedConfigRef = useRef(null);
    const aiProvider = useRef(null);
    // Data
    const [instructions, setInstructions] = useState('');

    const inCloseTimeout = useRef(null);

    useClickOutside([menuRef, textareaRef, openerRef], () => {
        setMenuOpened(false);
        setMode(false);
    });

    /**
     * Setup position
     */
    const setupPosition = useCallback(() => {
        if (openerRef.current) {
            const rect = openerRef.current.getBoundingClientRect();
            const metaBoxRect = metaBoxRef.current ? metaBoxRef.current.getBoundingClientRect() : null;
            const fieldMenuRect = fieldMenuRef.current ? fieldMenuRef.current.getBoundingClientRect() : null;
            const leftMenuWidth = document.getElementById('adminmenuwrap').offsetWidth;
            const topMenuHeight = document.getElementById('wpadminbar').offsetHeight;
            if (menuOpened) {
                // Menu position
                let top = rect.top - (metaBoxRect ? -2 : (topMenuHeight ? topMenuHeight - 36 : 0)) - (metaBoxRect?.top || 0);
                let left = rect.left - leftMenuWidth - (direction === 'left' ? COPILOT_MENU.width - rect.width + (metaBoxRect ? fieldMenuRect?.left - leftMenuWidth : 0) : (metaBoxRect ? fieldMenuRect?.left - leftMenuWidth : 0));
                let bottom = 'unset';
                if (top + COPILOT_MENU.height + rect.height > window.innerHeight) {
                    top = 'unset';
                    bottom = 0;
                }

                setPosition({
                    top: typeof top === 'number' ? top + 'px' : top,
                    left: typeof left === 'number' ? left + 'px' : left,
                    bottom: typeof bottom === 'number' ? bottom + 'px' : bottom,
                });
            }
            if (mode === 'custom') {
                // Textarea position
                let top = rect.top - (metaBoxRect ? -2 : (topMenuHeight ? topMenuHeight - 36 : 0)) - (metaBoxRect?.top || 0);
                let left = rect.left - leftMenuWidth - (direction === 'left' ? (textareaRef.current?.offsetWidth || 384) - rect.width + (metaBoxRect ? fieldMenuRect?.left - leftMenuWidth : 0) : (metaBoxRect ? fieldMenuRect?.left - leftMenuWidth : 0));
                let bottom = 'unset';
                if (top + (textareaRef.current?.offsetHeight || 0) + rect.height > window.innerHeight) {
                    top = 'unset';
                    bottom = 0;
                }

                setAITextareaPosition({
                    top: typeof top === 'number' ? top + 'px' : top,
                    left: typeof left === 'number' ? left + 'px' : left,
                    bottom: typeof bottom === 'number' ? bottom + 'px' : bottom,
                });
            }
        }
    }, [menuOpened, mode, direction]);

    useEffect(() => {
        // Events
        window.addEventListener('resize', setupPosition);
        window.addEventListener('scroll', setupPosition, true);

        return () => {
            window.removeEventListener('resize', setupPosition);
            window.removeEventListener('scroll', setupPosition, true);
        }
    }, [setupPosition]);

    useEffect(() => {
        if (menuOpened || mode) {
            setupPosition();
        }
    }, [menuOpened, mode, setupPosition]);

    /**
     * Start
     */
    const showTools = () => {
        setupAIClient();
        setMenuOpened(true);
        setMode(false);
        setDefaultModel(null);
    }

    /**
     * Check copilot settings before open textarea
     *
     * @return {Promise<void>}
     */
    const checkSettingsAndOpenMenu = async () => {
        if (loadingToOpen || disabled) {
            return;
        }
        // Close menu or custom generator
        setMenuOpened(false);
        setMode(false);
        // Check settings
        setLoadingToOpen(prev => prev + 1);
        let settingsReady = true;
        // Get the AI model
        if (!await getModel()) {
            settingsReady = false;
        }
        // Get config if the AI model found
        if (settingsReady) {
            if (!await getConfig()) {
                settingsReady = false;
            }
        }
        setLoadingToOpen(prev => prev - 1);
        // Create an AI client
        if (settingsReady) {
            showTools();
        }
    }

    /**
     * Get the AI model
     *
     * @return {Promise<boolean>}
     */
    const getModel = async () => {
        if (selectedModelRef.current?.id) {
            return true;
        }
        setLoadingToOpen(prev => prev + 1);
        let settingsReady = true;
        try {
            const saved = await GetSetting(LimbChatbot.rest.url, LimbChatbot.rest.nonce, 'lbaic.utilities.copilot.ai_model_id');
            if (saved?.value) {
                selectedModelRef.current = await GetModel(LimbChatbot.rest.url, LimbChatbot.rest.nonce, saved?.value);
            }
        } catch (e) {
            handleError(e, notifications?.set, {
                title: __("Copilot default AI model not found.", 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Please try again.", 'limb-chatbot'),
            });
        }
        if (!selectedModelRef.current?.id) {
            setShowAISettingsPopup(true);
            settingsReady = false;
        }
        setLoadingToOpen(prev => prev - 1);

        return settingsReady;
    }

    /**
     * Get config
     *
     * @return {Promise<boolean>}
     */
    const getConfig = async () => {
        if (selectedConfigRef.current?.id) {
            return true;
        }
        // Check the default config for the model AI provider
        setLoadingToOpen(prev => prev + 1);
        let settingsReady = true;
        try {
            const saved = await GetSetting(LimbChatbot.rest.url, LimbChatbot.rest.nonce, 'lbaic.utilities.copilot.config_id');
            if (saved?.value) {
                selectedConfigRef.current = await GetConfig(LimbChatbot.rest.url, LimbChatbot.rest.nonce, saved?.value, {
                    mask: false,
                });
            }
        } catch (e) {
            handleError(e, notifications?.set, {
                title: __("Default AI API key not found.", 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Please try again.", 'limb-chatbot'),
            });
        }
        if (!selectedConfigRef.current?.id) {
            setShowAISettingsPopup(true);
            settingsReady = false;
        }
        setLoadingToOpen(prev => prev - 1);

        return settingsReady;
    }

    /**
     * Settings saved
     *
     * @param {object} model Saved model data
     * @param {object} config Saved config data
     * @return {Promise<void>}
     */
    const settingsSaved = async ({model, config}) => {
        selectedModelRef.current = model;
        selectedConfigRef.current = config;
        setupAIClient();
    }

    /**
     * Config added
     *
     * @param {object} config Config data
     * @return {Promise<void>}
     */
    const configAdded = (config) => {
        selectedConfigRef.current = config;
        showTools();
    }

    /**
     * Create an AI client
     */
    const setupAIClient = async () => {
        setLoading(prev => prev + 1);
        // Create an AI provider instance
        let res = null;
        try {
            res = await new AI_PROVIDER[selectedModelRef.current.ai_provider_id]({
                apiKey: selectedConfigRef.current?.params.api_key,
                model: selectedModelRef.current.name,
            });
        } catch (e) {
            handleError(e, notifications?.set, {
                title: __("Failed to initialize the AI provider.", 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Please check and try again.", 'limb-chatbot'),
            });
        }
        setLoading(prev => prev - 1);

        aiProvider.current = res;
    }

    const updateText = async (type) => {
        if (disabled) {
            return;
        }
        setGenerating(type);
        // Generate text
        const res = await aiProvider.current?.generateContent({
            prompt: PROMPTS[type].before + text + PROMPTS[type].after,
        });
        setGenerating(false);
        // Check response
        if (res.status) {
            // Show generated text
            generated(res.data);
            setMenuOpened(false);
            setMode(false);
        } else {
            handleError({
                message: "Text generation failed.",
            }, notifications?.set, {
                title: __("Failed to generate content.", 'limb-chatbot'),
                description: res.data ? __(res.data, 'limb-chatbot') : __("Please check your connection and try again.", 'limb-chatbot'),
            });
        }
    }

    const openCustomGenerator = () => {
        if (disabled) {
            return;
        }
        setMode('custom');
        setTimeout(() => {
            setMenuOpened(false);
        }, 0);
    }

    const openSettings = () => {
        if (disabled) {
            return;
        }
        setShowAISettingsPopup(true);
        setMenuOpened(false);
    }

    /**
     * Generate text with AI
     */
    const generate = async () => {
        if (instructions.trim()) {
            setGenerating(true);
            // Generate text
            const res = await aiProvider.current?.generateContent({
                // systemInfo: '',
                prompt: PROMPTS.custom.before + instructions + PROMPTS.custom.after,
            });
            setGenerating(false);
            // Check response
            if (res.status) {
                // Show generated text
                generated(res.data);
                setMenuOpened(false);
                setMode(false);
            } else {
                handleError({
                    message: "Text generation failed.",
                }, notifications?.set, {
                    title: __("Failed to generate content.", 'limb-chatbot'),
                    description: res.data ? __(res.data, 'limb-chatbot') : __("Please check your connection and try again.", 'limb-chatbot'),
                });
            }
        }
    }

    /**
     * Keep the menu opened if intend to close it within 400 ms
     */
    const inMouseEnter = () => {
        if (inCloseTimeout.current) {
            clearTimeout(inCloseTimeout.current);
            inCloseTimeout.current = null;
        }
    }

    /**
     * Close menu on mouse leave after 400 ms
     */
    const inMouseLeave = () => {
        inCloseTimeout.current = setTimeout(() => {
            setMenuOpened(false);
        }, 400);
    }

    const isButtonDisabled = loading > 0 || generating || disabled;

    return <>
        <div className='lbaic-settings-copilot'>
            <button ref={openerRef}
                    className={`lbaic-settings-button-reset lbaic-settings-copilot-toggle${loadingToOpen || disabled ? ' lbaic-settings-button-disabled' : ''}`}
                    onClick={checkSettingsAndOpenMenu} tabIndex='-1'>
                <svg className={`lbaic-settings-copilot-toggle-i${loadingToOpen ? ' lbaic-settings-loading-circle' : ''}`}
                     xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
                    <use href={`#lbaic-settings-${loadingToOpen ? 'circle' : 'magic'}`}/>
                </svg>
            </button>
        </div>
        {!disabled && menuOpened && createPortal(
            <div ref={menuRef} className='lbaic-settings-copilot-menu active'
                 style={{
                     '--lbaic-settings-copilot-menu-top': position.top,
                     '--lbaic-settings-copilot-menu-left': position.left,
                     '--lbaic-settings-copilot-menu-bottom': position.bottom,
                 }}
                 onMouseEnter={inMouseEnter}
                 onMouseLeave={inMouseLeave}>
                <button tabIndex='-1' onClick={() => updateText('revise')}
                        className={`lbaic-settings-button lbaic-settings-button-h-30 lbaic-settings-button-copilot lbaic-settings-copilot-menu-action${isButtonDisabled ? ' lbaic-settings-button-disabled' : ''}`}>

                    <span className='lbaic-settings-button-label'>{__("Revise", 'limb-chatbot')}</span>
                    {generating === 'revise' &&
                        <svg
                            className='lbaic-settings-button-i lbaic-settings-loading-circle lbaic-settings-button-loading-circle'
                            xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
                            <use href='#lbaic-settings-circle'/>
                        </svg>}
                </button>
                <button tabIndex='-1' onClick={() => updateText('shorter')}
                        className={`lbaic-settings-button lbaic-settings-button-h-30 lbaic-settings-button-copilot lbaic-settings-copilot-menu-action${isButtonDisabled ? ' lbaic-settings-button-disabled' : ''}`}>
                    <span className='lbaic-settings-button-label'>{__("Shorter", 'limb-chatbot')}</span>
                    {generating === 'shorter' &&
                        <svg className='lbaic-settings-button-i lbaic-settings-loading-circle lbaic-settings-button-loading-circle'
                             xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
                            <use href='#lbaic-settings-circle'/>
                        </svg>}
                </button>
                <button tabIndex='-1' onClick={() => updateText('longer')}
                        className={`lbaic-settings-button lbaic-settings-button-h-30 lbaic-settings-button-copilot lbaic-settings-copilot-menu-action${isButtonDisabled ? ' lbaic-settings-button-disabled' : ''}`}>
                    <span className='lbaic-settings-button-label'>{__("Longer", 'limb-chatbot')}</span>
                    {generating === 'longer' &&
                        <svg className='lbaic-settings-button-i lbaic-settings-loading-circle lbaic-settings-button-loading-circle'
                             xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
                            <use href='#lbaic-settings-circle'/>
                        </svg>}
                </button>
                <button tabIndex='-1' onClick={openCustomGenerator}
                        className={`lbaic-settings-button lbaic-settings-button-h-30 lbaic-settings-button-copilot lbaic-settings-copilot-menu-action${isButtonDisabled ? ' lbaic-settings-button-disabled' : ''}`}>
                    <span className='lbaic-settings-button-label'>{__("Custom", 'limb-chatbot')}</span>
                </button>
                <button tabIndex='-1' onClick={openSettings}
                        className={`lbaic-settings-button lbaic-settings-button-h-30 lbaic-settings-button-copilot lbaic-settings-copilot-menu-settings${isButtonDisabled ? ' lbaic-settings-button-disabled' : ''}`}>
                    <div className="lbaic-settings-button-in">
                        <svg className='lbaic-settings-button-label-i'
                             xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
                            <use href='#lbaic-settings-advanced'/>
                        </svg>
                        <span className='lbaic-settings-button-label'>{__("Settings", 'limb-chatbot')}</span>
                    </div>
                </button>
            </div>
            , document.getElementById('lbaic-settings-field-menu'))}
        {!disabled && mode === 'custom' && createPortal(
            <div ref={textareaRef} className='lbaic-settings-copilot-mode-custom active'
                 style={{
                     '--lbaic-settings-ai-textarea-top': aiTextareaPosition.top,
                     '--lbaic-settings-ai-textarea-left': aiTextareaPosition.left,
                     '--lbaic-settings-ai-textarea-bottom': aiTextareaPosition.bottom,
                 }}>
                {mode === 'custom' && (
                    <ContentEditable
                        value={instructions}
                        onChange={setInstructions}
                        placeholder={__("Instructions", 'limb-chatbot')}
                        onEnter={generate}
                        disabled={disabled}
                        autofocus
                    />
                )}
                <button onClick={generate}
                        className={`lbaic-settings-button lbaic-settings-button-center lbaic-settings-button-h-28 lbaic-settings-button-primary lbaic-settings-copilot-mode-custom-action${isButtonDisabled ? ' lbaic-settings-button-disabled' : ''}`}
                        tabIndex='-1'>
                    {generating &&
                        <svg className='lbaic-settings-button-i lbaic-settings-loading-circle lbaic-settings-button-loading-circle'
                             xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
                            <use href='#lbaic-settings-circle'/>
                        </svg>}
                    <span className='lbaic-settings-button-label'>{__("Generate", 'limb-chatbot')}</span>
                </button>
            </div>
            , document.getElementById('lbaic-settings-field-menu'))}
        {showAISettingsPopup &&
            <Settings saved={settingsSaved} close={() => setShowAISettingsPopup(false)} notifications={notifications}/>}
        {showAddConfigPopup && (
            <AddEditConfig
                aiProviderId={defaultModel?.ai_provider_id}
                added={configAdded}
                close={() => setShowAddConfigPopup(false)}
                configData={{
                    default: 1,
                }}
                notifications={notifications}
            />
        )}
    </>
}