const { pool } = require('../config/database');
class Role {
    constructor(data) {
        this.id = data.id;
        this.name = data.name;
        this.organization_id = data.organization_id;
        this.description = data.description;
        this.level = data.level;
        this.is_active = data.is_active;
        this.created_at = data.created_at;
        this.updated_at = data.updated_at;
    }
    static async ensureUniqueIndex() {
        try {
            const checkSql = `
        SELECT COUNT(*) as cnt
        FROM INFORMATION_SCHEMA.STATISTICS
        WHERE TABLE_SCHEMA = DATABASE()
          AND TABLE_NAME = 'roles'
          AND INDEX_NAME = 'uniq_role_org_name'
      `;
            const [rows] = await pool.execute(checkSql);
            const exists = rows && rows[0] && Number(rows[0].cnt) > 0;
            if (!exists) {
                await pool.execute('ALTER TABLE roles ADD UNIQUE KEY uniq_role_org_name (organization_id, name)');
            }
        }
        catch (e) {
            // Silently ignore if cannot ensure index (e.g., permissions) but do not crash server
            return false;
        }
        return true;
    }
    // Get all active roles
    static async findAll(organizationId = null) {
        let query = 'SELECT * FROM roles WHERE is_active = 1';
        const params = [];
        if (organizationId) {
            query += ' AND organization_id = ?';
            params.push(organizationId);
        }
        query += ' ORDER BY level DESC';
        try {
            const [rows] = await pool.execute(query, params);
            return rows.map(row => new Role(row));
        }
        catch (error) {
            throw new Error(`Error finding roles: ${error.message}`);
        }
    }
    // Find role by ID
    static async findById(id, organizationId = null) {
        let query = 'SELECT * FROM roles WHERE id = ? AND is_active = 1';
        const params = [id];
        if (organizationId) {
            query += ' AND organization_id = ?';
            params.push(organizationId);
        }
        try {
            const [rows] = await pool.execute(query, params);
            return rows.length > 0 ? new Role(rows[0]) : null;
        }
        catch (error) {
            throw new Error(`Error finding role by ID: ${error.message}`);
        }
    }
    // Find role by name
    static async findByName(name, organizationId = null) {
        let query = 'SELECT * FROM roles WHERE name = ? AND is_active = 1';
        const params = [name];
        if (organizationId) {
            query += ' AND organization_id = ?';
            params.push(organizationId);
        }
        try {
            const [rows] = await pool.execute(query, params);
            return rows.length > 0 ? new Role(rows[0]) : null;
        }
        catch (error) {
            throw new Error(`Error finding role by name: ${error.message}`);
        }
    }
    // Get role with permissions
    static async getRoleWithPermissions(roleId) {
        const query = `
      SELECT 
        r.*,
        GROUP_CONCAT(p.name) as permissions
      FROM roles r
      LEFT JOIN role_permissions rp ON r.id = rp.role_id
      LEFT JOIN permissions p ON rp.permission_id = p.id AND p.is_active = 1
      WHERE r.id = ? AND r.is_active = 1
      GROUP BY r.id
    `;
        try {
            const [rows] = await pool.execute(query, [roleId]);
            if (rows.length > 0) {
                const role = new Role(rows[0]);
                role.permissions = rows[0].permissions ? rows[0].permissions.split(',') : [];
                return role;
            }
            return null;
        }
        catch (error) {
            throw new Error(`Error getting role with permissions: ${error.message}`);
        }
    }
    // Get user permissions (role permissions + direct user permissions)
    static async getUserPermissions(userId) {
        const query = `
      SELECT DISTINCT p.name, p.description, p.resource, p.action
      FROM permissions p
      WHERE p.is_active = 1
      AND (
        p.id IN (
          SELECT rp.permission_id 
          FROM role_permissions rp
          JOIN users u ON u.role_id = rp.role_id
          WHERE u.id = ?
        )
        OR p.id IN (
          SELECT up.permission_id 
          FROM user_permissions up
          WHERE up.user_id = ?
        )
      )
      ORDER BY p.resource, p.action
    `;
        try {
            const [rows] = await pool.execute(query, [userId, userId]);
            return rows;
        }
        catch (error) {
            throw new Error(`Error getting user permissions: ${error.message}`);
        }
    }
    // Check if user has permission
    static async userHasPermission(userId, permissionName) {
        const query = `
      SELECT COUNT(*) as count
      FROM permissions p
      WHERE p.name = ? AND p.is_active = 1
      AND (
        p.id IN (
          SELECT rp.permission_id 
          FROM role_permissions rp
          JOIN users u ON u.role_id = rp.role_id
          WHERE u.id = ?
        )
        OR p.id IN (
          SELECT up.permission_id 
          FROM user_permissions up
          WHERE up.user_id = ?
        )
      )
    `;
        try {
            const [rows] = await pool.execute(query, [permissionName, userId, userId]);
            return rows[0].count > 0;
        }
        catch (error) {
            throw new Error(`Error checking user permission: ${error.message}`);
        }
    }
    // Check if user has role
    static async userHasRole(userId, roleName) {
        const query = `
      SELECT COUNT(*) as count
      FROM users u
      JOIN roles r ON u.role_id = r.id
      WHERE u.id = ? AND r.name = ? AND r.is_active = 1
    `;
        try {
            const [rows] = await pool.execute(query, [userId, roleName]);
            return rows[0].count > 0;
        }
        catch (error) {
            throw new Error(`Error checking user role: ${error.message}`);
        }
    }
    // Create new role
    static async create(roleData) {
        const { name, organization_id, description, level, permissions = [] } = roleData;
        const connection = await pool.getConnection();
        try {
            await connection.beginTransaction();
            // Insert role
            const roleQuery = `
        INSERT INTO roles (name, organization_id, description, level, is_active) 
        VALUES (?, ?, ?, ?, 1)
      `;
            const [roleResult] = await connection.execute(roleQuery, [name, organization_id || 1, description, level]);
            const roleId = roleResult.insertId;
            // Insert role permissions if provided
            if (permissions.length > 0) {
                const permissionQuery = `
          INSERT INTO role_permissions (role_id, permission_id) 
          VALUES (?, ?)
        `;
                for (const permissionId of permissions) {
                    await connection.execute(permissionQuery, [roleId, permissionId]);
                }
            }
            await connection.commit();
            // Return the created role
            const createdRole = await Role.findById(roleId);
            return createdRole;
        }
        catch (error) {
            await connection.rollback();
            if (error && (error.code === 'ER_DUP_ENTRY' || /Duplicate entry/i.test(error.message))) {
                throw new Error('DUPLICATE_ROLE_NAME');
            }
            throw new Error(`Error creating role: ${error.message}`);
        }
        finally {
            connection.release();
        }
    }
    // Update role
    static async update(id, roleData) {
        const { name, description, level, permissions = [] } = roleData;
        const connection = await pool.getConnection();
        try {
            await connection.beginTransaction();
            // Update role
            const roleQuery = `
        UPDATE roles 
        SET name = ?, description = ?, level = ?, updated_at = CURRENT_TIMESTAMP
        WHERE id = ? AND is_active = 1
      `;
            const [roleResult] = await connection.execute(roleQuery, [name, description, level, id]);
            if (roleResult.affectedRows === 0) {
                throw new Error('Role not found or already inactive');
            }
            // Update role permissions
            // First, remove existing permissions
            await connection.execute('DELETE FROM role_permissions WHERE role_id = ?', [id]);
            // Then add new permissions
            if (permissions.length > 0) {
                const permissionQuery = `
          INSERT INTO role_permissions (role_id, permission_id) 
          VALUES (?, ?)
        `;
                for (const permissionId of permissions) {
                    await connection.execute(permissionQuery, [id, permissionId]);
                }
            }
            await connection.commit();
            // Return the updated role
            const updatedRole = await Role.findById(id);
            return updatedRole;
        }
        catch (error) {
            await connection.rollback();
            if (error && (error.code === 'ER_DUP_ENTRY' || /Duplicate entry/i.test(error.message))) {
                throw new Error('DUPLICATE_ROLE_NAME');
            }
            throw new Error(`Error updating role: ${error.message}`);
        }
        finally {
            connection.release();
        }
    }
    // Soft delete role (set is_active = 0)
    static async delete(id) {
        const query = `
      UPDATE roles 
      SET is_active = 0, updated_at = CURRENT_TIMESTAMP
      WHERE id = ? AND is_active = 1
    `;
        try {
            const [result] = await pool.execute(query, [id]);
            if (result.affectedRows === 0) {
                throw new Error('Role not found or already inactive');
            }
            return true;
        }
        catch (error) {
            throw new Error(`Error deleting role: ${error.message}`);
        }
    }
    // Get all roles with permissions (for admin interface)
    static async findAllWithPermissions(organizationId = null) {
        let query = `
      SELECT 
        r.*,
        o.name as organization_name,
        o.code as organization_code,
        GROUP_CONCAT(p.name) as permissions,
        GROUP_CONCAT(p.id) as permission_ids
      FROM roles r
      LEFT JOIN organizations o ON r.organization_id = o.id
      LEFT JOIN role_permissions rp ON r.id = rp.role_id
      LEFT JOIN permissions p ON rp.permission_id = p.id AND p.is_active = 1
      WHERE r.is_active = 1
    `;
        const params = [];
        if (organizationId) {
            query += ' AND r.organization_id = ?';
            params.push(organizationId);
        }
        query += `
      GROUP BY r.id, o.name, o.code
      ORDER BY r.organization_id, r.level DESC
    `;
        try {
            const [rows] = await pool.execute(query, params);
            const result = rows.map(row => ({
                ...row,
                permissions: row.permissions ? row.permissions.split(',') : [],
                permission_ids: row.permission_ids ? row.permission_ids.split(',').map(id => parseInt(id)) : []
            }));
            return result;
        }
        catch (error) {
            throw new Error(`Error finding roles with permissions: ${error.message}`);
        }
    }
    // Get all permissions with role assignments
    static async getAllPermissionsWithRoles() {
        const query = `
      SELECT 
        p.*,
        GROUP_CONCAT(r.name) as roles,
        GROUP_CONCAT(r.id) as role_ids
      FROM permissions p
      LEFT JOIN role_permissions rp ON p.id = rp.permission_id
      LEFT JOIN roles r ON rp.role_id = r.id AND r.is_active = 1
      WHERE p.is_active = 1
      GROUP BY p.id
      ORDER BY p.resource, p.action
    `;
        try {
            const [rows] = await pool.execute(query);
            return rows.map(row => ({
                ...row,
                roles: row.roles ? row.roles.split(',') : [],
                role_ids: row.role_ids ? row.role_ids.split(',').map(id => parseInt(id)) : []
            }));
        }
        catch (error) {
            throw new Error(`Error finding permissions with roles: ${error.message}`);
        }
    }
    // Convert to JSON
    toJSON() {
        return {
            id: this.id,
            name: this.name,
            description: this.description,
            level: this.level,
            is_active: this.is_active,
            created_at: this.created_at,
            updated_at: this.updated_at
        };
    }
}
module.exports = Role;
//# sourceMappingURL=Role.js.map