import {
    useContext
} from "react";
import {
    type ThemeContextStateType,
    type GapPropagationType,
    type ThemeContextType,
    type SharpnessType,
    type ThemesType,
    type ThemeConfigs
} from "../types";
import NCoreContext, {
    type ConfigType
} from "ncore-context";
import {
    mergeTypographyTokens,
    mergePalettes,
    mergeShapes
} from "../helpers";
import _defaultPaletteData from "../variants/themes/default.json";
import {
    androidTypographyFixer
} from "../utils";

const defaultPaletteData = androidTypographyFixer(_defaultPaletteData);

class NCoreUIKitTheme<T extends ThemesType> extends NCoreContext<ThemeContextType, ConfigType<ThemeContextType>> {
    projectThemes?: NCoreUIKit.Palette = defaultPaletteData;

    useContext = ({
        gapPropagation,
        paletteKey,
        sharpness,
        themeKey
    }: {
        paletteKey?: keyof NCoreUIKit.PaletteKey;
        themeKey?: keyof NCoreUIKit.ThemeKey;
        gapPropagation?: GapPropagationType;
        sharpness?: SharpnessType;
    } = {}): ThemeContextType => {
        const currentState = useContext(this.stateContext);

        if(
            gapPropagation ||
            paletteKey ||
            sharpness ||
            themeKey
        ) {
            const activeState = this.prepare({
                activeGapPropagation: gapPropagation ? gapPropagation : currentState.activeGapPropagation,
                activeSharpness: sharpness ? sharpness : currentState.activeSharpness,
                activePalette: paletteKey ? paletteKey : currentState.activePalette,
                activeTheme: themeKey ? themeKey : currentState.activeTheme
            });

            return {
                ...currentState,
                ...activeState
            };
        }

        return currentState;
    };

    constructor(initialState: T) {
        const {
            projectThemes,
            initialSelectedActiveSharpness,
            initialSelectedGapPropagation,
            initialSelectedPalette,
            initialSelectedTheme
        } = initialState;

        const initialPalette = defaultPaletteData.palettes.find(p => p.name === (initialSelectedPalette ?? "nibgat"));

        if(!initialPalette) {
            throw new Error("Initial Theme error!.");
        }

        const initialTheme = initialPalette.themes[initialSelectedTheme ?? "light"];

        super({
            paletteKeys: defaultPaletteData.palettes.map(p => p.name) as Array<keyof NCoreUIKit.PaletteKey>,
            themeKeys: Object.keys(initialPalette.themes) as Array<keyof NCoreUIKit.ThemeKey>,
            activeGapPropagation: initialSelectedGapPropagation ?? "compact",
            activeSharpness: initialSelectedActiveSharpness ?? "soft",
            inlineSpaces: defaultPaletteData.shapes.inlineSpaces,
            shapeConfigs: defaultPaletteData.shapes.shapeConfigs,
            typography: defaultPaletteData.typography.spacious,
            radiuses: defaultPaletteData.shapes.radiuses.soft,
            spaces: defaultPaletteData.shapes.spaces.spacious,
            activePalette: initialSelectedPalette ?? "nibgat",
            activeTheme: initialSelectedTheme ?? "dark",
            borders: defaultPaletteData.shapes.borders,
            colors: initialTheme,
            configs: {
                headerSpace: 0
            }
        }, {
            key: "NCoreUIKitTheme"
        });

        this.projectThemes = projectThemes;

        const activeState = this.prepare({
            activeGapPropagation: initialSelectedGapPropagation ?? "compact",
            activeSharpness: initialSelectedActiveSharpness ?? "soft",
            activePalette: initialSelectedPalette ?? "nibgat",
            activeTheme: initialSelectedTheme ?? "dark"
        });

        this.state = activeState as ThemeContextType;
    }

    updateConfigs = (configs: Partial<ThemeConfigs>) => {
        this.setState({
            configs: {
                ...this.state.configs,
                ...configs
            }
        });
    };

    prepare = (props?: {
        activePalette?: keyof NCoreUIKit.PaletteKey;
        activeGapPropagation?: GapPropagationType;
        activeTheme?: keyof NCoreUIKit.ThemeKey;
        activeSharpness?: SharpnessType;
    }): ThemeContextStateType => {
        const gapPropagation = props && props.activeGapPropagation ? props.activeGapPropagation : this.state.activeGapPropagation;
        const sharpness = props && props.activeSharpness ? props.activeSharpness : this.state.activeSharpness;
        const palette = props && props.activePalette ? props.activePalette : this.state.activePalette;
        const theme = props && props.activeTheme ? props.activeTheme : this.state.activeTheme;

        const defaultPalette = defaultPaletteData.palettes.find(p => p.name === palette) ?? defaultPaletteData.palettes[0]!;
        const defaultTheme = defaultPalette.themes[theme as keyof typeof defaultPalette.themes] ?? defaultPalette.themes.dark;

        const defaultState: ThemeContextStateType = {
            paletteKeys: defaultPaletteData.palettes.map(p => p.name) as Array<keyof NCoreUIKit.PaletteKey>,
            themeKeys: Object.keys(defaultPalette.themes) as Array<keyof NCoreUIKit.ThemeKey>,
            typography: defaultPaletteData.typography[gapPropagation],
            spaces: defaultPaletteData.shapes.spaces[gapPropagation],
            radiuses: defaultPaletteData.shapes.radiuses[sharpness],
            inlineSpaces: defaultPaletteData.shapes.inlineSpaces,
            shapeConfigs: defaultPaletteData.shapes.shapeConfigs,
            borders: defaultPaletteData.shapes.borders,
            activeGapPropagation: gapPropagation,
            configs: this.state.configs,
            activeSharpness: sharpness,
            activePalette: palette,
            colors: defaultTheme,
            activeTheme: theme
        };

        if(!this.projectThemes || (this.projectThemes && !this.projectThemes.palettes.length)) {
            return defaultState;
        }

        const currentProjectPalette = this.projectThemes.palettes.find(p => p.name === palette);

        if(!currentProjectPalette) {
            return defaultState;
        }

        const currentProjectTheme = currentProjectPalette.themes[theme];

        if(!currentProjectTheme) {
            return defaultState;
        }

        const shapes = mergeShapes({
            projectShapes: this.projectThemes.shapes,
            defaultShapes: defaultPaletteData.shapes
        });

        const typography = mergeTypographyTokens({
            defaultTypographyTokens: defaultPaletteData.typography,
            projectTypographyTokens: this.projectThemes.typography
        });

        const colors = mergePalettes(
            defaultPaletteData.palettes,
            this.projectThemes.palettes,
            palette,
            theme
        );

        const dPKeys = [
            ...new Set([
                ...Object.keys(defaultPaletteData.palettes[0]?.themes ?? currentProjectPalette.themes),
                ...Object.keys(this.projectThemes.palettes[0]?.themes ?? currentProjectPalette.themes)
            ])
        ];

        const dPPKeys = [
            ...new Set([
                ...defaultPaletteData.palettes.map(p => p.name),
                ...this.projectThemes.palettes.map(p => p.name)
            ])
        ];

        const newState: ThemeContextStateType = {
            paletteKeys: dPPKeys as Array<keyof NCoreUIKit.PaletteKey>,
            themeKeys: dPKeys as Array<keyof NCoreUIKit.ThemeKey>,
            typography: typography[gapPropagation],
            spaces: shapes.spaces[gapPropagation],
            activeGapPropagation: gapPropagation,
            radiuses: shapes.radiuses[sharpness],
            shapeConfigs: shapes.shapeConfigs,
            inlineSpaces: shapes.inlineSpaces,
            configs: this.state.configs,
            activeSharpness: sharpness,
            borders: shapes.borders,
            activePalette: palette,
            activeTheme: theme,
            colors: colors
        };

        return newState;
    };

    switch = ({
        gapPropagation,
        paletteKey,
        sharpness,
        themeKey
    }: {
        paletteKey?: keyof NCoreUIKit.PaletteKey;
        themeKey?: keyof NCoreUIKit.ThemeKey;
        gapPropagation?: GapPropagationType;
        sharpness?: SharpnessType;
    }) => {
        const activeState = this.prepare({
            activeGapPropagation: gapPropagation ?? this.state.activeGapPropagation,
            activeSharpness: sharpness ?? this.state.activeSharpness,
            activePalette: paletteKey ?? this.state.activePalette,
            activeTheme: themeKey ?? this.state.activeTheme
        });

        this.setState(activeState);
    };

    updateProjectTheme = (newProjectThemes: NCoreUIKit.Palette, newState?: {
        activePalette?: keyof NCoreUIKit.PaletteKey;
        activeGapPropagation?: GapPropagationType;
        activeTheme?: keyof NCoreUIKit.ThemeKey;
        activeSharpness?: SharpnessType;
    }) => {
        this.projectThemes = newProjectThemes;
        const activeState = this.prepare(newState);

        this.setState(activeState);
    };
}
export default NCoreUIKitTheme;
