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

@Injectable()
export class AuthMiddleware 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> {
    // Extract the token from the request headers
    const deviceType = req.headers['device-type'];

    if (deviceType === 'web') {
      const token = req.headers['authorization'];

      if (isEmpty(token)) {
        throw new UnauthorizedException(
          errorResponse(401, lan('middleware.token_not_found')),
        );
      }

      // Find the user with this token
      const user = await this.authenticationRepository.findUserTokenExpiry(
        token,
      );

      if (isEmpty(user)) {
        throw new UnauthorizedException(
          errorResponse(401, lan('middleware.invalid_token')),
        );
      }

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

      // Check if the token has expired
      if (currentTime >= accessTokenExpirationTime) {
        throw new UnauthorizedException(
          errorResponse(401, lan('middleware.token_expire')),
        );
      }

      // If all checks pass, return true
      return true;
    } else if (deviceType === 'mobile') {
      const deviceId = req.headers['device-id'];
      const token = req.headers['authorization'];

      if (isEmpty(deviceId)) {
        throw new UnauthorizedException(
          errorResponse(401, lan('device-id.not_found')),
        );
      }

      if (isEmpty(token)) {
        throw new UnauthorizedException(
          errorResponse(401, lan('middleware.token_not_found')),
        );
      }

      const user = await this.authenticationRepository.findAppUserTokenExpiry(
        token,
      );

      if (isEmpty(user)) {
        throw new UnauthorizedException(
          errorResponse(401, lan('middleware.invalid_token')),
        );
      }

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

      // Check if the token has expired
      if (currentTime >= accessTokenExpirationTime) {
        throw new UnauthorizedException(
          errorResponse(401, lan('middleware.token_expire')),
        );
      }

      if (deviceId !== user.device_id) {
        throw new UnauthorizedException(
          errorResponse(401, lan('device-id.invalid')),
        );
      }

      // If all checks pass, return true
      return true;
    } else {
      return false;
    }
  }
}
