import {
  CanActivate,
  ExecutionContext,
  ForbiddenException,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { UserRepository } from 'src/user/user.repository';
import { isEmpty } from '../helper';
import { errorResponse } from '../errors/response-config';
import { commonMessage } from '../messages';
import * as moment from 'moment';
import { adminRoute, userRoute } from '../routes';

@Injectable()
export class AuthGuardMiddleware implements CanActivate {
  constructor(private readonly authenticationRepository: UserRepository) {}

  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return this.validateRequest(request);
  }

  async validateRequest(req: Request): Promise<boolean> {
    const token = req.headers['authorization'];

    if (isEmpty(token)) {
      throw new UnauthorizedException(
        errorResponse(401, commonMessage.tokenNotFound),
      );
    }

    const user: any = await this.authenticationRepository.findUserTokenExpiry(
      token,
    );

    if (isEmpty(user)) {
      throw new UnauthorizedException(
        errorResponse(401, commonMessage.invalidToken),
      );
    }

    const accessTokenExpirationTime = moment(user.access_token_expiry)
      .utc(true)
      .valueOf();
    const currentTime = moment().utc().valueOf();

    if (currentTime >= accessTokenExpirationTime) {
      throw new UnauthorizedException(
        errorResponse(401, commonMessage.tokenExpired),
      );
    }

    const isAdmin = user.is_admin === 1;

    const url = new URL(req.url, `http://${req.headers['host']}`);
    const route = url.pathname;

    const normalizedRoute = route.replace(
      /\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g,
      '/:id',
    );

    if (isAdmin) {
      if (adminRoute.includes(normalizedRoute)) {
        return true;
      }
    } else {
      if (userRoute.includes(normalizedRoute)) {
        return true;
      }
    }

    throw new ForbiddenException(
      errorResponse(403, commonMessage.accessDenied),
    );
  }
}
