import Alpine from 'alpinejs';
import { io } from 'socket.io-client';
import { getAuthToken } from './game';

Alpine.data('socketClient', ({
    url = '',
    path = '/socket.io',
    room = '',
    autoConnect = true,
    withCredentials = true,
    authToken = null,
} = {}) => ({
    socket: null,
    connected: false,
    socketId: '',
    connectionError: '',
    lastEvent: null,
    joinedRoom: '',
    _url: String(url || '').trim(),
    _path: String(path || '/socket.io').trim(),
    _room: String(room || '').trim(),
    _autoConnect: Boolean(autoConnect),
    _withCredentials: Boolean(withCredentials),
    _authToken: typeof authToken === 'string' ? authToken.trim() : null,

    init() {
        if (this._autoConnect) {
            this.connect();
        }
    },

    destroy() {
        this.disconnect();
    },

    connect() {
        if (this.socket) {
            return this.socket;
        }

        this.connectionError = '';
        const token = this._authToken || getAuthToken();

        this.socket = io(this._url || undefined, {
            path: this._path || '/socket.io',
            withCredentials: this._withCredentials,
            auth: token ? { token } : undefined,
        });

        this.socket.on('connect', async () => {
            this.connected = true;
            this.socketId = String(this.socket?.id || '');
            this.connectionError = '';

            if (this._room !== '') {
                await this.joinRoom(this._room);
            }

            this.emitWindowEvent('socket:connected', {
                socketId: this.socketId,
                room: this.joinedRoom,
            });
        });

        this.socket.on('disconnect', async (reason) => {
            this.connected = false;
            this.socketId = '';
            this.emitWindowEvent('socket:disconnected', { reason });
            await this.leaveRoom(this._room);
        });

        this.socket.on('connect_error', (error) => {
            this.connectionError = error instanceof Error ? error.message : 'Socket connection failed.';
            this.emitWindowEvent('socket:error', {
                message: this.connectionError,
            });
        });

        this.socket.onAny((eventName, ...args) => {
            this.lastEvent = {
                name: String(eventName || ''),
                payload: args[0] ?? null,
                timestamp: new Date().toISOString(),
            };

            this.emitWindowEvent('socket:event', {
                event: this.lastEvent.name,
                payload: this.lastEvent.payload,
            });
        });

        return this.socket;
    },

    disconnect() {
        if (!this.socket) {
            return;
        }

        this.socket.removeAllListeners();
        this.socket.disconnect();
        this.socket = null;
        this.connected = false;
        this.socketId = '';
        this.joinedRoom = '';
    },

    async joinRoom(roomName) {
        const room = String(roomName || '').trim();
        if (!room || !this.socket) {
            return { ok: false, error: 'Socket not connected or room missing.' };
        }

        return new Promise((resolve) => {
            this.socket.emit('room:join', { room }, (ack) => {
                const response = ack && typeof ack === 'object'
                    ? ack
                    : { ok: false, error: 'Invalid server acknowledgement.' };

                if (response.ok) {
                    this.joinedRoom = room;
                }

                resolve(response);
            });
        });
    },

    async leaveRoom(roomName) {
        const room = String(roomName || this.joinedRoom || '').trim();
        if (!room || !this.socket) {
            return { ok: false, error: 'Socket not connected or room missing.' };
        }

        return new Promise((resolve) => {
            this.socket.emit('room:leave', { room }, (ack) => {
                const response = ack && typeof ack === 'object'
                    ? ack
                    : { ok: false, error: 'Invalid server acknowledgement.' };

                if (response.ok && this.joinedRoom === room) {
                    this.joinedRoom = '';
                }

                resolve(response);
            });
        });
    },

    emitRoomEvent(event, data = {}, room = '') {
        if (!this.socket) {
            return;
        }

        const targetRoom = String(room || this.joinedRoom || '').trim();
        if (!targetRoom) {
            return;
        }

        this.socket.emit('room:event', {
            room: targetRoom,
            event,
            data,
        });
    },

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