<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';

import DefaultLayout from '../layouts/DefaultLayout.vue';
import KakaoOtpDialog from '../components/KakaoOtpDialog.vue';
import { trpc } from '../utils/trpc';
import { sealPassword } from '../utils/passwordEnvelope';

const route = useRoute();
const router = useRouter();
const loading = ref(true);
const submitting = ref(false);
const errorMessage = ref('');
const infoMessage = ref('');
const oauthSessionId = ref('');
const passwordSetupSessionId = ref('');
const passwordSetupSuccessStatus = ref<'login' | 'verified'>('login');
const email = ref('');
const username = ref('');
const password = ref('');
const confirmPassword = ref('');
const displayName = ref('');
const termsAgreed = ref(false);
const privacyAgreed = ref(false);
const thirdPartyUse = ref(false);
const accountRecovery = ref<{
    action: 'link_existing' | 'rejoin';
    oauthSessionId: string;
    email: string;
} | null>(null);
const otpChallenge = ref<{ challengeId: string; expiresAt: string; attemptsRemaining: number } | null>(null);
const otpSuccessStatus = ref<'login' | 'verified'>('login');
const appBase = import.meta.env.BASE_URL;

const completeExchange = async (): Promise<void> => {
    const code = typeof route.query.code === 'string' ? route.query.code : '';
    const state = typeof route.query.state === 'string' ? route.query.state : '';
    if (!code || !state) {
        errorMessage.value = '카카오 인증 응답이 올바르지 않습니다.';
        loading.value = false;
        return;
    }
    try {
        const result = await trpc.auth.kakaoExchange.mutate({ code, state });
        if (result.status === 'otp') {
            otpChallenge.value = result;
            otpSuccessStatus.value = result.successStatus;
            return;
        }
        if (result.status === 'login') {
            window.localStorage.setItem('sammo-session-token', result.sessionToken);
            await router.replace('/lobby');
            return;
        }
        if (result.status === 'verified') {
            window.localStorage.setItem('sammo-session-token', result.sessionToken);
            await router.replace('/lobby?verified=1');
            return;
        }
        if (result.status === 'change_pw') {
            infoMessage.value = '카카오톡으로 임시 비밀번호를 보냈습니다.';
            return;
        }
        if (result.status === 'password_setup') {
            passwordSetupSessionId.value = result.oauthSessionId;
            passwordSetupSuccessStatus.value = result.successStatus;
            email.value = result.email;
            return;
        }
        if (result.status === 'account_recovery') {
            accountRecovery.value = result;
            email.value = result.email;
            return;
        }
        oauthSessionId.value = result.oauthSessionId;
        email.value = result.email;
    } catch (error) {
        errorMessage.value = error instanceof Error ? error.message : '카카오 인증을 완료하지 못했습니다.';
    } finally {
        loading.value = false;
    }
};

const resolveAccount = async (): Promise<void> => {
    if (!accountRecovery.value) return;
    errorMessage.value = '';
    submitting.value = true;
    try {
        const result = await trpc.auth.kakaoResolveAccount.mutate({
            oauthSessionId: accountRecovery.value.oauthSessionId,
            action: accountRecovery.value.action,
        });
        accountRecovery.value = null;
        if (result.status === 'otp') {
            otpChallenge.value = result;
            otpSuccessStatus.value = result.successStatus;
            return;
        }
        if (result.status === 'login') {
            window.localStorage.setItem('sammo-session-token', result.sessionToken);
            await router.replace('/lobby');
            return;
        }
        if (result.status === 'password_setup') {
            passwordSetupSessionId.value = result.oauthSessionId;
            passwordSetupSuccessStatus.value = result.successStatus;
            email.value = result.email;
            return;
        }
        oauthSessionId.value = result.oauthSessionId;
        email.value = result.email;
    } catch (error) {
        errorMessage.value = error instanceof Error ? error.message : '카카오 계정 연결을 완료하지 못했습니다.';
    } finally {
        submitting.value = false;
    }
};

const setMigratedPassword = async (): Promise<void> => {
    errorMessage.value = '';
    if (password.value !== confirmPassword.value) {
        errorMessage.value = '비밀번호 확인이 일치하지 않습니다.';
        return;
    }
    submitting.value = true;
    try {
        const credential = await sealPassword(password.value);
        const result = await trpc.auth.kakaoSetPassword.mutate({
            oauthSessionId: passwordSetupSessionId.value,
            credential,
        });
        password.value = '';
        confirmPassword.value = '';
        passwordSetupSessionId.value = '';
        if (result.status === 'otp') {
            otpChallenge.value = result;
            otpSuccessStatus.value = passwordSetupSuccessStatus.value;
            return;
        }
        window.localStorage.setItem('sammo-session-token', result.sessionToken);
        await router.replace(result.status === 'verified' ? '/lobby?verified=1' : '/lobby');
    } catch (error) {
        errorMessage.value = error instanceof Error ? error.message : '새 비밀번호를 설정하지 못했습니다.';
    } finally {
        submitting.value = false;
    }
};

const register = async (): Promise<void> => {
    errorMessage.value = '';
    if (password.value !== confirmPassword.value) {
        errorMessage.value = '비밀번호 확인이 일치하지 않습니다.';
        return;
    }
    if (!termsAgreed.value || !privacyAgreed.value) {
        errorMessage.value = '이용약관과 개인정보 처리방침에 동의해야 합니다.';
        return;
    }
    submitting.value = true;
    try {
        const credential = await sealPassword(password.value);
        const result = await trpc.auth.register.mutate({
            oauthSessionId: oauthSessionId.value,
            username: username.value,
            credential,
            displayName: displayName.value,
            termsAgreed: true,
            privacyAgreed: true,
            thirdPartyUse: thirdPartyUse.value,
        });
        if (result.status === 'otp') {
            otpChallenge.value = result;
            otpSuccessStatus.value = result.successStatus;
            oauthSessionId.value = '';
            return;
        }
        window.localStorage.setItem('sammo-session-token', result.sessionToken);
        await router.replace('/lobby');
    } catch (error) {
        errorMessage.value = error instanceof Error ? error.message : '회원가입에 실패했습니다.';
    } finally {
        submitting.value = false;
    }
};

const handleOtpVerified = async (sessionToken: string): Promise<void> => {
    window.localStorage.setItem('sammo-session-token', sessionToken);
    otpChallenge.value = null;
    await router.replace(otpSuccessStatus.value === 'verified' ? '/lobby?verified=1' : '/lobby');
};

onMounted(() => {
    void completeExchange();
});
</script>

<template>
    <DefaultLayout>
        <main id="oauth-container">
            <h1>삼국지 모의전투 HiDCHe</h1>
            <section class="oauth-card">
                <h2>
                    {{
                        accountRecovery
                            ? '카카오 계정 연결 확인'
                            : passwordSetupSessionId
                              ? '새 비밀번호 설정'
                              : '회원가입'
                    }}
                </h2>
                <p v-if="loading" class="oauth-message">카카오 인증을 확인하는 중...</p>
                <p v-else-if="infoMessage" class="oauth-message" role="status">{{ infoMessage }}</p>
                <div v-else-if="accountRecovery" class="recovery-panel" role="group" aria-label="카카오 계정 연결 확인">
                    <p v-if="accountRecovery.action === 'link_existing'">
                        <strong>{{ accountRecovery.email }}</strong> 이메일로 보존된 기존 계정이 있습니다. 이 계정에
                        카카오 로그인을 연결해드릴까요?
                    </p>
                    <p v-else>
                        <strong>{{ accountRecovery.email }}</strong> 이메일은 카카오에 이미 가입된 연결이 있지만 이
                        서비스에서 연결할 계정을 찾지 못했습니다. 새 계정으로 재가입하시겠습니까?
                    </p>
                    <div class="recovery-actions">
                        <button class="register-button" type="button" :disabled="submitting" @click="resolveAccount">
                            {{
                                submitting
                                    ? '처리 중...'
                                    : accountRecovery.action === 'link_existing'
                                      ? '기존 계정에 연결'
                                      : '재가입'
                            }}
                        </button>
                        <RouterLink class="back-link" to="/">취소</RouterLink>
                    </div>
                </div>
                <form
                    v-else-if="passwordSetupSessionId"
                    class="password-setup-form"
                    aria-label="새 비밀번호 설정"
                    @submit.prevent="setMigratedPassword"
                >
                    <p class="oauth-message">
                        카카오 인증으로 기존 계정을 확인했습니다. 앞으로 사용할 새 비밀번호를 설정해 주세요.
                    </p>
                    <div class="form-row">
                        <label for="migrated-password-email">카카오 이메일</label>
                        <input id="migrated-password-email" :value="email" readonly />
                    </div>
                    <div class="form-row">
                        <label for="migrated-password">새 비밀번호</label>
                        <input
                            id="migrated-password"
                            v-model="password"
                            type="password"
                            minlength="6"
                            autocomplete="new-password"
                            required
                        />
                    </div>
                    <div class="form-row">
                        <label for="migrated-password-confirm">비밀번호 확인</label>
                        <input
                            id="migrated-password-confirm"
                            v-model="confirmPassword"
                            type="password"
                            minlength="6"
                            autocomplete="new-password"
                            required
                        />
                    </div>
                    <button class="register-button" type="submit" :disabled="submitting">
                        {{ submitting ? '설정 중...' : '새 비밀번호 설정' }}
                    </button>
                    <RouterLink class="back-link" to="/">취소</RouterLink>
                </form>
                <form v-else-if="oauthSessionId" @submit.prevent="register">
                    <div class="form-row">
                        <label for="oauth-email">카카오 이메일</label>
                        <input id="oauth-email" :value="email" readonly />
                    </div>
                    <div class="form-row">
                        <label for="oauth-username">계정명</label>
                        <input id="oauth-username" v-model="username" minlength="4" maxlength="64" required />
                    </div>
                    <div class="form-row">
                        <label for="oauth-password">비밀번호</label>
                        <input id="oauth-password" v-model="password" type="password" minlength="6" required />
                    </div>
                    <div class="form-row">
                        <label for="oauth-confirm">비밀번호 확인</label>
                        <input id="oauth-confirm" v-model="confirmPassword" type="password" minlength="6" required />
                    </div>
                    <div class="form-row">
                        <label for="oauth-display-name">닉네임</label>
                        <div>
                            <input
                                id="oauth-display-name"
                                v-model="displayName"
                                minlength="2"
                                maxlength="40"
                                required
                            />
                            <small>깃수가 종료될 때 공개됩니다. 계속 사용할 이름이므로 신중하게 정해주세요.</small>
                        </div>
                    </div>
                    <div class="agreement-row">
                        <span>이용 약관</span>
                        <label>
                            <input v-model="termsAgreed" type="checkbox" />
                            <a :href="`${appBase}terms.1.html`" target="_blank">내용 확인</a> 후 동의합니다.
                        </label>
                    </div>
                    <div class="agreement-row">
                        <span>개인정보 제공 및 이용</span>
                        <label>
                            <input v-model="privacyAgreed" type="checkbox" />
                            <a :href="`${appBase}terms.2.html`" target="_blank">내용 확인</a> 후 동의합니다.
                        </label>
                    </div>
                    <div class="agreement-row">
                        <span>개인정보 제3자 제공 (선택)</span>
                        <label>
                            <input v-model="thirdPartyUse" type="checkbox" />
                            동의합니다.
                        </label>
                    </div>
                    <button class="register-button" type="submit" :disabled="submitting">
                        {{ submitting ? '가입 중...' : '가입' }}
                    </button>
                </form>
                <p v-if="errorMessage" class="oauth-error" role="alert">{{ errorMessage }}</p>
                <RouterLink v-if="!loading && !oauthSessionId" class="back-link" to="/">돌아가기</RouterLink>
            </section>
        </main>
    </DefaultLayout>
    <KakaoOtpDialog
        v-if="otpChallenge"
        :challenge-id="otpChallenge.challengeId"
        @verified="handleOtpVerified"
        @cancel="otpChallenge = null"
    />
</template>

<style scoped>
#oauth-container {
    width: min(calc(100% - 24px), 700px);
    margin: 90px auto 40px;
    color: #fff;
    font-family: Pretendard, 'Apple SD Gothic Neo', 'Noto Sans KR', 'Malgun Gothic', sans-serif;
}

#oauth-container h1 {
    margin: 0 0 18px;
    font-size: 32px;
    font-weight: 400;
    text-align: center;
}

.oauth-card {
    border: 1px solid #444;
    border-radius: 4px;
    background: #303030;
    overflow: hidden;
}

.oauth-card h2 {
    margin: 0;
    border-bottom: 1px solid #555;
    background: #444;
    padding: 8px 14px;
    font-size: 20px;
}

.oauth-card form {
    display: grid;
    gap: 12px;
    padding: 18px;
}

.recovery-panel {
    padding: 18px;
    text-align: center;
}

.recovery-panel p {
    margin: 0;
    line-height: 1.6;
}

.recovery-panel strong {
    color: #ffd180;
}

.recovery-actions {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 12px;
    margin-top: 18px;
}

.recovery-actions .register-button,
.recovery-actions .back-link {
    margin: 0;
}

.form-row,
.agreement-row {
    display: grid;
    grid-template-columns: 150px 1fr;
    gap: 12px;
    align-items: start;
}

.form-row > label,
.agreement-row > span {
    padding-top: 7px;
    text-align: right;
}

.form-row input:not([type='checkbox']) {
    width: 100%;
    box-sizing: border-box;
    border: 1px solid #ced4da;
    border-radius: 4px;
    background: #ddd;
    color: #303030;
    padding: 7px 10px;
}

.form-row small {
    display: block;
    margin-top: 4px;
    color: #aaa;
}

.agreement-row label {
    padding: 7px 0;
}

.agreement-row a {
    color: #6db9ff;
}

.register-button {
    min-height: 42px;
    margin-left: 162px;
    border: 1px solid #2f4d6c;
    border-radius: 4px;
    background: #375a7f;
    color: #fff;
    font-weight: 700;
    cursor: pointer;
}

.register-button:hover,
.register-button:focus {
    background: #2f4d6c;
}

.register-button:focus-visible {
    outline: 2px solid #f39c12;
    outline-offset: 1px;
}

.oauth-message,
.oauth-error,
.back-link {
    display: block;
    margin: 18px;
    text-align: center;
}

.oauth-error {
    color: #ff8a80;
}

.back-link {
    color: #6db9ff;
}

@media (max-width: 519px) {
    #oauth-container {
        width: calc(100% - 16px);
        margin-top: 78px;
    }

    #oauth-container h1 {
        font-size: 24px;
    }

    .form-row,
    .agreement-row {
        grid-template-columns: 1fr;
        gap: 3px;
    }

    .form-row > label,
    .agreement-row > span {
        padding-top: 0;
        text-align: left;
    }

    .register-button {
        margin-left: 0;
    }
}
</style>
