import * as React from 'react'

import './LoginField.css'

enum FieldError {
    REQUIRED = 'Required',
    INVALID = 'Invalid Value'
}

type LoginFieldProps = {
    password: boolean
}

type LoginFieldSettings = {
    errorText: FieldError,
    hasError: boolean,
    shake: boolean,
    value: string
}

export default class LoginField extends React.Component<LoginFieldProps, LoginFieldSettings> {

    private readonly USERNAME_REGEX = /^[a-zA-Z0-9_]{1,16}$/
    private readonly BASIC_EMAIL_REGEX = /^\S+@\S+\.\S+$/
    // private readonly VALID_EMAIL_REGEX = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i

    private readonly SHAKE_CLASS = 'shake'

    private errorSpanRef: React.RefObject<HTMLSpanElement>

    constructor(props: LoginFieldProps) {
        super(props)
        this.state = {
            errorText: FieldError.REQUIRED,
            hasError: true,
            shake: false,
            value: ''
        }
        this.errorSpanRef = React.createRef()
    }

    componentDidUpdate() {
        if(this.state.hasError) {
            // @ts-ignore Opacity is a number, not a string..
            this.errorSpanRef.current!.style.opacity = 1
            if(this.state.shake) {
                this.errorSpanRef.current!.classList.remove(this.SHAKE_CLASS)
                void this.errorSpanRef.current!.offsetWidth
                this.errorSpanRef.current!.classList.add(this.SHAKE_CLASS)
            }
        } else {
            // @ts-ignore Opacity is a number, not a string..
            this.errorSpanRef.current!.style.opacity = 0
        }
    }

    private getFieldSvg(): JSX.Element {
        if(this.props.password) {

            return (
                <>
                    <svg className="loginSVG" viewBox="40 32 60.36 70.43">
                        <g>
                            <path d="M86.16,54a16.38,16.38,0,1,0-32,0H44V102.7H96V54Zm-25.9-3.39a9.89,9.89,0,1,1,19.77,0A9.78,9.78,0,0,1,79.39,54H60.89A9.78,9.78,0,0,1,60.26,50.59ZM70,96.2a6.5,6.5,0,0,1-6.5-6.5,6.39,6.39,0,0,1,3.1-5.4V67h6.5V84.11a6.42,6.42,0,0,1,3.39,5.6A6.5,6.5,0,0,1,70,96.2Z"/>
                        </g>
                    </svg>
                </>
            )

        } else {

            return (
                <>
                    <svg className="loginSVG" viewBox="40 37 65.36 61.43">
                        <g>
                            <path d="M86.77,58.12A13.79,13.79,0,1,0,73,71.91,13.79,13.79,0,0,0,86.77,58.12M97,103.67a3.41,3.41,0,0,0,3.39-3.84,27.57,27.57,0,0,0-54.61,0,3.41,3.41,0,0,0,3.39,3.84Z"/>
                        </g>
                    </svg>
                </>
            )
            
        }
    }

    private formatError(error: FieldError): string {
        return `* ${error}`
    }

    private getErrorState(shake: boolean, errorText: FieldError): Partial<LoginFieldSettings> {
        return {
            shake,
            errorText,
            hasError: true,
        }
    }

    private getValidState(): Partial<LoginFieldSettings> {
        return {
            hasError: false
        }
    }

    private validateEmail = (value: string, shakeOnError: boolean): void => {
        let newState
        if(value) {
            if(!this.BASIC_EMAIL_REGEX.test(value) && !this.USERNAME_REGEX.test(value)) {
                newState = this.getErrorState(shakeOnError, FieldError.INVALID)
            } else {
                newState = this.getValidState()
            }
        } else {
            newState = this.getErrorState(shakeOnError, FieldError.REQUIRED)
        }
        this.setState({
            ...this.state,
            ...newState,
            value
        })
    }

    private validatePassword = (value: string, shakeOnError: boolean): void => {
        let newState
        if(value) {
            newState = this.getValidState()
        } else {
            newState = this.getErrorState(shakeOnError, FieldError.REQUIRED)
        }
        this.setState({
            ...this.state,
            ...newState,
            value
        })
    }

    private getValidateFunction(): (value: string, shakeOnError: boolean) => void {
        return this.props.password ? this.validatePassword : this.validateEmail
    }

    private handleBlur = (event: React.FocusEvent<HTMLInputElement>): void => {
        this.getValidateFunction()(event.target.value, true)
    }

    private handleInput = (event: React.FormEvent<HTMLInputElement>): void => {
        this.getValidateFunction()((event.target as HTMLInputElement).value, false)
    }

    render() {
        return (
            <>
                <div className="loginFieldContainer">
                    {this.getFieldSvg()}
                    <span
                        className="loginErrorSpan"
                        ref={this.errorSpanRef}>
                        {this.formatError(this.state.errorText)}
                    </span>
                    <input 
                        className="loginField"
                        type={this.props.password ? 'password' : 'text'}
                        value={this.state.value}
                        placeholder={this.props.password ? 'PASSWORD' : 'EMAIL OR USERNAME'}
                        onBlur={this.handleBlur}
                        onInput={this.handleInput} />
                </div>
            </>
        )
    }

}