import { Injectable, Logger } from '@nestjs/common';
import IORedis, { Redis } from 'ioredis';

import { CacheConst } from './cache.const';
import { convertShortTimeToSecond, generateOTP } from 'src/share/tool';
import { AppConfigService } from 'src/config/app/app-config.service';

@Injectable()
export class RedisClientService {
  private client: Redis;
  private logger: Logger = new Logger(RedisClientService.name);
  public readonly TTL_IMPORT: number = 60 * 60 * 24;
  public readonly TTL_OTP: number = 90;
  
  constructor(private configService: AppConfigService) {
    this.client = new IORedis(this.configService.redisUri);
  }

  getClient() {
    if (!this.client) {
      this.client = new IORedis(this.configService.redisUri);
    }
    return this.client;
  }

  private async delKeys(key: string) {
    try {
      const client = this.client;
      const keys = await this.client.keys(key);
      await Promise.all(keys.map((e) => client.del(e)));
    } catch (error) {
      return;
    }
  }

  async markLogin(uid: number, tid: string, prefix = 'admin') {
    try {
      const keyAccess = CacheConst.genKeyAccessToken(uid, tid, prefix);
      const keyRefresh = CacheConst.genKeyRefreshToken(uid, tid, prefix);
      const accessTokenTTL =
        convertShortTimeToSecond(this.configService.jwtExpiresIn) || 300;
      const refreshTokenTTL =
        convertShortTimeToSecond(this.configService.jwtRefreshIn) || 1000;
      await this.client.set(keyAccess, '1', 'EX', accessTokenTTL);
      await this.client.set(keyRefresh, '1', 'EX', refreshTokenTTL);
    } catch (error) {
      this.logger.error(error.message);
    }
  }

  async delMarkLogin(uid: string, tid: string, prefix = 'admin') {
    try {
      const keyAccess = CacheConst.genKeyAccessToken(uid, tid, prefix);
      const keyRefresh = CacheConst.genKeyRefreshToken(uid, tid, prefix);
      const keyPublicName = CacheConst.genKeyPubicName(uid, tid);
      await this.delKeys(keyAccess);
      await this.delKeys(keyRefresh);
      await this.delKeys(keyPublicName);
    } catch (error) {
      this.logger.error(error.message);
    }
  }

  async getPublicName(uid: string, tid: string) {
    const keyPublicName = CacheConst.genKeyPubicName(uid, tid);

    this.logger.debug(
      `${this.getPublicName.name} keyPublicName: ${keyPublicName}`,
    );

    const publicName = await this.getClient().get(keyPublicName);

    this.logger.debug(`${this.getPublicName.name} publicName: ${publicName}`);

    return publicName;
  }

  async setPublicName(uid: string | number, tid: string, name: string) {
    const keyPublicName = CacheConst.genKeyPubicName(uid, tid);

    await this.getClient().set(keyPublicName, name);
  }

  async checkMarkLogin(uid: string | number, tid: string, prefix = 'admin') {
    try {
      const keyadminAccess = await this.client.get(
        `${CacheConst.genKeyAccessToken(uid, tid, prefix)}`,
      );
      return {
        key: keyadminAccess,
        isMark: Boolean(keyadminAccess),
      };
    } catch (error) {
      this.logger.error(error.message);
      return null;
    }
  }

  async checkRefreshTokenFound(uid: string, tid: string, prefix = 'admin') {
    try {
      const keyRefresh = await this.client.get(
        `${CacheConst.genKeyRefreshToken(uid, tid, prefix)}`,
      );
      return Boolean(keyRefresh);
    } catch (error) {
      this.logger.error(error.message);
      return null;
    }
  }

  async setOTP(id: string, data: any) { 
    const otp = generateOTP();
    if (!id) {
      throw new Error('id not found');
    }

    const jsonData = JSON.stringify(data);
    const key = `${otp}:${id}`;
    await this.client.set(key, jsonData, 'EX', this.TTL_OTP);
    return otp;
  }
}
