import '@scss/gateway/login.scss';

import $ from 'jquery';
import { JQValidateForm, type NamedRules } from '@util/jqValidateForm';
import axios from 'axios';
import { convertFormData } from '@util/convertFormData';
import { setAxiosXMLHttpRequest } from '@util/setAxiosXMLHttpRequest';
import { unwrap_any } from '@util/unwrap_any';
import { sha512 } from 'js-sha512';
import { unwrap } from '@util/unwrap';
import { delay } from '@util/delay';
import { Modal } from 'bootstrap';
import '@/gateway/common';
import { isString } from 'lodash-es';
import { SammoRootAPI, type InvalidResponse } from '@/SammoRootAPI';
import type { LoginFailed, LoginResponse, LoginResponseWithKakao, OTPResponse } from '@/defs/API/Login';
import { classifyAutoLoginFailure, OTP_REQUIRED_MESSAGE } from '@/gateway/loginFlow';
declare global {
    interface Window {
        getOAuthToken: (mode: string, scope_list: string[]) => void;
        postOAuthResult: (mode: string) => void;
    }
}

declare const kakao_oauth_client_id: string;
declare const kakao_oauth_redirect_uri: string;

let modalOTP: Modal | undefined = undefined;

let oauthMode: string | null = null;

const TOKEN_VERSION = 1;
const LOGIN_TOKEN_KEY = 'sammo_login_token';
function regNextToken(tokenInfo: [number, string]) {
    localStorage.setItem(LOGIN_TOKEN_KEY, JSON.stringify([TOKEN_VERSION, tokenInfo, Date.now()]));
}

function getToken(): [number, string] | undefined {
    const trialToken = localStorage.getItem(LOGIN_TOKEN_KEY);
    if (!trialToken) {
        console.log('no token');
        return;
    }
    const tokenItems = JSON.parse(trialToken) as [number, [number, string], number];
    if (tokenItems[0] != TOKEN_VERSION) {
        console.log(tokenItems);
        resetToken();
        return;
    }
    console.debug(localStorage.getItem(LOGIN_TOKEN_KEY));
    const [, token,] = tokenItems;
    return token;
}

function resetToken() {
    localStorage.removeItem(LOGIN_TOKEN_KEY);
}

function showOTPModal() {
    const modalEl = unwrap(document.querySelector('#modalOTP'));
    if (!modalOTP) {
        modalOTP = new Modal(modalEl);
        modalEl.addEventListener('shown.bs.modal', function () {
            unwrap(document.querySelector<HTMLElement>('#otp_code')).focus();
        });
    }
    modalOTP.show();
}

async function tryAutoLogin() {
    try {
        const tokenInfo = getToken();
        if (!tokenInfo) {
            return;
        }

        const [tokenID, token] = tokenInfo;

        for (let attempt = 0; attempt < 2; attempt++) {
            const reqNonceStartAt = Date.now();
            const nonceResult = await SammoRootAPI.Login.ReqNonce(undefined, true);

            if (!nonceResult) {
                //api 에러.
                return;
            }

            if (!nonceResult.result) {
                resetToken();
                return;
            }

            const nonce = nonceResult.loginNonce;
            console.debug(
                'try auto login with token',
                tokenID,
                `attempt:${attempt + 1}`,
                `reqNonceElapsed:${Date.now() - reqNonceStartAt}ms`
            );

            const hashedToken = sha512(token + nonce);
            const loginResult = await SammoRootAPI.Login.LoginByToken({
                'hashedToken': hashedToken,
                'token_id': tokenID,
            }, true);

            if (!loginResult.result) {
                const failureAction = classifyAutoLoginFailure(loginResult, attempt);
                if (failureAction === 'retry') {
                    console.warn('auto login failed by procedure error. retrying once.');
                    await delay(150);
                    continue;
                }
                if (failureAction === 'prompt_otp') {
                    alert(OTP_REQUIRED_MESSAGE);
                    showOTPModal();
                    return;
                }
                if (failureAction === 'alert') {
                    alert(loginResult.reason);
                }
                console.error(loginResult.reason);
                return;
            }

            if (loginResult.nextToken) {
                regNextToken(loginResult.nextToken);
            }
            window.location.href = "./";
            return;
        }
    }
    catch (e) {
        if (isString(e)) {
            alert(e);
        }
        console.error(e);
        return;
    }
}

function getOAuthToken(mode: string, scope_list?: string[] | string) {
    if (mode === undefined) {
        mode = 'login';
    }
    oauthMode = mode;
    let url = `https://kauth.kakao.com/oauth/authorize?client_id=${kakao_oauth_client_id}&redirect_uri=${kakao_oauth_redirect_uri}&response_type=code`;
    if (Array.isArray(scope_list)) {
        url += `&scope=${scope_list.join(',')}`;
    } else if (typeof scope_list === 'string') {
        url += `&scope=${scope_list}`;
    }

    window.open(url, "KakaoAccountLogin", "width=600,height=450,resizable=yes,scrollbars=yes");
}
window.getOAuthToken = getOAuthToken;

async function sendTempPasswordToKakaoTalk(): Promise<void> {

    let result: InvalidResponse;

    try {
        const response = await axios({
            url: 'oauth_kakao/j_login_oauth.php',
            responseType: 'json',
            method: 'post',
        });
        result = response.data;
    }
    catch (e) {
        console.error(e);
        alert(`실패했습니다_login: ${e}`);
        return;
    }

    if (!result.result) {
        alert(result.reason);
        return;
    }

    try {
        const response = await axios({
            url: 'oauth_kakao/j_change_pw.php',
            responseType: 'json',
            method: 'post',
        });
        result = response.data;
    }
    catch (e) {
        console.error(e);
        alert(`실패했습니다_pw: ${e}`);
        return;
    }

    alert('임시 비밀번호가 카카오톡으로 전송되었습니다.');
}

async function doLoginUsingOAuth() {
    let result: LoginResponseWithKakao;

    try {
        const response = await axios({
            url: 'oauth_kakao/j_login_oauth.php',
            responseType: 'json',
            method: 'post',
        });
        result = response.data;
    }
    catch (e) {
        console.error(e);
        alert(`실패했습니다_login: ${e}`);
        return;
    }

    if (result.result) {
        if (result.nextToken) {
            regNextToken(result.nextToken);
        }
        window.location.href = "./";
        return;
    }

    if (!result.reqOTP) {
        alert(result.reason);
        return;
    }

    showOTPModal();
}

function postOAuthResult(mode: string) {
    if (mode == 'join') {
        window.location.href = 'oauth_kakao/join.php';
        return;
    }

    if (mode == 'req_email') {
        alert('이메일 정보 공유를 허가해 주셔야 합니다.');
        return;
    }

    if (mode == 'login') {
        console.log('로그인모드');
        if (oauthMode == 'change_pw') {
            void sendTempPasswordToKakaoTalk();
        } else {
            void doLoginUsingOAuth();
        }
        return;
    }

    alert('예외 발생!');
}
window.postOAuthResult = postOAuthResult;


$(async function ($) {
    setAxiosXMLHttpRequest();

    //로그인 먼저 해볼 것
    if (getToken()) {
        void tryAutoLogin();
        await delay(100);
    }


    type LoginFormType = {
        username: string,
        password: string,
    };
    const descriptor: NamedRules<LoginFormType> = {
        username: {
            required: true,
            type: 'string',
            message: '유저명을 입력해주세요',
        },
        password: {
            required: true,
            type: 'string',
            message: '비밀번호를 입력해주세요'
        }
    };

    const validator = new JQValidateForm($('#main_form'), descriptor);
    validator.installChangeHandler();

    $("#main_form").on('submit', async function (e) {
        e.preventDefault();

        const values = await validator.validate();
        if (values === undefined) {
            return;
        }
        const raw_password = values.password;
        const salt = unwrap_any<string>($('#global_salt').val());
        const hash_pw = sha512(salt + raw_password + salt);

        let result: LoginResponse | LoginFailed;

        try {
            result = await SammoRootAPI.Login.LoginByID({
                username: values.username,
                password: hash_pw,

            }, true);
        }
        catch (e) {
            console.error(e);
            alert(`실패했습니다: ${e}`);
            return;
        }


        if (result.result) {
            if (result.nextToken) {
                regNextToken(result.nextToken);
            }
            window.location.href = "./";
            return;
        }

        if (!result.reqOTP) {
            alert(result.reason);
            return;
        }

        showOTPModal();
    });

    $('#otp_form').on('submit', async function (e) {
        e.preventDefault();

        let result: OTPResponse;

        try {
            const response = await axios({
                url: 'oauth_kakao/j_check_OTP.php',
                responseType: 'json',
                method: 'post',
                data: convertFormData({
                    otp: unwrap_any<string>($('#otp_code').val()),
                })
            });
            result = response.data;
        }
        catch (e) {
            console.error(e);
            alert(`실패했습니다: ${e}`);
            return;
        }

        if (result.result) {
            alert(`로그인되었습니다. ${result.validUntil}까지 유효합니다.`);
            window.location.href = "./";
            return;
        }

        alert(result.reason);

        if (result.reset) {
            if (modalOTP) {
                modalOTP.hide();
            }
            return;
        }
    });

    $('#oauth_change_pw').on('click', function (e) {
        e.preventDefault();
        getOAuthToken('change_pw', 'talk_message');
    });

    $('#btn_kakao_login').on('click', function (e) {
        e.preventDefault();
        getOAuthToken('login', ['account_email', 'talk_message']);
    })

    //TODO: 모바일에서 크기 비례 지도 다시 띄우기?
    /*
    if (document.body.clientWidth < 700) {
        const targetWidth = document.body.clientWidth * 0.9;
        const scale = targetWidth / 700;
        const $map = $('#running_map');
        $map.find('.col').css('max-width', targetWidth);
        $map.find('.card').css('width', targetWidth);
        $map.find('.map-container').css({
            'transform-origin': 'top left',
            'transform': `scale(${scale}, ${scale})`,
            'height': 500 * scale,
        });
        $map.find('.map_body').data('scale', scale);
    }*/
});

window.fitIframe = function () {
    const iframe = unwrap(document.querySelector<HTMLIFrameElement>('#running_map'));//TODO: 근황 여러개 볼 수 있도록?
    const scrollHeight = unwrap(iframe.contentWindow).document.body.scrollHeight;
    iframe.style.height = `${scrollHeight}px`;
}
