import util from '../util/index.ts';
import {get} from './index.ts';

export type ApiModelPropertyType =
    'TEXT'
    | 'EMAIL'
    | 'URL'
    | 'INTEGER'
    | 'DECIMAL'
    | 'BOOLEAN'
    | 'DATE'
    | 'TIME'
    | 'DATETIME'
    | 'OPTION'

export type EnumItem = {
    key: string;
    caption: string;
    ordinal: number;
    disabled?: boolean;
    searchIndex?: string;
    attachment?: Record<string, string>;
}

export type EnumType = {
    name: string;
    subname?: string;
    items: EnumItem[];
}

export type ApiModelPropertyMeta = {
    caption: string;
    type: ApiModelPropertyType;
    validation?: Record<string, any>;
    enums?: EnumItem[];
}

export type ApiModelMeta = Record<string, ApiModelPropertyMeta>;

export type Region = {
    level: number;
    code: string;
    caption: string;
    suffix: string;
    group?: string;
    subs?: Region[];
}

const metas: Record<string, ApiModelMeta> = {}

const enumTypeMapping: Record<string, EnumType> = {}

const regionMapping: Record<string, Region> = {}

export async function getMeta(urlOrType: string): Promise<ApiModelMeta> {
    if (metas[urlOrType]) {
        return metas[urlOrType];
    } else {
        let url = '/api/meta/' + (urlOrType.includes('/') ? 'method' : 'model');
        let params = urlOrType.includes('/') ? {url: urlOrType} : {type: urlOrType};
        let meta = await get<ApiModelMeta>(url, params);
        if (meta) {
            metas[urlOrType] = meta;
        }
        return meta;
    }
}

function getEnumTypeMappingKey(type: string, subType?: string): string {
    return subType ? (type + '-' + subType) : type;
}

export function addEnumType(enumType: EnumType): void {
    const mappingKey = getEnumTypeMappingKey(enumType.name, enumType.subname);
    if (!enumTypeMapping[mappingKey]) {
        enumTypeMapping[mappingKey] = enumType;
    }
}

export async function resolveEnumType(name: string, subname?: string): Promise<EnumType> {
    const mapping = enumTypeMapping;
    const mappingKey = getEnumTypeMappingKey(name, subname);
    if (mapping[mappingKey]) {
        return mapping[mappingKey];
    } else {
        const enumType = await get<EnumType>('/api/meta/enum/type', {name, subname});
        mapping[mappingKey] = enumType;
        return enumType;
    }
}

export async function resolveEnumItems(type: string, subType?: string): Promise<EnumItem[] | null> {
    const enumType = await resolveEnumType(type, subType);
    // 返回深度克隆的选项集，以免调用者改动选项影响缓存数据
    return (enumType?.items) ? enumType.items.clone(true) : null;
}

export async function resolveEnumItem(type: string, subType: string | null, key: string): Promise<EnumItem | null> {
    if (!key) {
        return null;
    }
    let items = getCachedEnumItems(type, subType);
    if (!items) {
        items = await resolveEnumItems(type, subType);
    }
    return getEnumItem(items, key);
}

function getCachedEnumItems(type: string, subtype?: string): EnumItem[] | null {
    const mappingKey = getEnumTypeMappingKey(type, subtype);
    let enumType = enumTypeMapping[mappingKey];
    return enumType ? enumType.items : null;
}

function getEnumItem(items: EnumItem[] | null, key: string): EnumItem | null {
    if (Array.isArray(items)) {
        for (let item of items) {
            if (item.key === key) {
                return item;
            }
        }
    }
    return null;
}

export async function resolveEnumCaption(type: string, subtype: string | null, key: string): Promise<string> {
    let item = await resolveEnumItem(type, subtype, key);
    return item ? item.caption : '';
}

/**
 * 当可确定已缓存指定枚举类型时，获取指定枚举项的显示名称
 * @param type 枚举类型名
 * @param subtype 子类型名
 * @param key 枚举项的键
 * @return 指定枚举项的显示名称，没找到时返回null
 */
export function getEnumCaption(type: string, subtype: string | null, key: string): string | null {
    let items = getCachedEnumItems(type, subtype);
    if (items) {
        for (let item of items) {
            if (item.key === key) {
                return item.caption;
            }
        }
    }
    return null;
}

/**
 * 当可确定已缓存指定枚举类型时，获取指定枚举项的键
 * @param type 枚举类型名
 * @param subtype 子类型名
 * @param caption 枚举项的显示名称
 * @return {*|null} 指定枚举项的键，没找到时返回null
 */
export function getEnumKey(type: string, subtype: string | null, caption: string): string | null {
    let items = getCachedEnumItems(type, subtype);
    if (items) {
        for (let item of items) {
            if (item.caption === caption) {
                return item.key;
            }
        }
    }
    return null;
}

export function clearEnumTypeCache(name: string, subname?: string): void {
    const mappingKey = getEnumTypeMappingKey(name, subname);
    delete enumTypeMapping[mappingKey];
}

export function getRandomEnumItemKey(type: string, onlyDev: boolean = true): string | null {
    if (onlyDev && util.build.isProduction()) {
        return null;
    }
    let items = getCachedEnumItems(type);
    if (items && items.length) {
        let index = util.number.randomInt(0, items.length - 1);
        return items[index].key;
    }
    return null;
}

export async function loadRegion(regionCode: string, level: number = 3): Promise<Region> {
    level = Math.min(level, 3);
    let cacheKey = regionCode + '.' + level;
    let region = regionMapping[cacheKey];
    if (!region) {
        region = await get<Region>('/api/region/' + regionCode);
        filterRegionSubs(region, level);
        regionMapping[cacheKey] = region;
    }
    return region;
}

function filterRegionSubs(region: Region, level: number) {
    if (region.subs) {
        if (region.level >= level) {
            delete region.subs;
        } else {
            for (let sub of region.subs) {
                filterRegionSubs(sub, level);
            }
        }
    }
}
