import {get} from 'lodash';
import {createContext, useContext} from 'react';

export type ColorIntensity = number

export type ColorStateType = {
    default: string,
    hover: string,
    groupHover: string
};

export type ColorSourceType = string

export type ColorContextsType = {
    background: ColorStateType,
    border: ColorStateType,
    text: ColorStateType,
    // color string (hex, etc.)
    source: ColorSourceType
}

export type ColorType = {
    intensity: {
        /*SHADES*/
        10: ColorContextsType,
        20: ColorContextsType,
        30: ColorContextsType,
        40: ColorContextsType,
        50: ColorContextsType,
        60: ColorContextsType,
        70: ColorContextsType,
        80: ColorContextsType,
        90: ColorContextsType,
        /*NORMAL*/
        100: ColorContextsType,
        /*TINTS*/
        110: ColorContextsType,
        120: ColorContextsType,
        130: ColorContextsType
    },
    text?: {
        dark: {
            primary: string,
            secondary?: string,
            extra?: string,
        },
        light?: {
            primary: string,
            secondary?: string,
            extra?: string,
        },
        extra?: {
            primary: string,
            secondary?: string,
            extra?: string,
        },
    },
    background?: {
        light: string,
        lightDarker?: string,
        dark?: string,
        extra?: string
    },
    border?: {
        light: {
            primary: string,
            secondary?: string,
            extra?: string,
        },
        dark?: {
            primary: string,
            secondary?: string,
            extra?: string,
        },
        extra?: {
            primary: string,
            secondary?: string,
            extra?: string,
        },
    },
}

export type ColorState = {
    intensity: ColorIntensity,
    state: 'hover' | 'groupHover' | 'default'
}

export type ColorPaths =
    'text.dark.primary'
    | 'text.dark.secondary'
    | 'text.dark.extra'
    | 'text.light.primary'
    | 'text.light.secondary'
    | 'text.light.extra'
    | 'text.extra.primary'
    | 'text.extra.secondary'
    | 'text.extra.extra'
    | 'background.light'
    | 'background.lightDarker'
    | 'background.dark'
    | 'background.extra'
    | 'border.dark.primary'
    | 'border.dark.secondary'
    | 'border.dark.extra'
    | 'border.light.primary'
    | 'border.light.secondary'
    | 'border.light.extra'
    | 'border.extra.primary'
    | 'border.extra.secondary'
    | 'border.extra.extra'

export type ColorOption = {
    color: Color
}

export interface ColorWithCustomTint {
    color: Color;
    tint: (color: Color, type: keyof typeof Color.prototype, context?: string, path?: ColorPaths) => string | undefined;
}

export type ColorOptionWithCustomTint = {
    color: Color | ColorWithCustomTint
}

export type GetIntensity = number | ColorState

export const transparentColor: ColorContextsType = {
    background: {
        default: 'bg-transparent',
        hover: 'hover:bg-transparent',
        groupHover: 'group-hover:bg-transparent'
    },
    border: {
        default: 'border-transparent',
        hover: 'hover:border-transparent',
        groupHover: 'group-hover:border-transparent'
    },
    text: {
        default: 'text-transparent',
        hover: 'hover:text-transparent',
        groupHover: 'group-hover:text-transparent'
    },
    source: 'rgba(0, 0, 0, 0)'
}

export class Color {
    public readonly color: ColorType

    constructor(color: ColorType) {
        this.color = color
    }

    get(path: ColorPaths | number) {
        if (typeof path === 'number') {
            return get(this.color.intensity, path)
        }

        const color: string | undefined = get(this.color, path)

        if (color) {
            return color
        }

        //example, if we tried background.light and there's no secondary color...
        if (color?.includes('text.light')) {
            return this.color.text?.light?.primary || this.color.text?.light?.secondary || this.color.text?.light?.extra
        }

        if (color?.includes('text.dark')) {
            return this.color.text?.dark?.primary || this.color.text?.dark?.secondary || this.color.text?.dark?.extra
        }

        if (color?.includes('text.extra')) {
            return this.color.text?.extra?.primary || this.color.text?.extra?.secondary || this.color.text?.extra?.extra
        }

        // lets see if webpack dont crash after saving this
        // 
        // ...
    }

    text(intensity: GetIntensity): string {
        return this.getColor('text', intensity)
    }

    background(intensity: GetIntensity): string {
        return this.getColor('background', intensity)
    }

    border(intensity: GetIntensity): string {
        return this.getColor('border', intensity)
    }

    getColor(type: string, intensity: GetIntensity) {
        let intensityToUse: ColorIntensity
        let stateToUse: ColorState['state']

        if (typeof intensity === 'number' && intensity === 0) {
            // we need this be transparent
            return transparentColor[type].default
        } else if (typeof intensity === 'object' && intensity.intensity === 0) {
            return transparentColor[type][intensity.state];
        }

        if (typeof intensity === 'number') {
            intensityToUse = intensity
            stateToUse = 'default'
        } else {
            intensityToUse = intensity.intensity
            stateToUse = intensity.state
        }

        //example: this.color.intensity.100.background.hover
        return this.color.intensity[intensityToUse][type][stateToUse]
    }
}

export const hover = (intensity: ColorIntensity): ColorState => ({
    intensity,
    state: 'hover'
})

export const groupHover = (intensity: ColorIntensity): ColorState => ({
    intensity,
    state: 'groupHover'
})

export const CurrentColor = createContext<Color | null>(null)

export const useColor: (color?: Color) => Color = color => {
    const currentColor = useContext(CurrentColor)

    return color || currentColor!
}

export const request = (color: Color | ColorWithCustomTint, context?: string) => {
    return {
        get: (path: ColorPaths | number): string => {
            if (color instanceof Color) {
                return color.get(path)
            }

            return color.tint(color.color, 'get', context, path) ?? color.color.get(path)
        },
        text: (path: number | Parameters<typeof Color.prototype.text>[0]): string => {
            if (color instanceof Color) {
                return color.text(path)
            }

            return color.tint(color.color, 'text', context) ?? color.color.text(path)
        },
        background: (path: number | Parameters<typeof Color.prototype.background>[0]): string => {
            if (color instanceof Color) {
                return color.background(path)
            }

            return color.tint(color.color, 'background', context) ?? color.color.background?.(path)
        },
        border: (path: number | Parameters<typeof Color.prototype.border>[0]): string => {
            if (color instanceof Color) {
                return color.border(path)
            }

            return color.tint(color.color, 'border', context) ?? color.color.border?.(path)
        },
    }
}

export const resolveTint = (
    color: Color | ColorWithCustomTint,
    context: string,
    fallback: string,
    type: 'get' | 'text' | 'background' | 'border' = 'get',
    path?: ColorPaths
) => {
    if (color instanceof Color) {
        return fallback
    }

    return color.tint(color.color, type, context, path) ?? fallback
}
