import * as React from "react";

import * as CompileConfig from "../../../config.json";
import { Category, CategorySelection, CategorySkipOption } from "../../types";
import { CategorySkipOptionsComponent, ExtraOptionComponent, ToggleOption } from "./CategorySkipOptionsComponent";
import { SelectOptionComponent } from "./SelectOptionComponent";
import Config, { ConfigurationID, CustomConfiguration } from "../../config";
import { generateUserID } from "../../../maze-utils/src/setup";

let forceUpdateSkipProfilesTimeout: NodeJS.Timeout | null = null;
let forceUpdateSkipProfileIDsTimeout: NodeJS.Timeout | null = null;

export function CategoryChooserComponent() {
    const [configurations, setConfigurations] = React.useState(Config.local!.skipProfiles);
    const [selectedConfigurationID, setSelectedConfigurationID] = React.useState<ConfigurationID | null>(null);
    const [channelListText, setChannelListText] = React.useState("");

    const [configurationName, setConfigurationName] = React.useState("");
    const [selections, setSelections] = React.useState<CategorySelection[]>([]);

    React.useEffect(() => {
        setConfigurationName(getConfigurationValue(selectedConfigurationID, "name", ""));

        updateChannelList(setChannelListText, selectedConfigurationID!);
        setSelections(getConfigurationValue<CategorySelection[]>(selectedConfigurationID, "categorySelections"));

        if (selectedConfigurationID === null) {
            document.querySelectorAll(".hide-when-skip-profile").forEach((e) => e.classList.remove("hidden"));
        } else {
            document.querySelectorAll(".hide-when-skip-profile").forEach((e) => e.classList.add("hidden"));
        }
    }, [selectedConfigurationID]);

    const createNewConfig = () => {
        let newID = generateUserID().substring(0, 5);
        while (Config.local.skipProfiles[newID]) {
            newID = generateUserID().substring(0, 5);
        }

        const newConfiguration: CustomConfiguration = {
            name: `${chrome.i18n.getMessage("NewConfiguration")} ${Object.keys(Config.local.skipProfiles).length}`,
            categorySelections: [],
            showAutogeneratedChapters: null,
            autoSkipOnMusicVideos: null,
            skipNonMusicOnlyOnYoutubeMusic: null,
            muteSegments: null,
            fullVideoSegments: null,
            manualSkipOnFullVideo: null,
            minDuration: null
        };

        Config.local!.skipProfiles[newID] = newConfiguration;
        forceUpdateConfigurations();
        setConfigurations(Config.local!.skipProfiles);
        setSelectedConfigurationID(newID as ConfigurationID);

        updateChannelList(setChannelListText, newID as ConfigurationID);
    };
    React.useEffect(() => {
        if (window.location.hash === "#newProfile") {
            createNewConfig();
        }
    }, []);

    return (
        <>
            <div className="categoryChooserTopRow">
                <SelectOptionComponent
                    id="channelProfiles"
                    onChange={(value) => {
                        if (value === "null") value = null;

                        setSelectedConfigurationID(value as ConfigurationID);
                        updateChannelList(setChannelListText, value as ConfigurationID);
                    }}
                    value={selectedConfigurationID!}
                    options={[{
                        value: "null",
                        label: chrome.i18n.getMessage("DefaultConfiguration")
                    }].concat(Object.entries(configurations).map(([key, value]) => ({
                        value: key,
                        label: value.name
                    })))}
                />

                <div 
                    className="option-button trigger-button"
                    onClick={() => createNewConfig()}>
                    {chrome.i18n.getMessage("NewConfiguration")}
                </div>
            </div>

            {
                selectedConfigurationID &&
                <div className="configurationInfo">
                    <input 
                        type="text"
                        id="configurationName"
                        value={configurationName}
                        placeholder={chrome.i18n.getMessage("ConfigurationName")}
                        style={{width: document.getElementById("channelProfiles")?.clientWidth ?? null}}
                        onChange={(e) => {
                            const newName = e.target.value;
                            getConfig(selectedConfigurationID)!.name = newName;
                            setConfigurationName(newName);

                            forceUpdateConfigurations();
                            setConfigurations(Config.local!.skipProfiles);
                        }}/>

                    <div>
                        {chrome.i18n.getMessage("ChannelListInstructionsSB")}
                    </div>
                
                    <textarea 
                        className="option-text-box" 
                        rows={10} 
                        value={channelListText}
                        onChange={(e) => {
                            const newText = e.target.value;
                            setChannelListText(newText);

                            const channels = newText.split("\n").map((channel) => channel.trim()).filter((channel) => channel !== "");
                            if (channels.length > 0) {
                                for (const [channelID, id] of Object.entries(Config.local!.channelSkipProfileIDs)) {
                                    if (id === selectedConfigurationID) {
                                        if (!channels.includes(channelID)) {
                                            delete Config.local!.channelSkipProfileIDs[channelID];
                                        }
                                    }
                                }

                                for (const channel of channels) {
                                    Config.local!.channelSkipProfileIDs[channel] = selectedConfigurationID;
                                }
                            }

                            forceUpdateConfigurationIDs();
                        }}/>
                    
                    <div 
                        className="option-button trigger-button"
                        onClick={() => {
                            if (confirm(chrome.i18n.getMessage("areYouSureDeleteConfig"))) {
                                delete Config.local.skipProfiles[selectedConfigurationID];
                                forceUpdateConfigurations();

                                for (const [channelID, id] of Object.entries(Config.local.channelSkipProfileIDs)) {
                                    if (id === selectedConfigurationID) {
                                        delete Config.local.channelSkipProfileIDs[channelID];
                                    }
                                }
                                forceUpdateConfigurationIDs();

                                if (Config.local.skipProfileTemp && Config.local.skipProfileTemp.configID === selectedConfigurationID) {
                                    Config.local.skipProfileTemp = null;
                                }

                                setConfigurations(Config.local!.skipProfiles);
                                const newID = Object.keys(Config.local!.skipProfiles)[0] as ConfigurationID;
                                setSelectedConfigurationID(newID ?? null);
                            }
                        }}>
                        {chrome.i18n.getMessage("DeleteConfiguration")}
                    </div>
                </div>
            }

            <table id="categoryChooserTable"
                className="categoryChooserTable"> 
                <tbody>
                    {/* Headers */}
                    <tr id={"CategoryOptionsRow"}
                            className="categoryTableElement categoryTableHeader">
                        <th id={"CategoryOptionName"}>
                            {chrome.i18n.getMessage("category")}
                        </th>

                        <th id={"CategorySkipOption"}
                            className="skipOption">
                            {chrome.i18n.getMessage("skipOption")}
                        </th>

                        <th id={"CategoryColorOption"}
                            className="colorOption">
                            {chrome.i18n.getMessage("seekBarColor")}
                        </th>

                        <th id={"CategoryPreviewColorOption"}
                            className="previewColorOption">
                            {chrome.i18n.getMessage("previewColor")}
                        </th>
                    </tr>

                    <CategorySkipOptions
                        selectedConfigurationID={selectedConfigurationID}
                        selections={selections}
                        setSelections={setSelections}
                    />
                </tbody> 
            </table>

            <ExtraOptionsComponent
                selectedConfigurationID={selectedConfigurationID!}/>
        </>
    );
}

function CategorySkipOptions({ selectedConfigurationID, selections, setSelections}: { selectedConfigurationID: ConfigurationID | null;
        selections: CategorySelection[]; setSelections: (s: CategorySelection[]) => void; }): JSX.Element {
    const elements: JSX.Element[] = [];
    const defaultSkipOption = selectedConfigurationID === null ? CategorySkipOption.Disabled : CategorySkipOption.FallbackToDefault;

    for (const category of CompileConfig.categoryList) {
        elements.push(
            <CategorySkipOptionsComponent
                category={category as Category}
                selection={selections.find(selection => selection.name === category)?.option ?? defaultSkipOption}
                updateSelection={(option: CategorySkipOption) => {
                    const existingSelection = selections.find(selection => selection.name === category);
                    const deletingSelection = (option === CategorySkipOption.Disabled && selectedConfigurationID === null)
                        || (option === CategorySkipOption.FallbackToDefault && selectedConfigurationID !== null);
                    if (existingSelection) {
                        existingSelection.option = option;

                        if (deletingSelection) {
                            selections.splice(selections.indexOf(existingSelection), 1);
                        }
                    } else if (!deletingSelection) {
                        selections.push({
                            name: category as Category,
                            option: option
                        });
                    }

                    // Clone so React notices the change
                    selections = [...selections];

                    updateConfigurationValue(selectedConfigurationID, "categorySelections", selections, setSelections);
                }}
                isDefaultConfig={selectedConfigurationID === null}
                selectedConfigurationID={selectedConfigurationID}
                key={category}>
            </CategorySkipOptionsComponent>
        );
    }

    return <>
        {elements}
    </>;
}

function forceUpdateConfigurations() {
    if (forceUpdateSkipProfilesTimeout) {
        clearTimeout(forceUpdateSkipProfilesTimeout);
    }

    forceUpdateSkipProfilesTimeout = setTimeout(() => {
        Config.forceLocalUpdate("skipProfiles");
    }, 50);
}

function forceUpdateConfigurationIDs() {
    if (forceUpdateSkipProfileIDsTimeout) {
        clearTimeout(forceUpdateSkipProfileIDsTimeout);
    }

    forceUpdateSkipProfileIDsTimeout = setTimeout(() => {
        Config.forceLocalUpdate("channelSkipProfileIDs");
    }, 50);
}

function updateChannelList(setChannelListText: (value: string) => void, selectedConfigurationID: ConfigurationID) {
    setChannelListText(Object.entries(Config.local!.channelSkipProfileIDs)
        .filter(([, id]) => id === selectedConfigurationID)
        .map(([channelID]) => channelID).join("\n"))
}

function getConfig(selectedConfigurationID: ConfigurationID | null) {
    return selectedConfigurationID ? Config.local!.skipProfiles[selectedConfigurationID] : null;
}

export function getConfigurationValue<T>(selectedConfigurationID: ConfigurationID | null, option: string, defaultValue?: T): T {
    if (selectedConfigurationID === null) {
        if (defaultValue !== undefined) {
            return defaultValue;
        } else {
            return Config.config[option];
        }
    } else {
        return getConfig(selectedConfigurationID)[option];
    }
}

export function updateConfigurationValue(selectedConfigurationID: ConfigurationID | null, option: string, value: unknown, setFunction?: (value: unknown) => void) {
     if (selectedConfigurationID === null) {
        Config.config[option] = value;
    } else {
        const config = getConfig(selectedConfigurationID);
        if (value !== null) {
            config[option] = value;
        } else {
            delete config[option];
        }

        forceUpdateConfigurations();
    }

    if (setFunction) setFunction(value);
}

function ExtraOptionsComponent(props: {selectedConfigurationID: ConfigurationID}): JSX.Element {
    const options: ToggleOption[][] = [[{
        configKey: "muteSegments",
        label: chrome.i18n.getMessage("muteSegments"),
        type: "toggle"
    }], [{
        configKey: "fullVideoSegments",
        label: chrome.i18n.getMessage("fullVideoSegments"),
        type: "toggle"
    }, {
        configKey: "fullVideoLabelsOnThumbnails",
        label: chrome.i18n.getMessage("fullVideoLabelsOnThumbnails"),
        type: "toggle",
        dontShowOnCustomConfigs: true
    }, {
        configKey: "manualSkipOnFullVideo",
        label: chrome.i18n.getMessage("enableManualSkipOnFullVideo"),
        description: chrome.i18n.getMessage("whatManualSkipOnFullVideo"),
        type: "toggle"
    }], [{
        configKey: "minDuration",
        label: chrome.i18n.getMessage("minDuration"),
        description: chrome.i18n.getMessage("minDurationDescription"),
        type: "number"
    }]];

    const result: JSX.Element[] = [];

    for (const optionGroup of options) {
        const groupResult: JSX.Element[] = [];
        for (const option of optionGroup) {
            groupResult.push(
                <ExtraOptionComponent
                    option={option}
                    selectedConfigurationID={props.selectedConfigurationID}
                    key={option.configKey}/>
            );
        }

        result.push(
            <div className="extraOptionGroup" key={optionGroup.map(o => o.configKey).join("-")}>
                {groupResult}
            </div>
        );
    }

    return (<>
        {result}
    </>);
}