import Alpine from 'alpinejs';
import './gameStatistics';
import {
    commitThrowRequest,
    fetchGameState,
    finishGameRequest,
    startGameRequest,
    undoThrowRequest
} from './game';

Alpine.data('dart', ({
    gameId,
    ownerOnly = false,
    guestOnly = false,
    guestPath = '',
    ownerPath = '',
} = {}) => ({
    phase: 'select_number',
    selectedNumber: null,
    selectedMultiplier: null,
    gameId,
    game: null,
    ownerOnly: Boolean(ownerOnly),
    guestOnly: Boolean(guestOnly),
    guestPath: String(guestPath || '').trim(),
    ownerPath: String(ownerPath || '').trim(),
    isOwner: false,
    loading: true,
    isSubmittingAction: false,
    errorMessage: '',
    socketEventListener: null,

    get isRunning() {
        return this.game?.status === 'RUNNING';
    },

    get isFinished() {
        return this.game?.status === 'FINISHED';
    },

    get isAborted() {
        return this.game?.status === 'ABORTED';
    },

    get isDraft() {
        return this.game?.status === 'DRAFT';
    },

    get dartInTurn() {
        return this.game?.currentDartInTurn ?? 1;
    },

    get players() {
        return this.game?.players ?? [];
    },

    get activePlayerId() {
        return this.game?.activePlayerId ?? null;
    },

    get activePlayer() {
        const activeId = String(this.activePlayerId || '').trim();
        if (activeId === '') {
            return null;
        }

        return this.players.find((player) => String(player?.id || '') === activeId) || null;
    },

    get modeType() {
        return String(this.game?.mode?.type || '');
    },

    get isAroundTheWorld() {
        return this.modeType === 'ATW';
    },

    get canEdit() {
        return this.isOwner === true;
    },

    get isReadOnly() {
        return this.game !== null && !this.canEdit;
    },

    async init() {
        this.gameId = String(gameId || '').trim();
        if (this.gameId === '') return;

        this.socketEventListener = (event) => {
            void this.handleSocketEvent(event);
        };
        window.addEventListener('socket:event', this.socketEventListener);

        await this.loadGame();
    },

    destroy() {
        if (!(this.socketEventListener instanceof Function)) {
            return;
        }

        window.removeEventListener('socket:event', this.socketEventListener);
    },

    async loadGame() {
        this.loading = true;
        this.errorMessage = '';

        try {
            const data = await fetchGameState(this.gameId);
            this.applyGameState(data?.game ?? null);
            if (this.maybeRedirectByOwnership()) {
                return;
            }
        } catch (error) {
            this.errorMessage = error instanceof Error ? error.message : 'Spiel konnte nicht geladen werden.';
        } finally {
            this.loading = false;
        }
    },

    async startGame() {
        await this.performAction(() => startGameRequest(this.gameId), {
            actionType: 'state'
        });
    },

    async finishGame() {
        await this.performAction(() => finishGameRequest(this.gameId), {
            actionType: 'state'
        });
    },

    async undoLastThrow() {
        await this.performAction(() => undoThrowRequest(this.gameId), {
            actionType: 'state'
        });
    },

    selectNumber(number) {
        if (!this.isRunning || !this.canEdit || this.isSubmittingAction) return;
        this.selectedNumber = number;
        this.selectedMultiplier = null;
        this.phase = 'select_multiplier';
    },

    selectBull() {
        if (!this.isRunning || !this.canEdit || this.isSubmittingAction) return;
        this.selectedNumber = 'BULL';
        this.selectedMultiplier = null;
        this.phase = 'select_multiplier';
    },

    selectOut() {
        if (!this.isRunning || !this.canEdit || this.isSubmittingAction) return;
        this.selectedNumber = 'OUT';
        this.selectedMultiplier = 0;
        this.commitThrow();
    },

    selectMultiplier(multiplier) {
        if (!this.isRunning || !this.canEdit || this.isSubmittingAction || this.selectedNumber === null) return;
        this.selectedMultiplier = multiplier;
        this.commitThrow();
    },

    selectBullValue(value) {
        if (!this.isRunning || !this.canEdit || this.isSubmittingAction) return;
        this.selectedMultiplier = value;
        this.commitThrow();
    },

    backToNumber() {
        this.phase = 'select_number';
        this.selectedNumber = null;
        this.selectedMultiplier = null;
    },

    async commitThrow() {
        if (this.selectedNumber === null || this.selectedMultiplier === null) return;

        await this.performAction(() => commitThrowRequest(this.gameId, {
            segment: this.selectedNumber,
            multiplier: this.selectedMultiplier
        }), {
            requiresRunning: true,
            actionType: 'throw'
        });
    },

    async performAction(action, { requiresRunning = false, actionType = 'state' } = {}) {
        if (this.isSubmittingAction) return;
        if (!this.canEdit) return;
        if (requiresRunning && !this.isRunning) return;

        this.errorMessage = '';
        this.isSubmittingAction = true;

        try {
            const previousGame = this.game;
            const data = await action();
            const nextGame = data?.game ?? null;
            if (!nextGame || typeof nextGame !== 'object') {
                throw new Error('Ungültige Spielantwort vom Server.');
            }

            this.applyGameState(nextGame);
            this.phase = 'select_number';
            this.selectedNumber = null;
            this.selectedMultiplier = null;
            this.emitRealtimeEvents(actionType, previousGame, nextGame);
        } catch (error) {
            this.errorMessage = error instanceof Error ? error.message : 'Aktion fehlgeschlagen.';
        } finally {
            this.isSubmittingAction = false;
        }
    },

    applyGameState(gameState) {
        if (!gameState || typeof gameState !== 'object') {
            throw new Error('Ungültige Spielantwort vom Server.');
        }

        this.game = gameState;
        this.isOwner = Boolean(gameState?.isOwner);
        this.emitGameState();
    },

    maybeRedirectByOwnership() {
        if (this.ownerOnly && !this.canEdit && this.guestPath !== '' && window.location.pathname !== this.guestPath) {
            window.location.replace(this.guestPath);
            return true;
        }

        if (this.guestOnly && this.canEdit && this.ownerPath !== '' && window.location.pathname !== this.ownerPath) {
            window.location.replace(this.ownerPath);
            return true;
        }

        return false;
    },

    emitRealtimeEvents(actionType, previousGame, nextGame) {
        const baseDetail = {
            gameId: this.gameId,
            action: String(actionType || 'state'),
            game: nextGame,
        };

        if (actionType === 'throw') {
            const newestThrow = this.findNewestThrow(previousGame, nextGame);
            if (newestThrow !== null) {
                this.dispatchWindowEvent('game:throw-committed', {
                    ...baseDetail,
                    throw: newestThrow,
                });
            }
        }

        if (this.hasTurnChanged(previousGame, nextGame)) {
            this.dispatchWindowEvent('game:turn-changed', {
                ...baseDetail,
                currentTurnNumber: nextGame?.currentTurnNumber ?? null,
                currentDartInTurn: nextGame?.currentDartInTurn ?? null,
                activePlayerId: nextGame?.activePlayerId ?? null,
            });
        }

        this.dispatchWindowEvent('game:state-changed', baseDetail);
    },

    async handleSocketEvent(event) {
        const eventName = String(event?.detail?.event || '').trim();
        if (!['game:throw', 'game:turn'].includes(eventName)) {
            return;
        }

        const payload = event?.detail?.payload;
        const payloadGameId = String(payload?.gameId || '').trim();
        if (payloadGameId !== this.gameId) {
            return;
        }

        if (payload?.game && typeof payload.game === 'object') {
            try {
                this.applyGameState(payload.game);
                this.phase = 'select_number';
                this.selectedNumber = null;
                this.selectedMultiplier = null;
            } catch {
                await this.loadGame();
            }
            return;
        }

        await this.loadGame();
    },

    hasTurnChanged(previousGame, nextGame) {
        const previousTurn = Number(previousGame?.currentTurnNumber || 0);
        const nextTurn = Number(nextGame?.currentTurnNumber || 0);
        const previousDart = Number(previousGame?.currentDartInTurn || 0);
        const nextDart = Number(nextGame?.currentDartInTurn || 0);
        const previousPlayer = String(previousGame?.activePlayerId || '');
        const nextPlayer = String(nextGame?.activePlayerId || '');
        const previousStatus = String(previousGame?.status || '');
        const nextStatus = String(nextGame?.status || '');

        return previousTurn !== nextTurn
            || previousDart !== nextDart
            || previousPlayer !== nextPlayer
            || previousStatus !== nextStatus;
    },

    findNewestThrow(previousGame, nextGame) {
        const previousThrowIds = new Set(this.collectThrows(previousGame).map((item) => item.id));
        const nextThrows = this.collectThrows(nextGame);

        for (const dartThrow of nextThrows) {
            if (!previousThrowIds.has(dartThrow.id)) {
                return dartThrow;
            }
        }

        return null;
    },

    collectThrows(gameState) {
        if (!gameState || typeof gameState !== 'object') {
            return [];
        }

        const rows = [];
        const players = Array.isArray(gameState.players) ? gameState.players : [];

        for (const player of players) {
            const throws = Array.isArray(player?.throws) ? player.throws : [];
            for (const dartThrow of throws) {
                const throwId = String(dartThrow?.id || '').trim();
                if (throwId === '') {
                    continue;
                }

                rows.push(dartThrow);
            }
        }

        rows.sort((a, b) => {
            const left = String(a?.createdAt || '');
            const right = String(b?.createdAt || '');
            return right.localeCompare(left);
        });

        return rows;
    },

    dispatchWindowEvent(name, detail) {
        window.dispatchEvent(new CustomEvent(name, { detail }));
    },

    emitGameState() {
        this.dispatchWindowEvent('game:state-updated', {
            gameId: this.gameId,
            status: String(this.game?.status || ''),
        });
    },

    formatThrow(t) {
        if (t.isBull) return `Bull ${t.bullValue}`;
        if (t.isOut) return 'OUT';
        return `${t.segment}x${t.multiplier}`;
    },

    displayPlayerScore(player) {
        if (!this.isAroundTheWorld) {
            return player?.currentScore ?? 0;
        }

        const target = Number(player?.currentScore ?? 1);
        if (!Number.isFinite(target)) {
            return 1;
        }

        if (target >= 21) {
            return 'Bull';
        }

        return Math.max(1, Math.min(20, target));
    }
}));
