import { Request, Response, NextFunction } from "express";
import { catchAsyncError } from "./catchAsyncError";
import ErrorHandler from "./error";
import database from "../config/db";

interface AuthRequest extends Request {
  user?: any;
  permissions?: string[];
}

// Check if user has specific permission
export const hasPermission = (permissionName: string) => {
  return catchAsyncError(async (req: AuthRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return next(new ErrorHandler("Authentication required.", 401));
    }

    const result = await database.query(
      `SELECT has_permission($1, $2) as has_permission`,
      [req.user.id, permissionName]
    );

    const hasPermission = result.rows[0]?.has_permission;

    if (!hasPermission) {
      return next(new ErrorHandler(`Insufficient permissions. Required: ${permissionName}`, 403));
    }

    next();
  });
};

// Check if user has any of the given permissions
export const hasAnyPermission = (permissionNames: string[]) => {
  return catchAsyncError(async (req: AuthRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return next(new ErrorHandler("Authentication required.", 401));
    }

    let hasAny = false;
    
    for (const permissionName of permissionNames) {
      const result = await database.query(
        `SELECT has_permission($1, $2) as has_permission`,
        [req.user.id, permissionName]
      );
      
      if (result.rows[0]?.has_permission) {
        hasAny = true;
        break;
      }
    }

    if (!hasAny) {
      return next(new ErrorHandler("Insufficient permissions.", 403));
    }

    next();
  });
};

// Check if user has all of the given permissions
export const hasAllPermissions = (permissionNames: string[]) => {
  return catchAsyncError(async (req: AuthRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return next(new ErrorHandler("Authentication required.", 401));
    }

    for (const permissionName of permissionNames) {
      const result = await database.query(
        `SELECT has_permission($1, $2) as has_permission`,
        [req.user.id, permissionName]
      );
      
      if (!result.rows[0]?.has_permission) {
        return next(new ErrorHandler(`Missing permission: ${permissionName}`, 403));
      }
    }

    next();
  });
};

// Check if user has specific role
export const hasRole = (roleName: string) => {
  return catchAsyncError(async (req: AuthRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return next(new ErrorHandler("Authentication required.", 401));
    }

    const result = await database.query(
      `SELECT EXISTS(
        SELECT 1 
        FROM role_assignments ra
        INNER JOIN roles r ON ra.role_id = r.id
        WHERE ra.account_id = $1 
          AND ra.is_active = true
          AND (ra.expires_at IS NULL OR ra.expires_at > CURRENT_TIMESTAMP)
          AND r.name = $2
      ) as has_role`,
      [req.user.id, roleName]
    );

    const hasRole = result.rows[0]?.has_role;

    if (!hasRole) {
      return next(new ErrorHandler(`Required role: ${roleName}`, 403));
    }

    next();
  });
};

// Check if user has minimum role level
export const hasMinLevel = (minLevel: number) => {
  return catchAsyncError(async (req: AuthRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return next(new ErrorHandler("Authentication required.", 401));
    }

    const result = await database.query(
      `SELECT COALESCE(MAX(r.level), 0) as user_level
       FROM role_assignments ra
       INNER JOIN roles r ON ra.role_id = r.id
       WHERE ra.account_id = $1 
         AND ra.is_active = true
         AND (ra.expires_at IS NULL OR ra.expires_at > CURRENT_TIMESTAMP)`,
      [req.user.id]
    );

    const userLevel = parseInt(result.rows[0]?.user_level || 0);

    if (userLevel < minLevel) {
      return next(new ErrorHandler(`Insufficient privilege level. Required: ${minLevel}, Current: ${userLevel}`, 403));
    }

    next();
  });
};

// Middleware to load user permissions into request
export const loadUserPermissions = catchAsyncError(async (req: AuthRequest, res: Response, next: NextFunction) => {
  if (!req.user) {
    return next();
  }

  const result = await database.query(
    `SELECT * FROM get_user_permissions($1)`,
    [req.user.id]
  );

  req.permissions = result.rows.map((row: any) => row.permission_name);
  
  next();
});

// Middleware to check if user can access resource (owner or has permission)
export const canAccessResource = (
  resourceType: string,
  permissionName: string,
  ownerField: string = 'user_id'
) => {
  return catchAsyncError(async (req: AuthRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return next(new ErrorHandler("Authentication required.", 401));
    }

    // Check if user is owner of the resource
    const resourceId = req.params.id;
    if (resourceId) {
      // Determine table name from resource type
      let tableName = '';
      switch (resourceType) {
        case 'user':
          tableName = 'accounts';
          break;
        case 'company':
          tableName = 'accounts';
          break;
        case 'content':
          tableName = 'activities';
          break;
        default:
          tableName = resourceType + 's';
      }

      const ownerResult = await database.query(
        `SELECT ${ownerField} FROM ${tableName} WHERE id = $1`,
        [resourceId]
      );

      if (ownerResult.rows.length > 0 && ownerResult.rows[0][ownerField] === req.user.id) {
        // User owns the resource, allow access
        return next();
      }
    }

    // User is not owner, check permission
    const permissionResult = await database.query(
      `SELECT has_permission($1, $2) as has_permission`,
      [req.user.id, permissionName]
    );

    const hasPermission = permissionResult.rows[0]?.has_permission;

    if (!hasPermission) {
      return next(new ErrorHandler("Access denied. You don't own this resource or have permission to access it.", 403));
    }

    next();
  });
};

// Check if user can modify resource (supports both ownership and admin permissions)
export const canModifyResource = (
  resourceType: string,
  updatePermission: string,
  deletePermission: string,
  ownerField: string = 'user_id'
) => {
  return catchAsyncError(async (req: AuthRequest, res: Response, next: NextFunction) => {
    if (!req.user) {
      return next(new ErrorHandler("Authentication required.", 401));
    }

    const resourceId = req.params.id;
    const method = req.method;

    if (!resourceId) {
      return next(new ErrorHandler("Resource ID required.", 400));
    }

    // Determine permission needed based on HTTP method
    let permissionNeeded = '';
    if (method === 'PUT' || method === 'PATCH') {
      permissionNeeded = updatePermission;
    } else if (method === 'DELETE') {
      permissionNeeded = deletePermission;
    } else {
      return next(new ErrorHandler("Invalid method for this middleware.", 400));
    }

    // Determine table name
    let tableName = '';
    switch (resourceType) {
      case 'user':
        tableName = 'accounts';
        break;
      case 'company':
        tableName = 'accounts';
        break;
      case 'content':
        tableName = 'activities';
        break;
      default:
        tableName = resourceType + 's';
    }

    // Check if user is owner
    const ownerResult = await database.query(
      `SELECT ${ownerField} FROM ${tableName} WHERE id = $1`,
      [resourceId]
    );

    if (ownerResult.rows.length === 0) {
      return next(new ErrorHandler("Resource not found.", 404));
    }

    const isOwner = ownerResult.rows[0][ownerField] === req.user.id;

    // If user is owner, allow modification
    if (isOwner) {
      return next();
    }

    // User is not owner, check admin permission
    const permissionResult = await database.query(
      `SELECT has_permission($1, $2) as has_permission`,
      [req.user.id, permissionNeeded]
    );

    const hasPermission = permissionResult.rows[0]?.has_permission;

    if (!hasPermission) {
      return next(new ErrorHandler("You don't have permission to modify this resource.", 403));
    }

    next();
  });
};