import * as plugins from '../plugins.js';
import { Reception } from './classes.reception.js';
import { Jwt } from './classes.jwt.js';

export class JwtManager {
  public receptionRef: Reception;
  public get db() {
    return this.receptionRef.db.smartdataDb;
  }

  public smartjwtInstance = new plugins.smartjwt.SmartJwt();
  public jwtManagerEasyStore: plugins.smartdata.EasyStore<{
    jwtJsonKeypair: plugins.tsclass.network.IJwtKeypair;
  }>;
  public blockedJwtIdList: string[] = [];

  public typedrouter = new plugins.typedrequest.TypedRouter();

  public CJwt = plugins.smartdata.setDefaultManagerForDoc(this, Jwt);

  constructor(receptionRefArg: Reception) {
    this.receptionRef = receptionRefArg;
    this.receptionRef.typedrouter.addTypedRouter(this.typedrouter);
    this.typedrouter.addTypedHandler<plugins.idpInterfaces.request.IReq_RefreshJwt>(
      new plugins.typedrequest.TypedHandler(
        'refreshJwt',
        async (requestArg) => {
          const resultJwt = await Jwt.createJwtForRefreshToken(this, requestArg.refreshToken);
          return {
            status: 'loggedIn',
            jwt: resultJwt,
          };
        }
      )
    );
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_GetPublicKeyForValidation>(
        'getPublicKeyForValidation',
        async (requestArg) => {
          // TODO control backend token
          return {
            publicKeyPem: this.smartjwtInstance.getKeyPairAsJson().publicPem,
          };
        }
      )
    );

    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<plugins.idpInterfaces.request.IReq_PushOrGetJwtIdBlocklist>(
        'pushOrGetJwtIdBlocklist',
        async (requestArg) => {
          // TODO control backend token
          return {
            blockedJwtIds: this.blockedJwtIdList
          };
        }
      )
    );
  }

  public async pushPublicKeyToClients() {
    const targetConnections =
      await this.receptionRef.options.websiteServer.typedserver.typedsocket.findAllTargetConnectionsByTag<plugins.idpInterfaces.tags.ITag_LolePubapi>(
        'lole-reception',
        {
          backendToken: '',
        }
      );
    for (const targetConnection of targetConnections) {
      const pushPublicKeyTr =
        this.receptionRef.options.websiteServer.typedserver.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_PushPublicKeyForValidation>(
          'pushPublicKeyForValidation',
          targetConnection
        );
      await pushPublicKeyTr.fire({
        publicKeyPem: this.smartjwtInstance.getKeyPairAsJson().publicPem,
      });
    }
  }

  public async pushBlockedJwtIdListToClients() {
    const targetConnections =
      await this.receptionRef.options.websiteServer.typedserver.typedsocket.findAllTargetConnectionsByTag<plugins.idpInterfaces.tags.ITag_LolePubapi>(
        'lole-reception',
        {
          backendToken: '',
        }
      );
      for (const targetConnection of targetConnections) {
        const pushPublicKeyTr =
          this.receptionRef.options.websiteServer.typedserver.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_PushOrGetJwtIdBlocklist>(
            'pushOrGetJwtIdBlocklist',
            targetConnection
          );
        await pushPublicKeyTr.fire({
          blockedJwtIds: this.blockedJwtIdList
        });
      }
  }

  public async start() {
    this.jwtManagerEasyStore = await this.receptionRef.db.smartdataDb.createEasyStore(
      'jwtManagerEasyStore'
    );
    await this.smartjwtInstance.init();
    let existingKeyPair = await this.jwtManagerEasyStore.readKey('jwtJsonKeypair');
    if (!existingKeyPair) {
      await this.rotateKeyPair();
    }
    existingKeyPair = await this.jwtManagerEasyStore.readKey('jwtJsonKeypair');
    this.smartjwtInstance.setKeyPairAsJson(existingKeyPair);
  }

  public async rotateKeyPair() {
    await this.smartjwtInstance.createNewKeyPair();
    await this.jwtManagerEasyStore.writeKey(
      'jwtJsonKeypair',
      this.smartjwtInstance.getKeyPairAsJson()
    );
    await this.pushPublicKeyToClients();
  }

  public async verifyJWTAndGetData(jwtArg: string): Promise<Jwt> {
    const jwtData: plugins.idpInterfaces.data.IJwt = await this.smartjwtInstance.verifyJWTAndGetData(jwtArg);
    const jwt = await Jwt.getInstance({
      id: jwtData.id,
    });
    if (jwt.blocked) {
      return null;
    }
    if (jwt) {
      const loginSession = await jwt.getLoginSession();
      if (!loginSession) {
        await jwt.block();
        this.blockedJwtIdList.push(jwt.id);
        return null;
      }
    }
    return jwt;
  }
}
