import { IS_PUBLIC_KEY, WITH_NO_ROLE, WITH_ROLE } from '@hed-hog/api';
import { getLocaleText } from '@hed-hog/api-locale';
import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Request } from 'express';
import { TokenService } from '../../token/token.service';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private token: TokenService,
    private reflector: Reflector,
  ) { }

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    const withRole = this.reflector.getAllAndOverride<boolean>(WITH_ROLE, [
      context.getHandler(),
      context.getClass(),
    ]);

    const withNoRole = this.reflector.getAllAndOverride<boolean>(WITH_NO_ROLE, [
      context.getHandler(),
      context.getClass(),
    ]);

    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);
    const locale = request.headers['locale'] || request.headers['accept-language'] || 'en';

    // If endpoint requires role/authentication and no token provided
    if (!token) {
      if (isPublic) {
        return true;
      } else if (withNoRole) {
        // @NoRole() requires authentication but no specific role — token is mandatory
        throw new UnauthorizedException(
          getLocaleText('accessDenied', locale, 'Access denied.'),
        );
      } else if (withRole) {
        // @Role() decorator requires authentication
        throw new UnauthorizedException(
          getLocaleText('accessDenied', locale, 'Access denied.'),
        );
      } else {
        // No explicit decorator, default behavior (deny)
        throw new UnauthorizedException(
          getLocaleText('accessDenied', locale, 'Access denied.'),
        );
      }
    }
    
    try {
      const payload = await this.token.verify(locale, token);

      request['auth'] = payload;
    } catch (error) {
      if (isPublic) {
        return true;
      } else {
        // Return proper 401 status with clear message for JWT errors
        const message = (error as any)?.message || (error as any)?.name || 'Unauthorized';
        throw new UnauthorizedException(message);
      }
    }

    return true;
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];

    return type === 'Bearer' ? token : undefined;
  }
}
