import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'

import classNames from 'classnames'

import { axData } from '~utils/axData'

import './index.scss'

interface ErrorMap {
    [index: string]: string
    axValue1: string
    axValue2: string
}

interface Props {
    axType: number
    currentSkills?: SimpleAxSkill[],
    sendValidity: (isValid: boolean) => void
    sendValues: (primaryAxModifier: number, primaryAxValue: number, secondaryAxModifier: number, secondaryAxValue: number) => void
}

const AXSelect = (props: Props) => {
    const router = useRouter()
    const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
    const { t } = useTranslation('common')

    // Set up form states and error handling
    const [errors, setErrors] = useState<ErrorMap>({
        axValue1: '',
        axValue2: ''
    })

    const primaryErrorClasses = classNames({
        'errors': true,
        'visible': errors.axValue1.length > 0
    })

    const secondaryErrorClasses = classNames({
        'errors': true,
        'visible': errors.axValue2.length > 0
    })

    // Refs
    const primaryAxModifierSelect = React.createRef<HTMLSelectElement>()
    const primaryAxValueInput = React.createRef<HTMLInputElement>()
    const secondaryAxModifierSelect = React.createRef<HTMLSelectElement>()
    const secondaryAxValueInput = React.createRef<HTMLInputElement>()

    // States
    const [primaryAxModifier, setPrimaryAxModifier] = useState(-1)
    const [secondaryAxModifier, setSecondaryAxModifier] = useState(-1)
    const [primaryAxValue, setPrimaryAxValue] = useState(0.0)
    const [secondaryAxValue, setSecondaryAxValue] = useState(0.0)

    useEffect(() => {
        if (props.currentSkills && props.currentSkills[0]) {
            if (props.currentSkills[0].modifier != null)
                setPrimaryAxModifier(props.currentSkills[0].modifier)

            setPrimaryAxValue(props.currentSkills[0].strength)
        }

        if (props.currentSkills && props.currentSkills[1]) {
            if (props.currentSkills[1].modifier != null)
                setSecondaryAxModifier(props.currentSkills[1].modifier)

            setSecondaryAxValue(props.currentSkills[1].strength)
        }
    }, [props.currentSkills])

    useEffect(() => {
        props.sendValues(primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue)
    }, [props, primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue])

    useEffect(() => {
        props.sendValidity(primaryAxValue > 0 && errors.axValue1 === '' && errors.axValue2 === '')
    }, [props, primaryAxValue, errors])

    // Classes
    const secondarySetClasses = classNames({
        'AXSet': true,
        'hidden': primaryAxModifier < 0
    })

    function generateOptions(modifierSet: number) {
        const axOptions = axData[props.axType - 1]

        let axOptionElements: React.ReactNode[] = []
        if (modifierSet == 0) {
            axOptionElements = axOptions.map((ax, i) => {
                return (
                    <option key={i} value={ax.id}>{ax.name[locale]}</option>
                )
            })
        } else {
            // If we are loading data from the server, state doesn't set before render,
            // so our defaultValue is undefined.
            let modifier = -1;
            if (primaryAxModifier >= 0)
                modifier = primaryAxModifier
            else if (props.currentSkills)
                modifier = props.currentSkills[0].modifier

            if (modifier >= 0 && axOptions[modifier]) {
                const primarySkill = axOptions[modifier]

                if (primarySkill.secondary) {
                    const secondaryAxOptions = primarySkill.secondary
                    axOptionElements = secondaryAxOptions.map((ax, i) => {
                        return (
                            <option key={i} value={ax.id}>{ax.name[locale]}</option>
                        )
                    })
                }
            }
        }

        axOptionElements?.unshift(<option key={-1} value={-1}>{t('ax.no_skill')}</option>)
        return axOptionElements
    }

    function handleSelectChange(event: React.ChangeEvent<HTMLSelectElement>) {
        const value = parseInt(event.target.value)

        if (primaryAxModifierSelect.current == event.target) {
            setPrimaryAxModifier(value)

            if (primaryAxValueInput.current && secondaryAxModifierSelect.current && secondaryAxValueInput.current) {
                setupInput(axData[props.axType - 1][value], primaryAxValueInput.current)
                
                secondaryAxModifierSelect.current.value = "-1"
                secondaryAxValueInput.current.value = ""
            }
        } else {
            setSecondaryAxModifier(value)

            const primaryAxSkill = axData[props.axType - 1][primaryAxModifier]
            const currentAxSkill = (primaryAxSkill.secondary) ? 
                primaryAxSkill.secondary.find(skill => skill.id == value) : undefined

            if (secondaryAxValueInput.current)
                setupInput(currentAxSkill, secondaryAxValueInput.current)
        }
    }

    function handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
        const value = parseFloat(event.target.value)
        let newErrors = {...errors}

        if (primaryAxValueInput.current == event.target) {
            if (handlePrimaryErrors(value))
                setPrimaryAxValue(value)
        } else {
            if (handleSecondaryErrors(value))
                setSecondaryAxValue(value)
        }
    }

    function handlePrimaryErrors(value: number) {
        const primaryAxSkill = axData[props.axType - 1][primaryAxModifier]
        let newErrors = {...errors}

        if (value < primaryAxSkill.minValue) {
            newErrors.axValue1 = t('ax.errors.value_too_low', { 
                name: primaryAxSkill.name[locale], 
                minValue: primaryAxSkill.minValue, 
                suffix: (primaryAxSkill.suffix) ? primaryAxSkill.suffix : '' 
            })
        } else if (value > primaryAxSkill.maxValue) {
            newErrors.axValue1 = t('ax.errors.value_too_high', { 
                name: primaryAxSkill.name[locale], 
                maxValue: primaryAxSkill.minValue, 
                suffix: (primaryAxSkill.suffix) ? primaryAxSkill.suffix : '' 
            })
        } else if (!value || value <= 0) {
            newErrors.axValue1 = t('ax.errors.value_empty', { name: primaryAxSkill.name[locale] })
        } else {
            newErrors.axValue1 = ''
        }

        setErrors(newErrors)

        return newErrors.axValue1.length === 0
    }

    function handleSecondaryErrors(value: number) {
        const primaryAxSkill = axData[props.axType - 1][primaryAxModifier]
        let newErrors = {...errors}

        if (primaryAxSkill.secondary) {
            const secondaryAxSkill = primaryAxSkill.secondary.find(skill => skill.id == secondaryAxModifier)

            if (secondaryAxSkill) {
                if (value < secondaryAxSkill.minValue) {
                    newErrors.axValue2 = t('ax.errors.value_too_low', { 
                        name: secondaryAxSkill.name[locale], 
                        minValue: secondaryAxSkill.minValue, 
                        suffix: (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : '' 
                    })
                } else if (value > secondaryAxSkill.maxValue) {
                    newErrors.axValue2 = t('ax.errors.value_too_high', { 
                        name: secondaryAxSkill.name[locale], 
                        maxValue: secondaryAxSkill.minValue, 
                        suffix: (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : '' 
                    })
                } else if (!secondaryAxSkill.suffix && value % 1 !== 0) {
                    newErrors.axValue2 = t('ax.errors.value_not_whole', { name: secondaryAxSkill.name[locale] })
                } else if (primaryAxValue <= 0) {
                    newErrors.axValue1 = t('ax.errors.value_empty', { name: primaryAxSkill.name[locale] })
                } else {
                    newErrors.axValue2 = ''
                }
            }
        }

        setErrors(newErrors)

        return newErrors.axValue2.length === 0
    }

    function setupInput(ax: AxSkill | undefined, element: HTMLInputElement) {
        if (ax) {
            const rangeString = `${ax.minValue}~${ax.maxValue}${ax.suffix || ''}`

            element.disabled = false
            element.placeholder = rangeString
            element.min = `${ax.minValue}`
            element.max = `${ax.maxValue}`
            element.step = (ax.suffix) ? "0.5" : "1"
        } else {
            if (primaryAxValueInput.current && secondaryAxValueInput.current) {
                if (primaryAxValueInput.current == element) {
                    primaryAxValueInput.current.disabled = true
                    primaryAxValueInput.current.placeholder = ''   
                }

                secondaryAxValueInput.current.disabled = true
                secondaryAxValueInput.current.placeholder = ''
            }
        }
    }

    return (
        <div className="AXSelect">
            <div className="AXSet">
                <div className="fields">
                    <select key="ax1" defaultValue={ (props.currentSkills && props.currentSkills[0]) ? props.currentSkills[0].modifier : -1 } onChange={handleSelectChange} ref={primaryAxModifierSelect}>{ generateOptions(0) }</select>
                    <input defaultValue={ (props.currentSkills && props.currentSkills[0]) ? props.currentSkills[0].strength : 0 } className="Input" type="number" onChange={handleInputChange} ref={primaryAxValueInput} disabled={primaryAxValue != 0} />
                </div>
                <p className={primaryErrorClasses}>{errors.axValue1}</p>
            </div>

            <div className={secondarySetClasses}>
                <div className="fields">
                    <select key="ax2" defaultValue={ (props.currentSkills && props.currentSkills[1]) ? props.currentSkills[1].modifier : -1 } onChange={handleSelectChange} ref={secondaryAxModifierSelect}>{ generateOptions(1) }</select>
                    <input defaultValue={ (props.currentSkills && props.currentSkills[1]) ? props.currentSkills[1].strength : 0 } className="Input" type="number" onChange={handleInputChange} ref={secondaryAxValueInput} disabled={secondaryAxValue != 0} />
                </div>
                <p className={secondaryErrorClasses}>{errors.axValue2}</p>
            </div>
        </div>
    )
}

export default AXSelect