import { defineStore } from 'pinia';
import { takeGameSessionTransfer, type GameSessionTransfer } from '@sammo-ts/common/auth/gameSessionTransfer';
import { gatewayTrpc } from '../utils/gatewayTrpc';
import { trpc as gameTrpc } from '../utils/trpc';
import { gameFrontendRuntimeConfig } from '../config/runtimeConfig';

export type SessionStatus = 'unknown' | 'public' | 'authed' | 'general';

export interface SessionUser {
    id: string;
    username: string;
    displayName: string;
}

interface SessionState {
    status: SessionStatus;
    user: SessionUser | null;
    sessionToken: string | null;
    gameToken: string | null;
    profile: string | null;
    initializing: boolean;
    error: string | null;
}

const SESSION_TOKEN_KEY = 'sammo-session-token';
const PROFILE_KEY = 'sammo-game-profile';
const GAME_TOKEN_KEY = 'sammo-game-token';
const ACCESS_TOKEN_PREFIX = 'ga_';

const readStorage = (key: string): string | null => {
    if (typeof window === 'undefined') {
        return null;
    }
    return window.localStorage.getItem(key);
};

const writeStorage = (key: string, value: string | null): void => {
    if (typeof window === 'undefined') {
        return;
    }
    if (value) {
        window.localStorage.setItem(key, value);
    } else {
        window.localStorage.removeItem(key);
    }
};

const readQueryParam = (key: string): string | null => {
    if (typeof window === 'undefined') {
        return null;
    }
    const url = new URL(window.location.href);
    const value = url.searchParams.get(key);
    if (!value) {
        return null;
    }
    url.searchParams.delete(key);
    window.history.replaceState({}, '', url.toString());
    return value;
};

const readTransferredGameSession = (): GameSessionTransfer | null => {
    if (typeof window === 'undefined') {
        return null;
    }
    try {
        return takeGameSessionTransfer(window.sessionStorage);
    } catch {
        return null;
    }
};

const isAccessToken = (token: string | null): boolean => {
    if (!token) {
        return false;
    }
    return token.startsWith(ACCESS_TOKEN_PREFIX);
};

export const useSessionStore = defineStore('session', {
    state: (): SessionState => ({
        status: 'unknown',
        user: null,
        sessionToken: null,
        gameToken: null,
        profile: null,
        initializing: false,
        error: null,
    }),
    getters: {
        isReady: (state) => state.status !== 'unknown',
        isAuthed: (state) => state.status === 'authed' || state.status === 'general',
        hasGeneral: (state) => state.status === 'general',
        needsGeneral: (state) => state.status === 'authed',
    },
    actions: {
        setSessionToken(sessionToken: string | null) {
            this.sessionToken = sessionToken;
            writeStorage(SESSION_TOKEN_KEY, sessionToken);
        },
        setProfile(profile: string | null) {
            this.profile = profile;
            writeStorage(PROFILE_KEY, profile);
        },
        setGameToken(gameToken: string | null) {
            this.gameToken = gameToken;
            writeStorage(GAME_TOKEN_KEY, gameToken);
        },
        clearSession() {
            this.user = null;
            this.setSessionToken(null);
            this.setGameToken(null);
            this.status = 'public';
        },
        leaveGame() {
            this.setGameToken(null);
            this.status = this.sessionToken ? 'authed' : 'public';
        },
        async refreshGeneralStatus() {
            if (!this.gameToken) {
                this.status = this.sessionToken ? 'authed' : 'public';
                return;
            }
            if (!isAccessToken(this.gameToken)) {
                const exchanged = await this.exchangeGatewayToken();
                if (!exchanged) {
                    this.status = this.sessionToken ? 'authed' : 'public';
                    return;
                }
            }

            try {
                await gameTrpc.auth.status.query();
            } catch {
                this.setGameToken(null);
                if (this.sessionToken && this.profile) {
                    try {
                        const issued = await gatewayTrpc.auth.issueGameSession.mutate({
                            sessionToken: this.sessionToken,
                            profile: this.profile,
                        });
                        this.setGameToken(issued.gameToken);
                        if (await this.exchangeGatewayToken()) {
                            await gameTrpc.auth.status.query();
                        } else {
                            throw new Error('Game token exchange failed.');
                        }
                    } catch {
                        this.setGameToken(null);
                        this.error = 'game_session_invalid';
                        this.status = 'public';
                        return;
                    }
                } else {
                    this.error = 'game_session_invalid';
                    this.status = 'public';
                    return;
                }
            }

            try {
                const lobby = await gameTrpc.lobby.info.query();
                this.status = lobby.myGeneral ? 'general' : 'authed';
            } catch {
                this.error = 'game_lobby_unavailable';
                if (this.status === 'unknown' || this.status === 'public') {
                    this.status = 'authed';
                }
            }
        },
        async exchangeGatewayToken(): Promise<boolean> {
            if (!this.gameToken || isAccessToken(this.gameToken)) {
                return true;
            }
            try {
                const exchanged = await gameTrpc.auth.exchangeGatewayToken.mutate({
                    gatewayToken: this.gameToken,
                });
                this.setGameToken(exchanged.accessToken);
                return true;
            } catch {
                this.error = 'game_token_exchange_failed';
                this.setGameToken(null);
                return false;
            }
        },
        async initialize() {
            if (this.initializing || this.status !== 'unknown') {
                return;
            }

            this.initializing = true;
            this.error = null;

            const tokenFromQuery = readQueryParam('sessionToken');
            if (tokenFromQuery) {
                this.setSessionToken(tokenFromQuery);
            }

            const profileFromQuery = readQueryParam('profile');
            if (profileFromQuery) {
                this.setProfile(profileFromQuery);
            }

            const gatewayTokenFromQuery = readQueryParam('gameToken');
            if (gatewayTokenFromQuery) {
                this.setGameToken(gatewayTokenFromQuery);
            }

            const transferredGameSession = readTransferredGameSession();
            if (transferredGameSession) {
                this.setProfile(transferredGameSession.profile);
                this.setGameToken(transferredGameSession.gatewayToken);
            }

            const storedToken = this.sessionToken ?? readStorage(SESSION_TOKEN_KEY);
            if (storedToken && storedToken !== this.sessionToken) {
                this.setSessionToken(storedToken);
            }

            const storedGameToken = this.gameToken ?? readStorage(GAME_TOKEN_KEY);
            if (storedGameToken && storedGameToken !== this.gameToken) {
                this.setGameToken(storedGameToken);
            }

            const storedProfile = this.profile ?? readStorage(PROFILE_KEY) ?? gameFrontendRuntimeConfig.profile;
            if (storedProfile && storedProfile !== this.profile) {
                this.setProfile(storedProfile);
            }

            if (!this.sessionToken && !this.gameToken) {
                this.status = 'public';
                this.initializing = false;
                return;
            }

            if (this.sessionToken) {
                try {
                    const me = await gatewayTrpc.me.query();
                    if (!me) {
                        this.clearSession();
                        this.initializing = false;
                        return;
                    }
                    this.user = {
                        id: me.id,
                        username: me.username,
                        displayName: me.displayName,
                    };
                    this.status = 'authed';
                } catch {
                    this.error = 'gateway_unavailable';
                    this.status = 'public';
                    this.initializing = false;
                    return;
                }
            } else {
                this.status = 'authed';
            }

            if (!this.profile) {
                this.initializing = false;
                return;
            }

            try {
                if (!this.gameToken || !isAccessToken(this.gameToken)) {
                    const sessionToken = this.sessionToken;
                    if (sessionToken) {
                        const issued = await gatewayTrpc.auth.issueGameSession.mutate({
                            sessionToken,
                            profile: this.profile,
                        });
                        this.setGameToken(issued.gameToken);
                    }
                }
                await this.refreshGeneralStatus();
            } catch {
                this.error = 'game_session_unavailable';
                this.status = 'authed';
            } finally {
                this.initializing = false;
            }
        },
    },
});
