import {useEffect, useState} from "@wordpress/element";
import {__} from "@wordpress/i18n";
import {CUSTOM_OPTIONS_KEYS_PREFIX, UTILITIES_HEADER_KEYS} from "../../../../../../../../_data/chatbot/costs-limits";
import CostToken from "../../../../../../../../fields/cost-token";
import Dropdown from "../../../../../../../../fields/dropdown";
import {MAX_COSTS, MAX_TOKENS, formatCostValue} from "../../../../../../../../_data/chatbot/costs-limits";
import CustomOption from "../../../../../../../../_components/chatbot/costs-limits/custom-option";
import {LIMITS} from "../../../../../../../../_data/chatbot/costs-limits";
import {numberFormat} from "../../../../../../../../../../helpers";
import TableAccordionContentLoading from "../../../../../../containers/table-accordion-content-loading";
import useCostTokenOnToggle from "../../../../../../../../hooks/cost-token-on-toggle";
import ToggleButton from "../../../../../../../../fields/toggle-button";

export default function SingleMessageOutputUtility({slug, active, toggle, limits, setLimits, isDataFetched, notifications}) {
    // Actions states
    const [isDataReady, setIsDataReady] = useState(false);
    const [showAddNewMaxCostOptionPopup, setShowAddNewMaxCostOptionPopup] = useState(false);
    const [showAddNewMaxTokenOptionPopup, setShowAddNewMaxTokenOptionPopup] = useState(false);
    // Settings
    const [type, setType] = useState('cost');
    const [cost, setCost] = useState({
        value: 0,
        period: 0,
        status: 0,
    });
    const [token, setToken] = useState({
        value: 0,
        period: 0,
        status: 0,
    });
    const [costIndex, setCostIndex] = useState(-1);
    const [tokenIndex, setTokenIndex] = useState(-1);

    useCostTokenOnToggle({
        limitName: slug,
        type,
        isDataReady,
        setLimits,
    });

    useEffect(() => {
        if (isDataFetched) {
            setupData();
        }
    }, [isDataFetched]);

    useEffect(() => {
        updateUnitState(limits, costIndex, setCost);
    }, [limits, costIndex]);

    useEffect(() => {
        updateUnitState(limits, tokenIndex, setToken);
    }, [limits, tokenIndex]);

    /**
     * Get unit value
     *
     * @param {object[]} limits Limits
     * @param {number} unitIndex Unit index
     * @param {string} key Unit value key
     * @return {*|number}
     */
    const getUnitValue = (limits, unitIndex, key) => {
        return (slug in limits) && (unitIndex in limits[slug]) ? limits[slug][unitIndex][key] || 0 : 0;
    }

    /**
     * Update unit state
     *
     * @param {object[]} limits Limits
     * @param {number} unitIndex Unit index
     * @param {function} setter Unit state setter function
     */
    const updateUnitState = (limits, unitIndex, setter) => {
        // Get unit values
        const value = getUnitValue(limits, unitIndex, 'value');
        const period = getUnitValue(limits, unitIndex, 'period');
        const status = getUnitValue(limits, unitIndex, 'status');
        // Update unit state
        setter(prevState => ({
            ...prevState,
            value,
            period,
            status,
        }));
    }

    /**
     * Setup data
     * Add limits' data that have not been defined to manage settings
     */
    const setupData = () => {
        const data = slug in limits ? limits[slug] : [];
        const notSavedLimitsIndexes = [LIMITS.unitCost, LIMITS.unitToken];
        for (let i = 0; i < data.length; i++) {
            if (data[i].unit === LIMITS.unitCost) {
                notSavedLimitsIndexes.splice(notSavedLimitsIndexes.indexOf(LIMITS.unitCost), 1);
                setCostIndex(i);
            } else if (data[i].unit === LIMITS.unitToken) {
                notSavedLimitsIndexes.splice(notSavedLimitsIndexes.indexOf(LIMITS.unitToken), 1);
                setTokenIndex(i);
            }
        }
        // Check what type of limit is set: cost or token
        if (data.find(item => item.unit === LIMITS.unitToken && item.value)) {
            setType('token');
        }
        // Setup unavailable limits
        if (notSavedLimitsIndexes.length) {
            const newLimits = [];
            for (const unit of notSavedLimitsIndexes) {
                const newLimit = {
                    unit: unit,
                    value: 0,
                    period: 0,
                    status: 0,
                    // message: ''
                };
                // Set default values
                if (newLimit.unit === LIMITS.unitCost) { // Cost
                    setCostIndex(data.length + newLimits.length);
                } else if (newLimit.unit === LIMITS.unitToken) { // Token
                    setTokenIndex(data.length + newLimits.length);
                }
                newLimits.push(newLimit);
            }
            // Add to settings
            setLimits(prevState => ({
                ...prevState,
                [slug]: [
                    ...(prevState[slug] || []),
                    ...newLimits,
                ]
            }));
        }
        setTimeout(() => {
            setIsDataReady(true);
        }, 200);
    }

    /**
     * Update unit key state
     *
     * @param {number} unit Unit
     * @param {string} key Unit value key
     * @param {string|number} value Unit value
     */
    const updateUnitKey = (unit, key, value) => {
        const data = {
            [key]: value
        };
        if (key === 'value' && !value) {
            data['status'] = 0;
        }
        setLimits(prevState => ({
            ...prevState,
            [slug]: [
                ...prevState[slug].map(item => {
                    if (item.unit === unit) {
                        return {
                            ...item,
                            ...data
                        }
                    }
                    return item;
                })
            ]
        }));
    }

    /**
     * Add custom option
     *
     * @param {number|string} value Value
     * @param {string} key Storage key
     * @param {object[]} options Options
     * @param {number} unit Unit
     * @param {function|null} convertLabel Convert label callback
     * @param {function|null} convertValue Convert value callback
     * @return {*}
     */
    const addCustomOption = (value, key, options, unit, convertLabel = null, convertValue = null) => {
        // Storage key
        const storageKey = `${CUSTOM_OPTIONS_KEYS_PREFIX}.${key}.${slug}`;
        // Custom options
        const customOptions = JSON.parse(localStorage.getItem(storageKey) || '[]');
        // Convert value
        const convertedValue = typeof convertValue === 'function' ? convertValue(value) : value;
        // Check options and add if not already defined
        if (options.findIndex(item => item.value === convertedValue) === -1 && customOptions.findIndex(item => item.value === convertedValue) === -1) {
            const label = typeof convertLabel === 'function' ? convertLabel(value) : String(value);
            // Save custom option
            customOptions.push(convertedValue);
            localStorage.setItem(storageKey, JSON.stringify(customOptions));
            // Update options state
            options.push({
                label,
                value: convertedValue,
            });
        }
        return convertedValue;
    }

    return <>
        <tr className={`lbaic-settings-table-accordion-body-in lbaic-settings-table-accordion-summary lbaic-settings-cb-cl-table-summary${active === slug ? ' active' : ''}`}
            onClick={() => toggle(slug)}>
            <td className='lbaic-settings-table-accordion-body-item'>
                <div className='lbaic-settings-cb-cl-table-item'>
                    <span className='lbaic-settings-table-accordion-body-label'>{__("Single message (output)", 'limb-chatbot')}</span>
                </div>
            </td>
            <td className='lbaic-settings-table-accordion-body-item lbaic-settings-table-accordion-body-arrow'>
                <svg className='lbaic-settings-table-accordion-body-arrow-i' xmlns='http://www.w3.org/2000/svg' fill='none'
                     viewBox='0 0 24 24'>
                    <use href='#lbaic-settings-arrow'/>
                </svg>
            </td>
        </tr>
        <tr className={`lbaic-settings-table-accordion-body-in lbaic-settings-table-accordion-details${active === slug ? ' opened' : ''}`}>
            <td className='lbaic-settings-table-accordion-body-item lbaic-settings-table-accordion-details-in'
                colSpan={UTILITIES_HEADER_KEYS.length}>
                <div className='lbaic-settings-table-accordion-content'>
                    {!isDataReady && <TableAccordionContentLoading/>}
                    <div className="lbaic-settings-cb-cl-column">
                        <CostToken type={type} setType={setType}/>
                        <div className="lbaic-settings-cb-cl-column-in">
                            {type === 'cost' &&
                                <Dropdown value={cost.value}
                                          setValue={(value) => updateUnitKey(LIMITS.unitCost, 'value', value)}
                                          options={MAX_COSTS[slug]}
                                          defaultIcon="attach-money" iconClassName="lbaic-settings-cb-cl-ct-icon cost"
                                          addNew={() => setShowAddNewMaxCostOptionPopup(true)}
                                          placeholder={__("Max costs", 'limb-chatbot')}/>}
                            {type === 'token' &&
                                <Dropdown value={token.value}
                                          setValue={(value) => updateUnitKey(LIMITS.unitToken, 'value', value)}
                                          options={MAX_TOKENS[slug]}
                                          defaultIcon="match-case" iconClassName="lbaic-settings-cb-cl-ct-icon cost"
                                          addNew={() => setShowAddNewMaxTokenOptionPopup(true)}
                                          placeholder={__("Max tokens", 'limb-chatbot')}/>}
                        </div>
                        <div className="lbaic-settings-cb-cl-field-status">
                            <ToggleButton isActive={token.value ? token.status : cost.status}
                                          onClick={() => token.value ? updateUnitKey(LIMITS.unitToken, 'status', +!token.status) : updateUnitKey(LIMITS.unitCost, 'status', +!cost.status)}/>
                        </div>
                    </div>
                </div>
            </td>
        </tr>
        {showAddNewMaxCostOptionPopup &&
            <CustomOption
                setValue={(value) => {
                    const converted = addCustomOption(value, 'max_costs', MAX_COSTS[slug], LIMITS.unitCost, (v) => formatCostValue(v));
                    updateUnitKey(LIMITS.unitCost, 'value', converted);
                    setShowAddNewMaxCostOptionPopup(false);
                }}
                title={__("New max cost", 'limb-chatbot')}
                type="float" label={__("Max cost", 'limb-chatbot')}
                close={() => setShowAddNewMaxCostOptionPopup(false)}
                notifications={notifications}/>}
        {showAddNewMaxTokenOptionPopup &&
            <CustomOption
                setValue={(value) => {
                    const converted = addCustomOption(value, 'max_tokens', MAX_TOKENS[slug], LIMITS.unitToken, (v) => numberFormat(v, false));
                    updateUnitKey(LIMITS.unitToken, 'value', converted);
                    setShowAddNewMaxTokenOptionPopup(false);
                }}
                title={__("New max token", 'limb-chatbot')}
                type="integer" label={__("Max token", 'limb-chatbot')}
                close={() => setShowAddNewMaxTokenOptionPopup(false)}
                notifications={notifications}/>}
    </>
}