import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-http-bearer';
import {
  SYSTEM_USER_ID,
  SYSTEM_USER_ROLE_ID,
  UserDTO,
} from '../common/dto/user-dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { AuthApiService } from '../common/auth-api/auth-api.service';
import { AppTokenEntity } from '../entities/app-token.entity';
import { ConfigService } from '@nestjs/config';
import { SiteLanguage } from '../common/types/site-language';

const USER_CACHE_TIME = 5 * 60 * 1000;

type UserCacheEntry = {
  time: number;
  user: UserDTO;
};

@Injectable()
export class AppTokenStrategy extends PassportStrategy(Strategy, 'app-token') {
  private cachedUsers = new Map<number, UserCacheEntry>();

  constructor(
    @InjectRepository(AppTokenEntity)
    private readonly appTokenRepo: Repository<AppTokenEntity>,
    private readonly authApiService: AuthApiService,
    private _configService: ConfigService,
  ) {
    super();
  }

  private async getUserDTO(userId: number): Promise<UserDTO | null> {
    const exists = this.cachedUsers.get(userId);
    const now = Date.now();
    if (exists && exists.time + USER_CACHE_TIME > now) {
      return exists.user;
    }

    const user = await this.authApiService.getUserInfoByid(userId);
    if (!user) return null;

    const keysToDelete = [];
    for (const [userId, entry] of this.cachedUsers) {
      if (entry.time + USER_CACHE_TIME < now) {
        keysToDelete.push(userId);
      }
    }

    for (const userId of keysToDelete) {
      this.cachedUsers.delete(userId);
    }

    this.cachedUsers.set(userId, {
      time: now,
      user,
    });

    return user;
  }

  async validate(payload: any): Promise<UserDTO> {
    const system_app_token = this._configService.get('server.systemAppToken');
    if (system_app_token && payload === system_app_token) {
      return {
        email: '',
        id: SYSTEM_USER_ID,
        language: SiteLanguage.EN,
        licenses: [],
        name: 'system',
        roleId: SYSTEM_USER_ROLE_ID,
      };
    }

    const foundParams = await this.appTokenRepo.findOne({
      token: payload,
    });
    if (!foundParams) {
      throw new UnauthorizedException('Invalid app token');
    }

    const user = await this.getUserDTO(foundParams.userId);
    if (!user) {
      throw new UnauthorizedException('User not found');
    }

    return user;
  }
}
