import SHA256 from 'crypto-js/sha256'

type StorageRecord = Record<string, unknown>;
type ToastLine = string | { text: string; bold?: boolean };

function isStorageRecord(value: unknown): value is StorageRecord {
    return Object.prototype.toString.call(value) === '[object Object]';
}

export function $save<T>(key: string, value: T): void;
export function $save(key: StorageRecord): void;
export function $save<T>(key: string | StorageRecord, value?: T): void {
    if (isStorageRecord(key) && value === undefined) {
        for (const k in key) {
            if (Object.prototype.hasOwnProperty.call(key, k)) {
                localStorage.setItem(k, JSON.stringify(key[k]));
            }
        }
        return;
    }

    if (typeof key !== 'string') {
        return;
    }

    localStorage.setItem(key, JSON.stringify(value));
}

export function $read<T>(key: string, defaultValue: T): T;
export function $read<T>(key: string): T | null;
export function $read<T extends StorageRecord>(key: T): T;
export function $read<T>(key: string | StorageRecord, defaultValue?: T): T | StorageRecord | null {
    if (isStorageRecord(key) && defaultValue === undefined) {
        const result: StorageRecord = {};
        for (const k in key) {
            if (Object.prototype.hasOwnProperty.call(key, k)) {
                result[k] = $read(k, key[k]);
            }
        }
        return result;
    }

    if (typeof key !== 'string') {
        return defaultValue ?? null;
    }

    const value = localStorage.getItem(key);

    if (value === null) {
        return defaultValue ?? null;
    }

    try {
        return JSON.parse(value) as T;
    } catch {
        return defaultValue ?? null;
    }
}

export function $format_str(str: string, num: number) {
    if (str.length - 2 < num) {
        return str;
    }
    return str.substring(0, num) + '...';
}

let toastElement: HTMLDivElement | undefined;
let toastTimer: ReturnType<typeof window.setTimeout> | undefined;

export function $hideToast() {
    if (toastTimer) {
        window.clearTimeout(toastTimer);
        toastTimer = undefined;
    }

    if (!toastElement) {
        return;
    }

    toastElement.style.opacity = '0';
    toastElement.style.transform = 'translate(-50%, -40%)';
}

export function $toast(message?: string | ToastLine[], duration = 2000) {
    if (!message || (Array.isArray(message) && message.length === 0)) {
        return;
    }

    if (!toastElement) {
        toastElement = document.createElement('div');
        toastElement.style.cssText = [
            'position: fixed',
            'left: 50%',
            'top: 45%',
            'z-index: 99999',
            'box-sizing: border-box',
            'width: 420px',
            'padding: 28px 40px',
            'border-radius: 18px',
            'background: rgba(0, 0, 0, 0.72)',
            'color: #fff',
            'font-size: 28px',
            'line-height: 1.65',
            'text-align: center',
            'white-space: pre-line',
            'overflow-wrap: break-word',
            'transform: translate(-50%, -40%)',
            'opacity: 0',
            'pointer-events: none',
            'transition: opacity 0.2s ease, transform 0.2s ease',
        ].join(';');
        document.body.appendChild(toastElement);
    }

    if (toastTimer) {
        window.clearTimeout(toastTimer);
        toastTimer = undefined;
    }

    if (Array.isArray(message)) {
        toastElement.textContent = '';
        for (const line of message) {
            const lineElement = document.createElement('div');
            const text = typeof line === 'string' ? line : line.text;
            lineElement.textContent = text;
            if (typeof line !== 'string' && line.bold) {
                lineElement.style.fontWeight = '700';
            }
            toastElement.appendChild(lineElement);
        }
    } else {
        toastElement.textContent = message;
    }
    toastElement.style.opacity = '1';
    toastElement.style.transform = 'translate(-50%, -50%)';

    toastTimer = window.setTimeout(() => {
        if (!toastElement) {
            return;
        }

        toastElement.style.opacity = '0';
        toastElement.style.transform = 'translate(-50%, -40%)';
        toastTimer = undefined;
    }, duration);
}

export function $scoreSubmitFailedToast(duration = 10 * 60 * 1000) {
    $toast([
        '网络异常，分数提交失败，',
        '请以大屏游戏结果排行为准。',
    ], duration);
}

export function $alreadyRankedToast(duration = 10 * 60 * 1000) {
    $toast([
        { text: '一人只能上榜领奖一次', bold: true },
        '您的福利已到手，',
        '把机会留给其他玩家吧',
    ], duration);
}

type ConfirmOptions = {
    title?: string;
    message?: string;
    confirmText?: string;
    cancelText?: string;
}

let confirmMaskElement: HTMLDivElement | undefined;
let confirmBoxElement: HTMLDivElement | undefined;
let confirmTitleElement: HTMLDivElement | undefined;
let confirmMessageElement: HTMLDivElement | undefined;
let confirmCancelElement: HTMLButtonElement | undefined;
let confirmOkElement: HTMLButtonElement | undefined;
let confirmResolve: ((value: boolean) => void) | undefined;
let confirmHideTimer: ReturnType<typeof window.setTimeout> | undefined;

function closeConfirm(result: boolean) {
    if (!confirmMaskElement || !confirmBoxElement) {
        confirmResolve?.(result);
        confirmResolve = undefined;
        return;
    }

    if (confirmHideTimer) {
        window.clearTimeout(confirmHideTimer);
        confirmHideTimer = undefined;
    }

    confirmMaskElement.style.opacity = '0';
    confirmBoxElement.style.transform = 'scale(0.96)';
    confirmHideTimer = window.setTimeout(() => {
        if (confirmMaskElement) {
            confirmMaskElement.style.display = 'none';
        }
        confirmHideTimer = undefined;
    }, 200);

    confirmResolve?.(result);
    confirmResolve = undefined;
}

function ensureConfirmElement() {
    if (confirmMaskElement) {
        return;
    }

    confirmMaskElement = document.createElement('div');
    confirmMaskElement.style.cssText = [
        'position: fixed',
        'inset: 0',
        'z-index: 99998',
        'display: none',
        'align-items: center',
        'justify-content: center',
        'background: rgba(0, 0, 0, 0.45)',
        'opacity: 0',
        'transition: opacity 0.2s ease',
    ].join(';');

    confirmBoxElement = document.createElement('div');
    confirmBoxElement.style.cssText = [
        'width: min(560px, calc(100vw - 80px))',
        'box-sizing: border-box',
        'padding: 34px 32px 28px',
        'border-radius: 22px',
        'background: #fff',
        'color: #333',
        'font-size: 24px',
        'line-height: 1.45',
        'text-align: center',
        'box-shadow: 0 18px 48px rgba(0, 0, 0, 0.22)',
        'transform: scale(0.96)',
        'transition: transform 0.2s ease',
    ].join(';');

    confirmTitleElement = document.createElement('div');
    confirmTitleElement.style.cssText = [
        'margin-bottom: 16px',
        'font-size: 30px',
        'font-weight: 700',
        'color: #222',
    ].join(';');

    confirmMessageElement = document.createElement('div');
    confirmMessageElement.style.cssText = [
        'min-height: 34px',
        'word-break: break-word',
    ].join(';');

    const buttonWrap = document.createElement('div');
    buttonWrap.style.cssText = [
        'display: flex',
        'gap: 18px',
        'margin-top: 30px',
    ].join(';');

    confirmCancelElement = document.createElement('button');
    confirmCancelElement.type = 'button';
    confirmCancelElement.style.cssText = [
        'flex: 1',
        'height: 58px',
        'border: 1px solid #ddd',
        'border-radius: 29px',
        'background: #fff',
        'color: #666',
        'font-size: 24px',
    ].join(';');
    confirmCancelElement.onclick = () => closeConfirm(false);

    confirmOkElement = document.createElement('button');
    confirmOkElement.type = 'button';
    confirmOkElement.style.cssText = [
        'flex: 1',
        'height: 58px',
        'border: 0',
        'border-radius: 29px',
        'background: #AA0000',
        'color: #fff',
        'font-size: 24px',
    ].join(';');
    confirmOkElement.onclick = () => closeConfirm(true);

    confirmMaskElement.onclick = (event) => {
        if (event.target === confirmMaskElement) {
            closeConfirm(false);
        }
    };

    buttonWrap.append(confirmCancelElement, confirmOkElement);
    confirmBoxElement.append(confirmTitleElement, confirmMessageElement, buttonWrap);
    confirmMaskElement.appendChild(confirmBoxElement);
    document.body.appendChild(confirmMaskElement);
}

export function $confirm(message?: string, title?: string): Promise<boolean>;
export function $confirm(options?: ConfirmOptions): Promise<boolean>;
export function $confirm(messageOrOptions?: string | ConfirmOptions, title?: string): Promise<boolean> {
    const options: ConfirmOptions = typeof messageOrOptions === 'object'
        ? messageOrOptions
        : { message: messageOrOptions, title };

    if (!options.message) {
        return Promise.resolve(false);
    }

    ensureConfirmElement();

    if (confirmResolve) {
        confirmResolve(false);
        confirmResolve = undefined;
    }

    if (confirmHideTimer) {
        window.clearTimeout(confirmHideTimer);
        confirmHideTimer = undefined;
    }

    confirmTitleElement!.textContent = options.title ?? '提示';
    confirmMessageElement!.textContent = options.message;
    confirmCancelElement!.textContent = options.cancelText ?? '取消';
    confirmOkElement!.textContent = options.confirmText ?? '确定';
    confirmMaskElement!.style.display = 'flex';
    window.requestAnimationFrame(() => {
        if (!confirmMaskElement || !confirmBoxElement) {
            return;
        }

        confirmMaskElement.style.opacity = '1';
        confirmBoxElement.style.transform = 'scale(1)';
    });

    return new Promise<boolean>((resolve) => {
        confirmResolve = resolve;
    });
}

export function $is_run_local(): boolean {
    if (/https?:\/\/localhost|192.168/gim.test(location.href)) {
        return true;
    }
    return false;
}

function $query<T extends string | string[]>(q: T): T {
    const query = location.href.match(/\?([^#]*)/)?.[1]?.replace(/\/$/, '');
    const params = new URLSearchParams(query);
    if (Array.isArray(q)) {
        return q.map((key) => params.get(key)).filter((v): v is string => !!v) as T;
    }
    return params.get(q) as T;
}

export function $remove_socket_storage(): void {
    localStorage.removeItem('nickname');
    localStorage.removeItem('token');
    localStorage.removeItem('avatar');
    localStorage.removeItem('token_origin');
}

export function $read_socket_storge(): any | null {
    let token = $read('token', '');
    let nickname = $read('nickname', '');
    const avatar = $read('avatar', '');
    const token_origin = $read('token_origin', '');
    if (token && token_origin) {
        return { nickname, avatar, token, token_origin }
    }
    return null;
}


export function $getWechat(): any | null {
    const q = $query(['token', 'nickname', 'avatar','userno']);
    if (q && q.length == 4) {
        return {
            token: q[3],        //玩家的唯一码,不用做压缩
            token_origin: q[0],//这个token要提交给api.guocai365.org.cn做验证
            nickname: q[1],
            avatar: q[2],
        };
    }
    // 调试模式：无真实微信参数时使用模拟数据
    if (__DEBUG__) {
        const mockToken = 'debug_mock_token_' + Date.now()
        return {
            token: SHA256(mockToken).toString(),
            token_origin: mockToken,
            nickname: '测试用户',
            avatar: 'https://picsum.photos/200',
        }
    }

    return null;
}
