import { AxiosError, AxiosInstance, AxiosResponse } from "axios";
import { persianStringToEnglishString } from "nums2persian";
import { EnumValidateState } from "../../enums";
import { ElementFactory } from "../../Page/ElementsOfFormFactory/ElementFactory";
import { StructureCoding } from "../../StructureCoding";
import { IMainStateFactory } from "../../Types";
import { Cach } from "../Cach";
import { TREECODE } from "amisa-paths";
import { useAxios, useUrls } from "../../AmisaAuth/Models/StorageManager/TokenInfo";
import { SettingInfo, useSettingInfo } from "../../AmisaAuth/Models/SettingInfo";

export type IResultOfValidate = true | [string, EnumValidateState];
export type IStructuralCodeState =
    | 'clear'
    | 'ok'
    | 'dirty'
    | 'withoutParent'
    | 'newCode';

export type IStructuralTypeInitilaValue = { name: string, fullName: IFullName[], childCount: number, id?: number, codeValueState: IStructuralCodeState, code: string, text: string };

export type IStructuralTypeOfCode = 'hesab' | 'markaz1' | 'markaz2' | 'markaz3' | 'kala';

export type IStructuralCodeUseCaseType = 'MainPageCode' | 'Filter' | 'SelectCode';

interface IGetNewCodeResponse {
    massageRoot?: string,
    isSuccess: boolean,
    newCode?: string,
}
export interface IFullName {
    id: number;
    code: string;
    name: string;
}

export class StructrulCodeFactory extends ElementFactory {
    public forceUpdate = () => { };

    public urls: TREECODE;
    public apiAxios: AxiosInstance;

    public get any(): any {
        return this;
    }

    public focusToElement = () => {
        this.mainStateFactory.elementsOfForm.focuseToThisElement(this);
        this.element?.focus();
    }

    public showTreeView?: () => void;
    public showListView?: (value?: string) => void;

    private refreshAllfromOfMainCode(_data: any) {
        if (this.useCaseType === 'MainPageCode' &&
            this.mainStateFactory.elementsOfForm.responses &&
            this.mainStateFactory.elementsOfForm.responses.length > 0) {
            this.mainStateFactory.elementsOfForm.responses.forEach((element: ElementFactory) => {
                element.deseriallize(_data[element.responseKey!]);
            });
            const forceUpdate = this.mainStateFactory.any.forceUpdate;
            forceUpdate && forceUpdate();
        }
    }

    private setValidationState = (result: any): void => {
        this.codeValueState = result === null ? 'ok'
            : result === 0 ? 'newCode'
                : result === 1 ? 'dirty'
                    : result === 2 ? 'withoutParent'
                        : 'clear';
    }

    public codeWaitForLoad: null | string = null;
    private timerForLoad: NodeJS.Timeout | null = null;
    private clearTimerForLoad = () => {
        if (this.timerForLoad) {
            clearTimeout(this.timerForLoad);
        }
    };
    public getNewCode = () => {
        if (this.code === '') {
            this.mainStateFactory.elementsOfForm.showInvalidArgumentMessageBox('لطفا یک کد در سطح مورد نظر انتخاب کنید');
            console.error('code in the same level did not choose');
        } else {
            this.validate();
            if (this.validation !== true) {
                this.mainStateFactory.elementsOfForm.showInvalidArgumentMessageBox(this.validation[0]);
                console.error(this.validation[0]);
            } else {
                this.getNewCodeData(this.code);
            }
        }
    }

    private setCachedData = () => {
        Cach.setValue(this, { fullName: this.fullName, childCount: this.childCount, name: this.name, id: this.id, codeValueState: this.codeValueState, code: this.code, text: this.text });
    }
    private getNewCodeData(currentCode: string) {
        this.mainStateFactory.elementsOfForm.showWaitingFormSpinner();
        this.apiAxios.get<IGetNewCodeResponse>(`${this.urls.new}/${currentCode}`)
            .then((response): void => {
                if (response.data.isSuccess) {
                    this.onChangeHandlerCode(response.data.newCode || '');
                    ///0 = new code
                    this.setValidationState(0)
                    this.childCount = 0;
                    this.validate();
                    this.setCachedData();
                    this.refreshAllfromOfMainCode({});
                    this.codeWaitForLoad = null;
                    this.forceUpdate();
                } else {
                    this.mainStateFactory.elementsOfForm.showInvalidArgumentMessageBox(response.data.massageRoot || 'some thing went wrong');
                }

            })
            .catch((error: AxiosError): void => {
                console.error(error);
                this.forceUpdate();
            })
            .finally(() => {
                this.mainStateFactory.elementsOfForm.closeWaitingFormSpinner();
            });

    }
    public getCodeData = () => {
        this.clearTimerForLoad();

        this.timerForLoad = setTimeout(() => {
            this.codeWaitForLoad = this.code;
            this.getCodeDataAxios();
        }, 500)

    }
    private getCodeDataAxios = () => {
        if (this.codeWaitForLoad === '') {
            this.clearData();
            return;
        } else if (this.codeValueState === 'dirty') {
            this.fullName = [];
            this.childCount = 0;
            this.codeWaitForLoad = null;
            this.validate();
            this.setCachedData();
            this.refreshAllfromOfMainCode({});
            this.forceUpdate();
            return;
        }

        this.mainStateFactory.elementsOfForm.showWaitingFormSpinner();
        this.forceUpdateSpinner();
        this.apiAxios.get(`${this.urls.get}/${this.codeWaitForLoad}`)
            .then((response: AxiosResponse): void => {
                this.fullName = response.data.data.fullName;
                this.name = response.data.data.name;
                this.setValidationState(response.data.result);
                this.childCount = response.data.data.childCount;
                this.validate();
                this.setCachedData();
                this.refreshAllfromOfMainCode(response.data.data);
                this.codeWaitForLoad = null;
                this.forceUpdate();
            })
            .catch((error: AxiosError): void => {
                console.error(error)
                this.forceUpdate();
            })
            .finally(() => {
                this.mainStateFactory.elementsOfForm.closeWaitingFormSpinner();
            });


    }
    public clearData = () => {
        Cach.clear(this);
        this.#code = null;
        this.#text = null;
        this._defaultValue = null;

        this.fullName = [];
        this.codeWaitForLoad = null;

        this.name = '';
        this.codeValueState = 'clear';
        this.childCount = 0;
        this.validation = true;
        this.refreshAllfromOfMainCode({});
    }

    public id: number | null = null;
    public fullName: IFullName[] = [];
    public name: string = '';

    ///state that get from server data
    public codeValueState: IStructuralCodeState = 'clear';

    public childCount = 0;

    validate = (): IResultOfValidate => {
        if (this.required && !this.code) {
            // 'namingNotImplement'
            this.validation = [`${this.caption} وارد نشده است`, EnumValidateState.empty];
            return this.validation;
        } else if (this.codeValueState === 'dirty') {
            this.validation = [`طول ${this.caption} وارد شده معتبر نیست`, EnumValidateState.invalidLength];
            return this.validation;
        } else if (this.codeValueState === 'withoutParent') {
            this.validation = [`${this.caption} وارد شده سرفصل بالاتر ندارد`, EnumValidateState.withoutParent];
            return this.validation;
        } else if (this.onlyLastLevel && this.codeValueState === 'newCode') {
            this.validation = [`${this.caption} وارد شده وجود ندارد`, EnumValidateState.withoutParent];
            return this.validation;
        } else if (this.onlyLastLevel && this.childCount > 0) {
            this.validation = [`${this.caption} وارد شده سرفصل جزء دارد`, EnumValidateState.hasChildren];
            return this.validation;
        }
        this.validation = true;
        return this.validation;
    }

    #code: string | null = null;
    #text: string | null = null;
    get text(): string {
        return this.#text || '';
    }
    setText(text: string) {
        this.#text = text;
        this.setCachedData();
        this.forceUpdate();
    }

    public get value() {
        return this.#code;
    }

    get code(): string {
        return this.#code || '';
    }
    setValue(code: string) {
        this.#code = code;
        this.setCachedData();
        this.forceUpdate();
    }
    public deseriallize = (e?: string | null) => { }

    private _defaultValue: null | string = null;

    restartDefaultValue = () => {
        this._defaultValue = this.code;
        this.refreshHasChange();
    }
    refreshHasChange = () => {
        if (this.showHasChangeFlag) {
            this._hasChange = this._defaultValue !== this.#code;
        }
    }

    public settingInfo: SettingInfo;
    public coding: StructureCoding;
    public placeholder: string;
    public maxLength: number;
    constructor(
        _mainStateFactory: IMainStateFactory,
        _fieldName: string,
        _dispose: () => void,
        public useCaseType: IStructuralCodeUseCaseType,
        public required: boolean,
        public onlyLastLevel: boolean,
        public caption: string,
        public typeOfCode: IStructuralTypeOfCode,
        public showHasChangeFlag: boolean,
        public tabIndex?: number,
        initialValue?: IStructuralTypeInitilaValue,
        payLoadKey?: string,
        responseKey?: string,
        history?: {
            key: string,
            axios: (controllerPath: string) => AxiosInstance,
            controllerPath: string,
            actionPath?: string,
        },
        public onChange?: ((dateBoxFactory: StructrulCodeFactory) => void)
    ) {
        super(_mainStateFactory, _fieldName, _dispose, payLoadKey, responseKey, history);
        this.settingInfo = useSettingInfo();

        switch (typeOfCode) {
            case "hesab":
                this.urls = useUrls('BaseCode', 'Hesab');
                this.coding = this.settingInfo.codingHesab.getStructurCoding();
                break;
            case "kala":
                this.urls = useUrls('BaseCode', 'Kala');
                this.coding = this.settingInfo.codingKala.getStructurCoding();
                break;
            case "markaz1":
                this.urls = useUrls('BaseCode', 'Markaz1');
                this.coding = this.settingInfo.codingMarkaz1.getStructurCoding();
                break;
            case "markaz2":
                this.urls = useUrls('BaseCode', 'Markaz2');
                this.coding = this.settingInfo.codingMarkaz2.getStructurCoding();
                break;
            case "markaz3":
                this.urls = useUrls('BaseCode', 'Markaz3');
                this.coding = this.settingInfo.codingMarkaz3.getStructurCoding();
                break;
            default:
                throw new Error(`type of code : '${typeOfCode}' not implemented`);
        }
        this.apiAxios = useAxios(this.urls);

        if (typeof initialValue === 'object') {
            this.name = initialValue.name;
            this.fullName = initialValue.fullName;
            this.childCount = initialValue.childCount;
            this.id = initialValue.id || 0;
            this.codeValueState = initialValue.codeValueState;
            this.#code = initialValue.code;
            this.#text = initialValue.text;
        }
        this.placeholder = this.getPlaceholder();
        this.maxLength = this.getMaxLength();

        if (Cach.isCached(this)) {
            const cach = Cach.getCached(this);
            this.name = cach.value.name;
            this.fullName = cach.value.fullName;
            this.childCount = cach.value.childCount;
            this.id = cach.value.id;
            this.codeValueState = cach.value.codeValueState;
            this.#code = cach.value.code;
            this.#text = cach.value.text;
            this.validation = cach.validation;
            this._hasChange = cach.hasChange;
        }
    }

    public forceUpdateSpinner = () => { }

    private getMaxLength(): number {
        return this.coding.maxCodingLen + this.coding.maxLevel - 1
    }

    public onChangeHandlerElement = (element: HTMLInputElement) => {
        const cursor = element.selectionEnd || 0;
        const valueNotPersian = element.value;
        this.onChangeHandlerCode(valueNotPersian, cursor);

    }

    public onChangeHandlerCode = (code: string, cursorPosition?: number) => {
        const cursor = cursorPosition || code.length;
        const value = persianStringToEnglishString(code, false);
        this.#code = value;

        this.codeValueState = this.coding.changeEventOfCode(this.code).state;

        const result = value.split('');
        const resultLength = result.length;

        for (let index = 0; index < resultLength; index++) {
            if (index < cursor && index < this.coding.maxCodingLen) {
                if (index === this.coding.untilLevelLen9 && this.coding.levelLen9) {
                    result.splice(this.coding.untilLevelLen9 + 8, 0, '-');
                } else if (index === this.coding.untilLevelLen8 && this.coding.levelLen8) {
                    result.splice(this.coding.untilLevelLen8 + 7, 0, '-');
                } else if (index === this.coding.untilLevelLen7 && this.coding.levelLen7) {
                    result.splice(this.coding.untilLevelLen7 + 6, 0, '-');
                } else if (index === this.coding.untilLevelLen6 && this.coding.levelLen6) {
                    result.splice(this.coding.untilLevelLen6 + 5, 0, '-');
                } else if (index === this.coding.untilLevelLen5 && this.coding.levelLen5) {
                    result.splice(this.coding.untilLevelLen5 + 4, 0, '-');
                } else if (index === this.coding.untilLevelLen4 && this.coding.levelLen4) {
                    result.splice(this.coding.untilLevelLen4 + 3, 0, '-');
                } else if (index === this.coding.untilLevelLen3 && this.coding.levelLen3) {
                    result.splice(this.coding.untilLevelLen3 + 2, 0, '-');
                } else if (index === this.coding.untilLevelLen2 && this.coding.levelLen2) {
                    result.splice(this.coding.untilLevelLen2 + 1, 0, '-');
                } else if (index === this.coding.untilLevelLen1 && this.coding.levelLen1) {
                    result.splice(this.coding.untilLevelLen1, 0, '-');
                }
            }
        }
        this.setText(result.join(''));
        this.onChange && this.onChange(this);
        this.getCodeData();
    }
    private getPlaceholder = (): string => {
        const levels: string[] = [];
        if (this.coding.levelLen1) levels.push(''.padEnd(this.coding.levelLen1, '#'));
        if (this.coding.levelLen2) levels.push(''.padEnd(this.coding.levelLen2, '#'));
        if (this.coding.levelLen3) levels.push(''.padEnd(this.coding.levelLen3, '#'));
        if (this.coding.levelLen4) levels.push(''.padEnd(this.coding.levelLen4, '#'));
        if (this.coding.levelLen5) levels.push(''.padEnd(this.coding.levelLen5, '#'));
        if (this.coding.levelLen6) levels.push(''.padEnd(this.coding.levelLen6, '#'));
        if (this.coding.levelLen7) levels.push(''.padEnd(this.coding.levelLen7, '#'));
        if (this.coding.levelLen8) levels.push(''.padEnd(this.coding.levelLen8, '#'));
        if (this.coding.levelLen9) levels.push(''.padEnd(this.coding.levelLen9, '#'));
        return levels.join('  -  ');
    }
}

