import { Request, Response } from 'express';
import User, { IUser } from '../models/User.js';
import OrganizationMember, { OrgRole } from '../models/OrganizationMember.js';

// Define locally to avoid import cycle risks
interface IAppUser extends IUser {
    organizationId?: string;
    organizationRole?: OrgRole;
    organizationBanned?: boolean;
}

interface AuthRequest extends Request {
    appUser?: IAppUser;
}

/**
 * Sync user from Auth to MongoDB
 * Creates user if doesn't exist, updates if exists
 * Also creates/updates OrganizationMember for the current organization
 */
export const syncUser = async (req: Request, res: Response) => {
    console.log('--- syncUser called ---', req.body);
    try {
        const { externalId, email, name, organizationId, clerkRole } = req.body;

        if (!externalId || !email || !name) {
            return res.status(400).json({ error: 'externalId, email e name são obrigatórios.' });
        }

        // 1. Upsert the global User record
        let user = await User.findOne({ externalId });

        if (user) {
            user.email = email;
            user.name = name;
            await user.save();
        } else {
            user = await User.create({
                externalId,
                email,
                name,
                role: 'guest', // Default global role
                isBanned: false
            });
        }

        if (organizationId) {

            // Map Auth role to our app role
            let appRole: OrgRole = 'guest';
            if (clerkRole === 'org:admin') {
                appRole = 'admin';
            } else if (clerkRole === 'org:member') {
                appRole = 'user';
            }

            // Use findOneAndUpdate with upsert to handle race conditions atomically
            // This avoids the need for try/catch on create and handles existing members too
            const member = await OrganizationMember.findOneAndUpdate(
                { userId: externalId, organizationId },
                {
                    $set: {
                        name,
                        email,
                        // Only update role if it's the first time (creation)
                        // Or we can optionally update it if needed. 
                        // For now, let's NOT overwrite role on update to preserve local changes, 
                        // UNLESS we want to force sync with Auth.
                        // Let's use $setOnInsert for fields we only want to set on creation.
                    },
                    $setOnInsert: {
                        role: appRole,
                        isBanned: false
                    }
                },
                { upsert: true, new: true, setDefaultsOnInsert: true }
            );

            // Return combined info
            return res.json({
                ...user.toObject(),
                organizationRole: member.role,
                organizationBanned: member.isBanned
            });
        }

        res.json(user);
    } catch (error) {
        console.error('Error syncing user:', error);
        // Retornar 200 mesmo com erro para não travar o frontend se for algo não crítico,
        // mas aqui é crítico. Vamos logar melhor.
        res.status(500).json({ error: 'Erro ao sincronizar usuário: ' + (error instanceof Error ? error.message : String(error)) });
    }
};

/**
 * Get current user data with organization context
 */
export const getCurrentUser = async (req: AuthRequest, res: Response) => {
    try {
        if (!req.appUser) {
            return res.status(404).json({ error: 'Usuário não encontrado.' });
        }

        const organizationId = req.headers['x-organization-id'] as string;

        if (organizationId) {
            const member = await OrganizationMember.findOne({
                userId: req.appUser.externalId,
                organizationId
            });

            if (member) {
                return res.json({
                    ...req.appUser.toObject(),
                    role: member.role,
                    isBanned: member.isBanned,
                    organizationId
                });
            }
        }

        res.json(req.appUser);
    } catch (error) {
        console.error('Error getting current user:', error);
        res.status(500).json({ error: 'Erro ao buscar usuário.' });
    }
};

/**
 * Get all users for the current organization (admin only)
 */
export const getAllUsers = async (req: Request, res: Response) => {
    try {
        const organizationId = req.headers['x-organization-id'] as string;

        console.log('getAllUsers called with organizationId:', organizationId);

        if (!organizationId) {
            return res.status(400).json({ error: 'Organização não selecionada.' });
        }

        const members = await OrganizationMember.find({ organizationId }).sort({ createdAt: -1 });
        console.log(`Found ${members.length} members for org ${organizationId}:`, members.map(m => ({ name: m.name, email: m.email, externalId: m.userId })));
        res.json(members);
    } catch (error) {
        console.error('Error getting users:', error);
        res.status(500).json({ error: 'Erro ao buscar usuários.' });
    }
};

/**
 * Update user role within organization (admin only)
 */
export const updateUserRole = async (req: AuthRequest, res: Response) => {
    try {
        const { id } = req.params;
        const { role } = req.body;
        const organizationId = req.headers['x-organization-id'] as string;

        if (!organizationId) {
            return res.status(400).json({ error: 'Organização não selecionada.' });
        }

        if (!['guest', 'user', 'admin'].includes(role)) {
            return res.status(400).json({ error: 'Role inválido. Use: guest, user ou admin.' });
        }

        const member = await OrganizationMember.findById(id);
        if (!member || member.organizationId !== organizationId) {
            return res.status(404).json({ error: 'Usuário não encontrado nesta organização.' });
        }

        // Prevent removing the last admin
        if (member.role === 'admin' && role !== 'admin') {
            const adminCount = await OrganizationMember.countDocuments({ organizationId, role: 'admin' });
            if (adminCount <= 1) {
                return res.status(400).json({ error: 'Não é possível remover o último administrador.' });
            }
        }

        member.role = role as OrgRole;
        await member.save();

        res.json(member);
    } catch (error) {
        console.error('Error toggling ban:', error);
        res.status(500).json({ error: 'Erro ao alterar status de banimento.' });
    }
};

/**
 * Ban or unban user within organization (admin only)
 */
export const toggleBanUser = async (req: AuthRequest, res: Response) => {
    try {
        const { id } = req.params;
        const { isBanned } = req.body;
        const organizationId = req.headers['x-organization-id'] as string;

        if (!organizationId) {
            return res.status(400).json({ error: 'Organização não selecionada.' });
        }

        const member = await OrganizationMember.findById(id);
        if (!member || member.organizationId !== organizationId) {
            return res.status(404).json({ error: 'Usuário não encontrado nesta organização.' });
        }

        // Prevent banning yourself
        if (req.appUser && member.userId === req.appUser.externalId) {
            return res.status(400).json({ error: 'Você não pode banir a si mesmo.' });
        }

        // Prevent banning another admin
        if (member.role === 'admin') {
            return res.status(400).json({ error: 'Não é possível banir um administrador.' });
        }

        member.isBanned = isBanned;
        await member.save();

        res.json(member);
    } catch (error) {
        console.error('Error toggling ban:', error);
        res.status(500).json({ error: 'Erro ao alterar status de banimento.' });
    }
};

/**
 * Update current user's lastSeenAt timestamp
 */
export const heartbeat = async (req: AuthRequest, res: Response) => {
    try {
        if (!req.appUser) {
            return res.status(401).json({ error: 'Não autenticado.' });
        }

        // Update User model
        await User.findByIdAndUpdate(req.appUser._id, { lastSeenAt: new Date() });

        // Also update Organization Member for tighter query
        // But for now User model is enough if we join correctly, or just use User model for presence.
        // Actually, since we want to show users per organization, we should filter by Org. 
        // Our 'User.ts' has organizationId, but it might be just the 'default' one. 
        // Let's rely on OrganizationMember for the list, but we need to update lastSeenAt there too? 
        // Strategy: Update User (global), and when querying active users, join or filter.
        // Better: Update OrganizationMember too if we want org-specific presence? 
        // Simpler: Just update User. When fetching active users, we fetch OrganizationMembers and populate User details, filtering by User.lastSeenAt.

        res.status(200).send();
    } catch (error) {
        // Silent fail for heartbeat
        console.error('Heartbeat error:', error);
        res.status(500).send();
    }
};

/**
 * Get active users in the same organization (seen in last 2 mins)
 */
export const getActiveUsers = async (req: AuthRequest, res: Response) => {
    try {
        const organizationId = req.headers['x-organization-id'] as string;
        const currentUserId = req.appUser?._id;

        if (!organizationId) {
            return res.status(400).json([]);
        }

        // Find members of this org
        const members = await OrganizationMember.find({ organizationId });

        // Get their Auth IDs
        const externalIds = members.map(m => m.userId);

        // Find Users who were seen recently (2 minutes)
        const twoMinutesAgo = new Date(Date.now() - 2 * 60 * 1000);

        const activeUsers = await User.find({
            externalId: { $in: externalIds },
            lastSeenAt: { $gte: twoMinutesAgo },
            _id: { $ne: currentUserId } // Optional: exclude self
        }).select('name email lastSeenAt externalId'); // Only needed fields

        res.json(activeUsers);
    } catch (error) {
        console.error('Error getting active users:', error);
        res.status(500).json([]);
    }
};

// Delete organization member
export const deleteUser = async (req: Request, res: Response) => {
    try {
        const { id } = req.params;
        const organizationId = req.headers['x-organization-id'] as string;

        if (!organizationId) {
            return res.status(400).json({ error: 'Organização não selecionada.' });
        }

        console.log(`Deleting member ${id} from organization ${organizationId}`);

        // Delete from OrganizationMember collection
        const result = await OrganizationMember.findByIdAndDelete(id);

        if (!result) {
            return res.status(404).json({ error: 'Membro não encontrado.' });
        }

        console.log(`Member ${result.name} deleted successfully`);

        res.json({ message: 'Membro removido com sucesso.', deletedMember: result });
    } catch (error) {
        console.error('Error deleting user:', error);
        res.status(500).json({ error: 'Erro ao remover membro.' });
    }
};
