import { AdminUser } from '../entities/AdminUser';
import { AuthorizationError } from '../errors/AdminDomainError';

/**
 * Domain service for authorization checks
 * Stateless service that enforces access control rules
 */
export class AuthorizationService {
  /**
   * Check if an actor can perform an action on a target user
   */
  static canPerformAction(
    actor: AdminUser,
    action: 'view' | 'manage' | 'modify_roles' | 'change_status' | 'delete',
    target?: AdminUser
  ): boolean {
    // Actors must be system admins and active
    if (!actor.isSystemAdmin() || !actor.isActive()) {
      return false;
    }

    switch (action) {
      case 'view':
        return this.canView(actor, target);
      case 'manage':
        return this.canManage(actor, target);
      case 'modify_roles':
        return this.canModifyRoles(actor, target);
      case 'change_status':
        return this.canChangeStatus(actor, target);
      case 'delete':
        return this.canDelete(actor, target);
      default:
        return false;
    }
  }

  /**
   * Check if actor can view target user
   */
  private static canView(actor: AdminUser, target?: AdminUser): boolean {
    if (!target) {
      // Viewing list - only admins can view
      return actor.isSystemAdmin();
    }

    // Can always view self
    if (actor.id.equals(target.id)) {
      return true;
    }

    // Owner can view everyone
    if (actor.hasRole('owner')) {
      return true;
    }

    // Admin can view non-admin users
    if (actor.hasRole('admin')) {
      return !target.isSystemAdmin();
    }

    return false;
  }

  /**
   * Check if actor can manage target user
   */
  private static canManage(actor: AdminUser, target?: AdminUser): boolean {
    if (!target) {
      return false;
    }

    // Can always manage self
    if (actor.id.equals(target.id)) {
      return true;
    }

    // Owner can manage everyone
    if (actor.hasRole('owner')) {
      return true;
    }

    // Admin can manage non-admin users
    if (actor.hasRole('admin')) {
      return !target.isSystemAdmin();
    }

    return false;
  }

  /**
   * Check if actor can modify roles of target user
   */
  private static canModifyRoles(actor: AdminUser, target?: AdminUser): boolean {
    if (!target) {
      return false;
    }

    // Only owner can modify roles
    if (!actor.hasRole('owner')) {
      return false;
    }

    // Cannot modify own roles (prevents accidental lockout)
    if (actor.id.equals(target.id)) {
      return false;
    }

    return true;
  }

  /**
   * Check if actor can change status of target user
   */
  private static canChangeStatus(actor: AdminUser, target?: AdminUser): boolean {
    if (!target) {
      return false;
    }

    // Cannot change own status
    if (actor.id.equals(target.id)) {
      return false;
    }

    // Owner can change anyone's status
    if (actor.hasRole('owner')) {
      return true;
    }

    // Admin can change user status but not other admins/owners
    if (actor.hasRole('admin')) {
      return !target.isSystemAdmin();
    }

    return false;
  }

  /**
   * Check if actor can delete target user
   */
  private static canDelete(actor: AdminUser, target?: AdminUser): boolean {
    if (!target) {
      return false;
    }

    // Cannot delete self
    if (actor.id.equals(target.id)) {
      return false;
    }

    // Owner can delete anyone
    if (actor.hasRole('owner')) {
      return true;
    }

    // Admin can delete users but not admins/owners
    if (actor.hasRole('admin')) {
      return !target.isSystemAdmin();
    }

    return false;
  }

  /**
   * Enforce authorization - throws if not authorized
   */
  static enforce(
    actor: AdminUser,
    action: 'view' | 'manage' | 'modify_roles' | 'change_status' | 'delete',
    target?: AdminUser
  ): void {
    if (!this.canPerformAction(actor, action, target)) {
      const actionLabel = action.replace('_', ' ');
      const targetLabel = target ? `user ${target.email.value}` : 'user list';
      throw new AuthorizationError(
        `User ${actor.email.value} is not authorized to ${actionLabel} ${targetLabel}`
      );
    }
  }

  /**
   * Check if actor can list users
   */
  static canListUsers(actor: AdminUser): boolean {
    return actor.isSystemAdmin() && actor.isActive();
  }

  /**
   * Check if actor can create users
   */
  static canCreateUsers(actor: AdminUser): boolean {
    // Only owner can create users with admin roles
    // Admin can create regular users
    return actor.isSystemAdmin() && actor.isActive();
  }

  /**
   * Check if actor can assign a specific role
   */
  static canAssignRole(actor: AdminUser, roleToAssign: string, target?: AdminUser): boolean {
    // Only owner can assign owner or admin roles
    if (roleToAssign === 'owner' || roleToAssign === 'admin') {
      return actor.hasRole('owner');
    }

    // Admin can assign user role
    if (roleToAssign === 'user') {
      // Admin can assign user role to non-admin users
      // Owner can assign user role to anyone
      if (actor.hasRole('owner')) {
        return true;
      }
      if (actor.hasRole('admin')) {
        if (!target) {
          return true;
        }
        return !target.isSystemAdmin();
      }
    }

    return false;
  }

  /**
   * Get permissions for actor
   */
  static getPermissions(actor: AdminUser): string[] {
    const permissions: string[] = [];

    if (!actor.isSystemAdmin() || !actor.isActive()) {
      return permissions;
    }

    // Base permissions for all admins
    permissions.push(
      'users.view',
      'users.list'
    );

    // Admin permissions
    if (actor.hasRole('admin')) {
      permissions.push(
        'users.manage',
        'users.status.change',
        'users.create',
        'users.delete'
      );
    }

    // Owner permissions
    if (actor.hasRole('owner')) {
      permissions.push(
        'users.manage',
        'users.roles.modify',
        'users.status.change',
        'users.create',
        'users.delete',
        'users.export'
      );
    }

    return permissions;
  }

  /**
   * Check if actor has specific permission
   */
  static hasPermission(actor: AdminUser, permission: string): boolean {
    const permissions = this.getPermissions(actor);
    return permissions.includes(permission);
  }

  /**
   * Enforce permission - throws if not authorized
   */
  static enforcePermission(actor: AdminUser, permission: string): void {
    if (!this.hasPermission(actor, permission)) {
      throw new AuthorizationError(
        `User ${actor.email.value} does not have permission: ${permission}`
      );
    }
  }
}