import { FastifyInstance, FastifyRequest } from 'fastify';
import * as roomService from './rooms.service';
import { getUserById } from '../user/user.service';
import jwt from 'jsonwebtoken';

import {getConfig} from '../../shared/config';
const { values } = getConfig();
const jwtSecret = values.server?.jwt_secret;

interface WSClient {
    socket: any;
    userId: string;
    username: string;
    roomId: string;
    isGuest: boolean;
}

const clients = new Map<string, WSClient>();


function getClientIP(req: any): string {
    return req.headers['x-forwarded-for']?.split(',')[0].trim() ||
        req.headers['x-real-ip'] ||
        req.connection?.remoteAddress ||
        req.socket?.remoteAddress ||
        'unknown';
}

export function setupRoomWebSocket(fastify: FastifyInstance) {
    // @ts-ignore
    fastify.get('/ws/room/:roomId', { websocket: true }, (connection: any, req: any) => {
        handleWebSocketConnection(connection, req).catch(err => {
            console.error('WebSocket error:', err);
            try {
                connection.socket.close();
            } catch (e) {
                // Socket already closed
            }
        });
    });
}

async function handleWebSocketConnection(connection: any, req: any) {
    const socket = connection.socket || connection;
    const roomId = req.params.roomId;
    const token = req.query.token;
    const guestName = req.query.guestName;
    const password = req.query.password;
    const clientIP = getClientIP(req);

    let userId: string;
    let username: string;
    let avatar: string | undefined;
    let isGuest = false;
    let realUserId: any;

    const room = roomService.getRoom(roomId);

    // 1. Validaciones básicas de existencia y Ban
    if (!room) {
        socket.send(JSON.stringify({ type: 'error', message: 'Room not found' }));
        socket.close();
        return;
    }

    if (roomService.isIPBanned(roomId, clientIP)) {
        socket.send(JSON.stringify({ type: 'error', message: 'You have been banned from this room' }));
        socket.close();
        return;
    }

    // 2. MOVIDO ARRIBA: Autenticar usuario PRIMERO para saber quién es
    if (token) {
        try {
            const decoded: any = jwt.verify(token, jwtSecret);
            realUserId = decoded.id;
            const user = await getUserById(realUserId);

            if (user) {
                userId = `user_${realUserId}`;
                username = user.username;
                avatar = user.profile_picture_url || undefined;
                isGuest = false;
            } else {
                throw new Error('User not found');
            }
        } catch (err) {
            socket.send(JSON.stringify({ type: 'error', message: 'Invalid token' }));
            socket.close();
            return;
        }
    } else if (guestName && guestName.trim()) {
        // ... (Lógica de Guest se mantiene igual) ...
        const nameToCheck = guestName.trim();
        const isNameTaken = Array.from(room.users.values()).some(
            u => u.username.toLowerCase() === nameToCheck.toLowerCase()
        );
        if (isNameTaken) {
            socket.send(JSON.stringify({ type: 'error', message: 'Username is already taken' }));
            socket.close();
            return;
        }
        userId = `guest_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
        username = nameToCheck;
        isGuest = true;
    } else {
        socket.send(JSON.stringify({ type: 'error', message: 'Authentication required' }));
        socket.close();
        return;
    }

    // 3. Determinar si es Host
    const isHost = room.host.userId === realUserId || room.host.id === userId;

    // 4. MOVIDO ABAJO: Validar contraseña SOLO SI NO ES HOST
    if (room.password && !isHost) {
        if (!password || !roomService.verifyRoomPassword(roomId, password)) {
            socket.send(JSON.stringify({ type: 'error', message: 'Invalid password' }));
            socket.close();
            return;
        }
    }

    const userInRoom = {
        id: userId,
        username,
        avatar,
        isHost: isHost,
        isGuest,
        userId: realUserId,
        ipAddress: clientIP,  // NUEVO
        permissions: isHost ? undefined : { ...roomService.DEFAULT_GUEST_PERMISSIONS }
    };

    roomService.addUserToRoom(roomId, userInRoom);

    // Registrar cliente
    clients.set(userId, {
        socket: socket,
        userId,
        username,
        roomId,
        isGuest
    });

    socket.send(JSON.stringify({
        type: 'init',
        userId,
        username,
        isGuest,
        isHost,  // NUEVO: Enviar explícitamente
        room: {
            id: room.id,
            name: room.name,
            users: Array.from(room.users.values()).map(u => ({
                id: u.id,
                username: u.username,
                avatar: u.avatar,
                isHost: u.isHost,
                isGuest: u.isGuest,
                permissions: u.permissions  // NUEVO
            })),
            currentVideo: room.currentVideo,
            queue: room.queue || []
        }
    }));

    // Notificar a otros usuarios
    broadcastToRoom(roomId, {
        type: 'user_joined',
        user: {
            id: userId,
            username,
            avatar,
            isGuest
        }
    }, userId);

    // Manejar mensajes
    socket.on('message', (message: Buffer) => {
        try {
            const data = JSON.parse(message.toString());
            handleMessage(roomId, userId, data);
        } catch (err) {
            console.error('WebSocket message error:', err);
        }
    });

    // Manejar desconexión
    socket.on('close', () => {
        clients.delete(userId);
        roomService.removeUserFromRoom(roomId, userId);

        broadcastToRoom(roomId, {
            type: 'user_left',
            user: { userId, username }
        });
    });
}

function handleMessage(roomId: string, userId: string, data: any) {
    const room = roomService.getRoom(roomId);
    if (!room) return;

    const user = room.users.get(userId);
    if (!user) return;

    console.log('Handling message:', data.type, 'from user:', userId, 'isHost:', room.host.id === userId);

    switch (data.type) {
        case 'chat':
            broadcastToRoom(roomId, {
                type: 'chat',
                userId,
                username: room.users.get(userId)?.username || 'Unknown',
                avatar: room.users.get(userId)?.avatar,
                message: data.message,
                timestamp: Date.now()
            });
            break;

        case 'update_permissions':
            if (!user.isHost) {
                console.warn('Non-host attempted to update permissions');
                return;
            }

            const success = roomService.updateUserPermissions(
                roomId,
                data.targetUserId,
                data.permissions
            );

            if (success) {
                const updatedRoom = roomService.getRoom(roomId);
                if (updatedRoom) {
                    broadcastToRoom(roomId, {
                        type: 'permissions_updated',
                        users: Array.from(updatedRoom.users.values()).map(u => ({
                            id: u.id,
                            username: u.username,
                            avatar: u.avatar,
                            isHost: u.isHost,
                            isGuest: u.isGuest,
                            permissions: u.permissions
                        }))
                    });
                }
            }
            break;

        // NUEVO: Baneo de usuarios (solo host)
        case 'ban_user':
            if (!user.isHost) {
                console.warn('Non-host attempted to ban user');
                return;
            }

            const targetUser = room.users.get(data.targetUserId);
            if (targetUser && targetUser.ipAddress) {
                roomService.banUserIP(roomId, targetUser.ipAddress);

                // Cerrar conexión del usuario baneado
                const targetClient = clients.get(data.targetUserId);
                if (targetClient && targetClient.socket) {
                    targetClient.socket.send(JSON.stringify({
                        type: 'banned',
                        message: 'You have been banned from this room'
                    }));
                    targetClient.socket.close();
                }
            }
            break;


        case 'queue_play_item':
            if (!user.isHost && !roomService.hasPermission(roomId, userId, 'canManageQueue')) {
                console.warn('User lacks permission for queue management');
                return;
            }

            const itemToPlay = roomService.getAndRemoveQueueItem(roomId, data.itemUid);
            if (itemToPlay) {
                const videoPayload = {
                    videoData: itemToPlay.videoData.videoData,
                    subtitles: itemToPlay.videoData.subtitles,
                    currentTime: 0,
                    isPlaying: true
                };

                roomService.updateRoomVideo(roomId, videoPayload);
                roomService.updateRoomMetadata(roomId, itemToPlay.metadata);

                broadcastToRoom(roomId, {
                    type: 'video_update',
                    video: videoPayload,
                    metadata: itemToPlay.metadata
                });

                broadcastToRoom(roomId, {
                    type: 'queue_update',
                    queue: room.queue
                });
            }
            break;

        case 'queue_move':
            if (!user.isHost && !roomService.hasPermission(roomId, userId, 'canManageQueue')) {
                return;
            }

            const moved = roomService.moveQueueItem(roomId, data.itemUid, data.direction);
            if (moved) {
                broadcastToRoom(roomId, {
                    type: 'queue_update',
                    queue: room.queue
                });
            }
            break;

        case 'request_sync':
            // Cualquier usuario puede pedir sync
            const host = clients.get(room.host.id);
            if (host && host.socket && host.socket.readyState === 1) {
                console.log(`[Sync Request] User ${userId} requested sync from host`);

                host.socket.send(JSON.stringify({
                    type: 'sync_requested',
                    requestedBy: userId,
                    username: room.users.get(userId)?.username
                }));
            } else {
                console.warn(`[Sync Request] Host not available for user ${userId}`);

                if (room.currentVideo) {
                    const client = clients.get(userId);
                    if (client && client.socket && client.socket.readyState === 1) {
                        console.log(`[Sync Request] Sending cached video state to ${userId}`);
                        client.socket.send(JSON.stringify({
                            type: 'sync',
                            currentTime: room.currentVideo.currentTime || 0,
                            isPlaying: room.currentVideo.isPlaying || false
                        }));
                    }
                }
            }
            break;

        case 'video_update':
            const canUpdateVideo = user.isHost ||
                roomService.hasPermission(roomId, userId, 'canControl') ||
                roomService.hasPermission(roomId, userId, 'canManageQueue');

            if (!canUpdateVideo) {
                console.warn('User lacks permissions to update video');
                return;
            }

            roomService.updateRoomVideo(roomId, data.video);
            roomService.updateRoomMetadata(roomId, data.metadata);

            broadcastToRoom(roomId, {
                type: 'video_update',
                video: data.video,
                metadata: data.metadata
            });
            break;

        case 'queue_add_batch':
            if (!user.isHost && !roomService.hasPermission(roomId, userId, 'canManageQueue')) {
                return;
            }

            if (Array.isArray(data.items)) {
                data.items.forEach((item: any, i: number) => {
                    const newItem = {
                        uid: `q_${Date.now()}_${i}_${Math.random().toString(36).substr(2, 5)}`,
                        metadata: item.metadata,
                        videoData: item.video,
                        addedBy: room.users.get(userId)?.username || 'Unknown'
                    };
                    roomService.addQueueItem(roomId, newItem);
                });

                broadcastToRoom(roomId, {
                    type: 'queue_update',
                    queue: room.queue
                });
            }
            break;

        case 'sync':
            if (room.host.id !== userId) return;

            if (room.currentVideo) {
                room.currentVideo.currentTime = data.currentTime;
                room.currentVideo.isPlaying = data.isPlaying;
            }

            broadcastToRoom(roomId, {
                type: 'sync',
                currentTime: data.currentTime,
                isPlaying: data.isPlaying
            }, userId);
            break;

        case 'request_users':
            const currentRoom = roomService.getRoom(roomId);
            if (currentRoom) {
                const client = clients.get(userId);
                if (client && client.socket && client.socket.readyState === 1) {
                    client.socket.send(JSON.stringify({
                        type: 'users_update', // Nuevo tipo de respuesta
                        users: Array.from(currentRoom.users.values()).map(u => ({
                            id: u.id,
                            username: u.username,
                            avatar: u.avatar,
                            isHost: u.isHost,
                            isGuest: u.isGuest
                        }))
                    }));
                }
            }
            break;

        case 'play':
            if (!user.isHost && !roomService.hasPermission(roomId, userId, 'canControl')) {
                console.warn('User lacks control permissions');
                return;
            }

            broadcastToRoom(roomId, {
                type: 'play',
                currentTime: data.currentTime,
                timestamp: Date.now()
            }, userId);
            break;

        case 'pause':
            if (!user.isHost && !roomService.hasPermission(roomId, userId, 'canControl')) {
                console.warn('User lacks control permissions for pause');
                return;
            }

            console.log('Broadcasting pause event to room:', roomId);
            broadcastToRoom(roomId, {
                type: 'pause',
                currentTime: data.currentTime,
                timestamp: Date.now()
            }, userId);
            break;

        case 'seek':
            if (!user.isHost && !roomService.hasPermission(roomId, userId, 'canControl')) {
                console.warn('User lacks control permissions for seek');
                return;
            }

            console.log('Broadcasting seek event to room:', roomId);
            broadcastToRoom(roomId, {
                type: 'seek',
                currentTime: data.currentTime,
                timestamp: Date.now()
            }, userId);
            break;

        case 'request_state':
            const client = clients.get(userId);
            if (client && client.socket && client.socket.readyState === 1) {
                const updatedRoom = roomService.getRoom(roomId);
                if (updatedRoom) {
                    client.socket.send(JSON.stringify({
                        type: 'init',
                        userId,
                        username: client.username,
                        isGuest: client.isGuest,
                        room: {
                            id: updatedRoom.id,
                            name: updatedRoom.name,
                            users: Array.from(updatedRoom.users.values()).map(u => ({
                                id: u.id,
                                username: u.username,
                                avatar: u.avatar,
                                isHost: u.isHost,
                                isGuest: u.isGuest
                            })),
                            currentVideo: room.currentVideo,
                            metadata: room.metadata
                        }
                    }));
                }
            }
            break;

        case 'queue_add':
            if (!user.isHost && !roomService.hasPermission(roomId, userId, 'canManageQueue')) {
                return;
            }

            const newItem = {
                uid: `q_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`,
                metadata: data.metadata,
                videoData: data.video,
                addedBy: room.users.get(userId)?.username || 'Unknown'
            };

            roomService.addQueueItem(roomId, newItem);

            broadcastToRoom(roomId, {
                type: 'queue_update',
                queue: room.queue
            });
            break;

        case 'queue_remove':
            if (!user.isHost && !roomService.hasPermission(roomId, userId, 'canManageQueue')) {
                return;
            }

            roomService.removeQueueItem(roomId, data.itemUid);
            broadcastToRoom(roomId, {
                type: 'queue_update',
                queue: room.queue
            });
            break;

        case 'play_next':
            if (room.host.id !== userId) return;

            const nextItem = roomService.getNextQueueItem(roomId);
            if (nextItem) {
                const videoPayload = {
                    videoData: nextItem.videoData.videoData,
                    subtitles: nextItem.videoData.subtitles,
                    currentTime: 0,
                    isPlaying: true
                };

                roomService.updateRoomVideo(roomId, videoPayload);
                roomService.updateRoomMetadata(roomId, nextItem.metadata);

                broadcastToRoom(roomId, {
                    type: 'video_update',
                    video: videoPayload,
                    metadata: nextItem.metadata
                });

                broadcastToRoom(roomId, {
                    type: 'queue_update',
                    queue: room.queue
                });
            }
            break;

        default:
            console.warn('Unknown message type:', data.type);
            break;
    }
}

function broadcastToRoom(roomId: string, message: any, excludeUserId?: string) {
    const room = roomService.getRoom(roomId);
    if (!room) return;

    const messageStr = JSON.stringify(message);

    console.log('Broadcasting to room:', roomId, 'message type:', message.type, 'excluding:', excludeUserId);

    let sentCount = 0;
    room.users.forEach((user) => {
        if (user.id !== excludeUserId) {
            const client = clients.get(user.id);
            if (client && client.socket && client.socket.readyState === 1) {
                try {
                    client.socket.send(messageStr);
                    sentCount++;
                    console.log('Sent to user:', user.id, user.username);
                } catch (err) {
                    console.error('Error sending message to user:', user.id, err);
                }
            } else {
                console.warn('User socket not ready:', user.id, 'readyState:', client?.socket?.readyState);
            }
        }
    });

    console.log(`Broadcast complete: sent to ${sentCount} users`);
}