import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { IS_PUBLIC_KEY, PERMISSIONS_KEY } from '../../shared/common/decorators';
import { appForbidden, appUnauthorized } from '../../shared/common/app-error';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  constructor(private reflector: Reflector) {
    super();
  }

  canActivate(context: ExecutionContext) {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (isPublic) return true;
    return super.canActivate(context);
  }

  handleRequest<TUser = unknown>(err: Error | null, user: TUser): TUser {
    if (err || !user) throw err || appUnauthorized('INVALID_CREDENTIALS');
    return user;
  }
}

@Injectable()
export class PermissionsGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const required = this.reflector.getAllAndOverride<string[]>(PERMISSIONS_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    const { user } = context.switchToHttp().getRequest();
    if (!user || user.userType !== 'ADMIN') {
      throw appForbidden('ADMIN_ACCESS_REQUIRED');
    }
    if (user.role === 'SUPER_ADMIN') return true;

    if (!required?.length) {
      throw appForbidden('INSUFFICIENT_PERMISSIONS');
    }

    const userPerms: string[] = user.permissions ?? [];
    const hasAccess = required.some((p) => userPerms.includes(p));
    if (!hasAccess) throw appForbidden('INSUFFICIENT_PERMISSIONS');
    return true;
  }
}

export function UserTypeGuard(...types: string[]) {
  @Injectable()
  class MixedUserTypeGuard implements CanActivate {
    canActivate(context: ExecutionContext): boolean {
      const { user } = context.switchToHttp().getRequest();
      if (!types.includes(user?.userType)) {
        throw appForbidden('ACCESS_DENIED_PORTAL');
      }
      return true;
    }
  }
  return MixedUserTypeGuard;
}

@Injectable()
export class PlayerGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (isPublic) return true;
    const { user } = context.switchToHttp().getRequest();
    if (user?.userType !== 'PLAYER') throw appForbidden('PLAYER_ACCESS_ONLY');
    return true;
  }
}

@Injectable()
export class AdminGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const { user } = context.switchToHttp().getRequest();
    if (user?.userType !== 'ADMIN') throw appForbidden('ADMIN_ACCESS_ONLY');
    return true;
  }
}

@Injectable()
export class AgentGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const { user } = context.switchToHttp().getRequest();
    if (user?.userType !== 'AGENT') throw appForbidden('AGENT_ACCESS_ONLY');
    return true;
  }
}
