/**
 * Unified Permission Checking Utility
 *
 * This module provides a centralized system for checking permissions across
 * both global user roles and tenant-specific roles.
 *
 * WHEN TO USE WHICH PERMISSION CHECK:
 *
 * 1. Use checkGlobalPermission() for:
 *    - Platform-wide features (user management, system settings)
 *    - Cross-tenant operations
 *    - Administrative tools
 *    - Features that aren't tenant-specific
 *
 * 2. Use checkTenantPermission() for:
 *    - Tenant-scoped operations (team management, tenant settings)
 *    - Resource access within a tenant (orders, inventory, recipes)
 *    - Organization-specific features
 *    - Most application features
 *
 * 3. Use checkCombinedPermission() for:
 *    - Features that require EITHER global OR tenant permissions
 *    - Mixed access scenarios (e.g., super admin OR tenant owner)
 *    - Fallback permission checks
 *
 * EXAMPLES:
 *
 * // Check if user can manage platform users (global only)
 * checkGlobalPermission(user, { requiredRole: 'admin' })
 *
 * // Check if user can manage tenant team (tenant only)
 * checkTenantPermission(tenantAccess, { requiredRole: 'owner' })
 *
 * // Check if user can access a feature (either global admin or tenant owner)
 * checkCombinedPermission(user, tenantAccess, {
 *   globalRoles: ['admin', 'super_admin'],
 *   tenantRoles: ['owner']
 * })
 */

import {
  GLOBAL_USER_ROLES,
  TENANT_ROLES,
  ROLE_HIERARCHY,
  hasGlobalRole,
  hasTenantRole,
  type GlobalUserRole,
  type TenantRole,
  type Role
} from '../types/roles';

/**
 * User object structure (from Auth service)
 */
export interface User {
  id: string;
  email: string;
  role: GlobalUserRole;
  full_name?: string;
  is_active: boolean;
}

/**
 * Tenant access object structure (from Tenant service)
 */
export interface TenantAccess {
  has_access: boolean;
  role: TenantRole;
  permissions: string[];
}

/**
 * Permission check options for global permissions
 */
export interface GlobalPermissionOptions {
  requiredRole: GlobalUserRole;
  allowHigherRoles?: boolean; // Default: true
}

/**
 * Permission check options for tenant permissions
 */
export interface TenantPermissionOptions {
  requiredRole?: TenantRole;
  requiredPermission?: string;
  allowHigherRoles?: boolean; // Default: true
}

/**
 * Permission check options for combined (global + tenant) permissions
 */
export interface CombinedPermissionOptions {
  globalRoles?: GlobalUserRole[];
  tenantRoles?: TenantRole[];
  tenantPermissions?: string[];
  requireBoth?: boolean; // Default: false (OR logic), true for AND logic
}

/**
 * Check if a user has a specific global permission
 *
 * @param user - User object from auth store
 * @param options - Permission requirements
 * @returns true if user has the required global permission
 *
 * @example
 * // Check if user is an admin
 * checkGlobalPermission(user, { requiredRole: 'admin' })
 *
 * // Check if user is exactly a manager (no higher roles)
 * checkGlobalPermission(user, { requiredRole: 'manager', allowHigherRoles: false })
 */
export function checkGlobalPermission(
  user: User | null | undefined,
  options: GlobalPermissionOptions
): boolean {
  if (!user || !user.is_active) return false;

  const { requiredRole, allowHigherRoles = true } = options;

  if (allowHigherRoles) {
    // Check if user has the required role or higher
    return hasGlobalRole(user.role, requiredRole);
  } else {
    // Check for exact role match
    return user.role === requiredRole;
  }
}

/**
 * Check if a user has a specific tenant permission
 *
 * @param tenantAccess - Tenant access object from tenant store
 * @param options - Permission requirements
 * @returns true if user has the required tenant permission
 *
 * @example
 * // Check if user is a tenant owner
 * checkTenantPermission(tenantAccess, { requiredRole: 'owner' })
 *
 * // Check if user has a specific permission
 * checkTenantPermission(tenantAccess, { requiredPermission: 'manage_team' })
 *
 * // Check if user is exactly an admin (no higher roles)
 * checkTenantPermission(tenantAccess, { requiredRole: 'admin', allowHigherRoles: false })
 */
export function checkTenantPermission(
  tenantAccess: TenantAccess | null | undefined,
  options: TenantPermissionOptions
): boolean {
  if (!tenantAccess || !tenantAccess.has_access) return false;

  const { requiredRole, requiredPermission, allowHigherRoles = true } = options;

  // Check role-based permission
  if (requiredRole) {
    if (allowHigherRoles) {
      if (!hasTenantRole(tenantAccess.role, requiredRole)) {
        return false;
      }
    } else {
      if (tenantAccess.role !== requiredRole) {
        return false;
      }
    }
  }

  // Check specific permission
  if (requiredPermission) {
    if (!tenantAccess.permissions?.includes(requiredPermission)) {
      return false;
    }
  }

  return true;
}

/**
 * Check combined global and tenant permissions
 *
 * @param user - User object from auth store
 * @param tenantAccess - Tenant access object from tenant store
 * @param options - Permission requirements
 * @returns true if user meets the permission criteria
 *
 * @example
 * // Check if user is either global admin OR tenant owner (OR logic)
 * checkCombinedPermission(user, tenantAccess, {
 *   globalRoles: ['admin', 'super_admin'],
 *   tenantRoles: ['owner']
 * })
 *
 * // Check if user is global admin AND tenant member (AND logic)
 * checkCombinedPermission(user, tenantAccess, {
 *   globalRoles: ['admin'],
 *   tenantRoles: ['member', 'admin', 'owner'],
 *   requireBoth: true
 * })
 */
export function checkCombinedPermission(
  user: User | null | undefined,
  tenantAccess: TenantAccess | null | undefined,
  options: CombinedPermissionOptions
): boolean {
  const {
    globalRoles = [],
    tenantRoles = [],
    tenantPermissions = [],
    requireBoth = false
  } = options;

  // Check global roles
  const hasGlobalAccess = globalRoles.length === 0 || Boolean(
    user?.is_active &&
    globalRoles.some(role => hasGlobalRole(user.role, role))
  );

  // Check tenant roles
  const hasTenantRoleAccess = tenantRoles.length === 0 || Boolean(
    tenantAccess?.has_access &&
    tenantRoles.some(role => hasTenantRole(tenantAccess.role, role))
  );

  // Check tenant permissions
  const hasTenantPermissionAccess = tenantPermissions.length === 0 || Boolean(
    tenantAccess?.has_access &&
    tenantPermissions.some(perm => tenantAccess.permissions?.includes(perm))
  );

  // Combine tenant role and permission checks (must pass at least one)
  const hasTenantAccess = hasTenantRoleAccess || hasTenantPermissionAccess;

  if (requireBoth) {
    // AND logic: must have both global and tenant access
    return hasGlobalAccess && hasTenantAccess;
  } else {
    // OR logic: must have either global or tenant access
    return hasGlobalAccess || hasTenantAccess;
  }
}

/**
 * Check if user can manage team members
 *
 * @param user - User object
 * @param tenantAccess - Tenant access object
 * @returns true if user can manage team
 *
 * @example
 * canManageTeam(user, tenantAccess)
 */
export function canManageTeam(
  user: User | null | undefined,
  tenantAccess: TenantAccess | null | undefined
): boolean {
  return checkCombinedPermission(user, tenantAccess, {
    globalRoles: [GLOBAL_USER_ROLES.ADMIN, GLOBAL_USER_ROLES.SUPER_ADMIN],
    tenantRoles: [TENANT_ROLES.OWNER, TENANT_ROLES.ADMIN]
  });
}

/**
 * Check if user is tenant owner
 *
 * @param user - User object
 * @param tenantAccess - Tenant access object
 * @returns true if user is owner
 *
 * @example
 * isTenantOwner(user, tenantAccess)
 */
export function isTenantOwner(
  user: User | null | undefined,
  tenantAccess: TenantAccess | null | undefined
): boolean {
  return checkCombinedPermission(user, tenantAccess, {
    globalRoles: [GLOBAL_USER_ROLES.SUPER_ADMIN], // Super admin can act as owner
    tenantRoles: [TENANT_ROLES.OWNER]
  });
}

/**
 * Check if user can perform administrative actions
 *
 * @param user - User object
 * @returns true if user has admin access
 *
 * @example
 * canPerformAdminActions(user)
 */
export function canPerformAdminActions(
  user: User | null | undefined
): boolean {
  return checkGlobalPermission(user, {
    requiredRole: GLOBAL_USER_ROLES.ADMIN
  });
}

/**
 * Get user's effective permissions for a tenant
 *
 * @param user - User object
 * @param tenantAccess - Tenant access object
 * @returns Object with permission flags
 *
 * @example
 * const perms = getEffectivePermissions(user, tenantAccess)
 * if (perms.canManageTeam) { ... }
 */
export function getEffectivePermissions(
  user: User | null | undefined,
  tenantAccess: TenantAccess | null | undefined
) {
  return {
    // Global permissions
    isGlobalAdmin: checkGlobalPermission(user, { requiredRole: GLOBAL_USER_ROLES.ADMIN }),
    isSuperAdmin: checkGlobalPermission(user, { requiredRole: GLOBAL_USER_ROLES.SUPER_ADMIN }),
    isManager: checkGlobalPermission(user, { requiredRole: GLOBAL_USER_ROLES.MANAGER }),

    // Tenant permissions
    isTenantOwner: checkTenantPermission(tenantAccess, { requiredRole: TENANT_ROLES.OWNER }),
    isTenantAdmin: checkTenantPermission(tenantAccess, { requiredRole: TENANT_ROLES.ADMIN }),
    isTenantMember: checkTenantPermission(tenantAccess, { requiredRole: TENANT_ROLES.MEMBER }),
    isTenantViewer: checkTenantPermission(tenantAccess, { requiredRole: TENANT_ROLES.VIEWER }),

    // Combined permissions
    canManageTeam: canManageTeam(user, tenantAccess),
    canTransferOwnership: isTenantOwner(user, tenantAccess),
    canPerformAdminActions: canPerformAdminActions(user),

    // Access flags
    hasGlobalAccess: !!user?.is_active,
    hasTenantAccess: !!tenantAccess?.has_access,
  };
}

/**
 * Permission validation error types
 */
export class PermissionError extends Error {
  constructor(message: string, public readonly requiredPermissions: string[]) {
    super(message);
    this.name = 'PermissionError';
  }
}

/**
 * Assert that user has required permissions, throw error if not
 *
 * @param user - User object
 * @param tenantAccess - Tenant access object
 * @param options - Permission requirements
 * @throws PermissionError if user lacks required permissions
 *
 * @example
 * assertPermission(user, tenantAccess, {
 *   tenantRoles: ['owner'],
 *   errorMessage: 'Only tenant owners can perform this action'
 * })
 */
export function assertPermission(
  user: User | null | undefined,
  tenantAccess: TenantAccess | null | undefined,
  options: CombinedPermissionOptions & { errorMessage?: string }
): void {
  const hasPermission = checkCombinedPermission(user, tenantAccess, options);

  if (!hasPermission) {
    const requiredPerms = [
      ...(options.globalRoles || []),
      ...(options.tenantRoles || []),
      ...(options.tenantPermissions || [])
    ];

    throw new PermissionError(
      options.errorMessage || 'You do not have permission to perform this action',
      requiredPerms
    );
  }
}
