import { animated } from "@react-spring/web";
import {ReactElement, useMemo} from "react";
import {
    evaluateResourceBarStyles,
    ResourceBarColors,
    ResourceBarStyles
} from "./resource_bar";
import {isDefined} from "./type_check";
import {
    SpringyValueInterpolatables,
    useSpringyValue
} from "./SpringyValueHook";
import "./CharacterStatus.css";

export enum CharacterHealth {
    Full = "Full",
    Healthy = "Healthy",
    Wounded = "Wounded",
    Crisis = "Crisis",
    Peril = "Peril",
    KO = "KO",
}

export enum CharacterTurnState {
    None = "None",
    Ready = "Ready",
    HighTurns = "HighTurns",
    Done = "Done",
    CantAct = "CantAct",
    KO = "KO",
}

export enum SPType {
    UltimaPoints = "Ultima",
    FabulaPoints = "Fabula",
}

export interface StatusEffect {
    name: string
    iconUrl: string
}

export interface Character {
    portraitUrl?: string
    name?: string
    level?: number
    hp?: number
    maxHp?: number
    health?: CharacterHealth
    mp?: number
    maxMp?: number
    ip?: number
    maxIp?: number
    sp?: number
    spType?: SPType
    turnsLeft?: number
    turnsTotal?: number
    canAct?: boolean
    statuses?: StatusEffect[]
}

const hpBarStyle: SpringyValueInterpolatables<ResourceBarStyles> = {
    foreground: 'linear-gradient(to bottom, rgb(255, 255, 255, 0.1) 0%, rgb(0, 0, 0, 0) 30% 50%, rgb(0, 0, 0, 0.1) 80%, rgb(0, 0, 0, 0.2) 95%, rgb(0, 0, 0, 0.3) 100%)',
    barDirection: "to right",
    barColors: ({flashValue}: {flashValue: number}): ResourceBarColors => {
        return {
            emptiedColor: `rgb(${Math.min(1, Math.max(flashValue, 0)) * 55 + 55}, 55, 55)`,
            toEmptyColor: 'rgb(200, 0, 0)',
            toFillColor: 'rgb(150, 250, 250)',
            filledColor: 'rgb(0, 200, 0)',
        }
    },
}

const mpBarStyle: SpringyValueInterpolatables<ResourceBarStyles> = {
    foreground: 'linear-gradient(to bottom, rgb(255, 255, 255, 0.1) 0%, rgb(0, 0, 0, 0) 30% 50%, rgb(0, 0, 0, 0.1) 80%, rgb(0, 0, 0, 0.2) 95%, rgb(0, 0, 0, 0.3) 100%)',
    barColors: ({flashValue}: {flashValue: number}): ResourceBarColors => {
        return {
            emptiedColor: `rgb(${Math.min(1, Math.max(flashValue, 0)) * 55 + 55}, 55, 55)`,
            toEmptyColor: 'rgb(250, 250, 60)',
            toFillColor: 'rgb(150, 250, 250)',
            filledColor: 'rgb(0, 150, 255)',
        }
    },
}

const ipBarStyle: SpringyValueInterpolatables<ResourceBarStyles> = {
    foreground: 'linear-gradient(to bottom, rgb(255, 255, 255, 0.1) 0%, rgb(0, 0, 0, 0) 30% 50%, rgb(0, 0, 0, 0.1) 80%, rgb(0, 0, 0, 0.2) 95%, rgb(0, 0, 0, 0.3) 100%)',
    barColors: ({flashValue}: {flashValue: number}): ResourceBarColors => {
        return {
            emptiedColor: `rgb(${Math.min(1, Math.max(flashValue, 0)) * 55 + 55}, 55, 55)`,
            toEmptyColor: 'rgb(160, 55, 195)',
            toFillColor: 'rgb(250, 250, 60)',
            filledColor: 'rgb(255, 150, 55)',
        }
    },
}

export function CharacterStatus({character}: {character: Character}): ReactElement {
    const {name, level, health} = character

    const {hp, maxHp} = character
    const {interpolate: hpInterpolate} = useSpringyValue({
        current: hp,
        max: maxHp,
        flash: isDefined(maxHp) && isDefined(hp) && hp * 2 < maxHp && hp > 0,
    })
    const {hpText, hpBarStyleInterpolated} = useMemo(() => {
        if (isDefined(hp) && isDefined(maxHp) && maxHp > 0) {
            return {
                hpText: hpInterpolate(({recentValue}) => `${Math.round(recentValue)}`),
                hpBarStyleInterpolated: evaluateResourceBarStyles(hpBarStyle, hpInterpolate),
            }
        } else {
            return {}
        }
    }, [hp, maxHp, hpInterpolate])

    const {mp, maxMp} = character
    const {interpolate: mpInterpolate} = useSpringyValue({
        current: mp,
        max: maxMp,
        flash: false,
    })
    const {mpText, mpBarStyleInterpolated} = useMemo(() => {
        if (isDefined(mp) && isDefined(maxMp) && maxMp > 0) {
            return {
                mpText: mpInterpolate(({recentValue}) => `${Math.round(recentValue)}`),
                mpBarStyleInterpolated: evaluateResourceBarStyles(mpBarStyle, mpInterpolate),
            }
        } else {
            return {}
        }
    }, [mp, maxMp, mpInterpolate])

    const {ip, maxIp} = character
    const {interpolate: ipInterpolate} = useSpringyValue({
        current: ip,
        max: maxIp,
        flash: false,
    })
    const {ipText, ipBarStyleInterpolated} = useMemo(() => {
        if (isDefined(ip) && isDefined(maxIp) && maxIp > 0) {
            return {
                ipText: ipInterpolate(({recentValue}) => `${Math.round(recentValue)}`),
                ipBarStyleInterpolated: evaluateResourceBarStyles(ipBarStyle, ipInterpolate),
            }
        } else {
            return {}
        }
    }, [ip, maxIp, ipInterpolate])

    const {sp, spType} = character
    const {interpolate: spInterpolate} = useSpringyValue({
        current: sp,
        flash: isDefined(spType) && isDefined(sp) && sp > 0,
    })
    const {spText} = useMemo(() => {
        if (isDefined(sp) && isDefined(spType)) {
            return {
                spText: spInterpolate(({recentValue}) => recentValue.toFixed(0))
            }
        } else {
            return {}
        }
    }, [sp, spType, spInterpolate])

    const {turnsLeft, turnsTotal, canAct} = character
    const {turnsState, turnsText} = useMemo(() => {
        if (isDefined(turnsTotal) && hp === 0 && isDefined(maxHp) && maxHp > 0) {
            return {
                turnsState: CharacterTurnState.KO,
            }
        } else if (isDefined(turnsTotal) && (canAct === false || turnsTotal === 0)) {
            return {
                turnsState: CharacterTurnState.CantAct,
            }
        } else if (isDefined(turnsTotal) && turnsLeft === 0) {
            return {
                turnsState: CharacterTurnState.Done,
            }
        } else if (turnsTotal === 1 && turnsLeft === 1) {
            return {
                turnsState: CharacterTurnState.Ready,
            }
        } else if (isDefined(turnsTotal) && turnsTotal > 1 && isDefined(turnsLeft)) {
            return {
                turnsState: CharacterTurnState.HighTurns,
                turnsText: `${turnsLeft}`
            }
        } else {
            return {
                turnsState: CharacterTurnState.None
            }
        }
    }, [hp, maxHp, canAct, turnsLeft, turnsTotal])

    return <div className="characterStatus">
        <div className={"characterPortrait"} />
        {isDefined(turnsState) &&
            <div className={"characterTurns characterTurns" + turnsState}>{turnsText}</div>}
        <div className={"characterHeader"}>
            {isDefined(level) &&
                <div className="characterLevel">
                    <span className="characterLevelLabel">Lv</span>
                    <span className="characterLevelValue">{level}</span>
                </div>}
            {isDefined(name) &&
                <div className={"characterName characterName" + (health ?? "Unknown")}>{name}</div>}
        </div>
        {isDefined(hpText) &&
            <div className={"characterHp"}>
                <animated.div className={"characterHpBar"} style={hpBarStyleInterpolated} />
                <animated.div className={"characterHpValue"}>{hpText}</animated.div>
            </div>}
        {isDefined(mpText) &&
            <div className={"characterMp"}>
                <animated.div className={"characterMpBar"} style={mpBarStyleInterpolated} />
                <animated.div className={"characterMpValue"}>{mpText}</animated.div>
            </div>}
        {isDefined(ipText) &&
            <div className={"characterIp"}>
                <animated.div className={"characterIpBar"} style={ipBarStyleInterpolated} />
                <animated.div className={"characterIpValue"}>{ipText}</animated.div>
            </div>}
        {isDefined(spText) &&
            <animated.div className={"characterSp characterSp" + spType}>
                <animated.span className={"characterSpValue characterSpValue" + spType}>
                    {spText}</animated.span>
            </animated.div>}
    </div>
}
