import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ApiResultListWithTotal } from '../common/types/api-result';
import {
  LicenseTypeDTO,
  ProjectLicenseDBDTO,
  ProjectLicenseTariffDTO,
} from './dto/license-dto';
import { LicenseTypeEntity } from '../entities/license-type.entity';
import { getDefaultLicense } from './get-project-license';
import { ProjectLicenseEntity } from '../entities/project-license.entity';
import { ApiError } from '../common/error/api-error';
import axios from 'axios';
import { decodeBufferToString } from '../utils/string-utils';
import { ApiErrorCodes } from '../common/error/api-error-codes';

@Injectable()
export class LicenseService {
  constructor(
    private configService: ConfigService,
    @InjectRepository(LicenseTypeEntity)
    private readonly licenseTypeRepo: Repository<LicenseTypeEntity>,
    @InjectRepository(ProjectLicenseEntity)
    private readonly projectLicenseRepo: Repository<ProjectLicenseEntity>,
  ) {}
  async getTariffs(): Promise<ApiResultListWithTotal<ProjectLicenseTariffDTO>> {
    const axiosParams: any = {
      method: 'POST',
      url:
        this.configService.get('supervisor.supervisorUrl') +
        'license/getTariffs',
      data: {
        key: this.configService.get('supervisor.supervisorServerKey'),
      },
    };
    const res = await axios(axiosParams);
    this._checkAxiosResult(axiosParams, res);
    const tariffs: ApiResultListWithTotal<ProjectLicenseTariffDTO> = {
      list: [],
      total: 0,
    };
    tariffs.list = [...res.data.list];
    tariffs.total = res.data.total;
    const default_license = getDefaultLicense();
    tariffs.list.push({
      id: default_license.id,
      title: default_license.title,
      licenseTypeName: default_license.name,
      monthNum: null,
      priceRub: '0',
      priceUsd: '0',
      features: default_license.features,
    });
    tariffs.total++;
    return {
      list: tariffs.list.sort((a, b) => a.id - b.id),
      total: tariffs.total,
    };
  }

  async isTrial(project_id: string): Promise<boolean> {
    const axiosParams: any = {
      method: 'POST',
      url:
        this.configService.get('supervisor.supervisorUrl') + 'license/isTrial',
      data: {
        pid: project_id,
        key: this.configService.get('supervisor.supervisorServerKey'),
      },
    };
    const res = await axios(axiosParams);
    this._checkAxiosResult(axiosParams, res);
    return res.data;
  }

  async activateTrial(project_id: string): Promise<boolean> {
    const axiosParams: any = {
      method: 'POST',
      url:
        this.configService.get('supervisor.supervisorUrl') +
        'license/activateTrial',
      data: {
        pid: project_id,
        key: this.configService.get('supervisor.supervisorServerKey'),
      },
    };
    const res = await axios(axiosParams);
    this._checkAxiosResult(axiosParams, res);
    return true;
  }

  async acceptSync(
    licenseTypes: LicenseTypeDTO[],
    projectLicenses: ProjectLicenseDBDTO[],
  ) {
    const deleting_license_types = licenseTypes.filter((lt) => lt.deletedAt);
    if (deleting_license_types.length > 0) {
      await this.licenseTypeRepo
        .createQueryBuilder()
        .where('id = ANY(:license_type_ids)', {
          license_type_ids: deleting_license_types.map((item) => item.id),
        })
        .delete()
        .execute();
    }
    const updating_license_types = licenseTypes.filter((lt) => !lt.deletedAt);
    for (const license_type of updating_license_types) {
      const new_license_type = new LicenseTypeEntity();
      new_license_type.id = license_type.id;
      new_license_type.name = license_type.name;
      new_license_type.title = license_type.title;
      new_license_type.priority = license_type.priority;
      new_license_type.features = license_type.features as any;
      await this.licenseTypeRepo.upsert([new_license_type], ['id']);
    }

    const deleting_project_licenses = projectLicenses.filter(
      (pl) => pl.deletedAt,
    );
    if (deleting_project_licenses.length > 0) {
      await this.projectLicenseRepo
        .createQueryBuilder()
        .where('id = ANY(:project_license_ids)', {
          project_license_ids: deleting_project_licenses.map((item) => item.id),
        })
        .delete()
        .execute();
    }
    const updating_project_licenses = projectLicenses.filter(
      (lt) => !lt.deletedAt,
    );
    for (const project_license of updating_project_licenses) {
      const new_project_license = new ProjectLicenseEntity();
      new_project_license.id = project_license.id;
      new_project_license.isTrial = project_license.isTrial;
      new_project_license.licenseTypeId = project_license.licenseTypeId;
      new_project_license.projectId = project_license.projectId;
      new_project_license.features = project_license.features;
      new_project_license.startAt = new Date(project_license.startAt);
      new_project_license.till = project_license.till
        ? new Date(project_license.till)
        : null;
      new_project_license.updatedAt = new Date(project_license.updatedAt);
      await this.projectLicenseRepo.upsert([new_project_license], ['id']);
    }
  }

  _checkAxiosResult(axiosParams: any, res: any) {
    if (res.status !== 200 && res.status !== 201) {
      let errorObject: any;
      if (axiosParams.responseType === 'arraybuffer') {
        const resStr = decodeBufferToString(res.data);
        errorObject = JSON.parse(resStr);
      } else {
        errorObject = res.data;
      }
      if (!errorObject.message) {
        errorObject = {
          message: JSON.stringify(errorObject),
          code: 'SERVER_ERROR',
          payload: null,
        };
      }
      throw new ApiError(
        errorObject.message,
        errorObject.code,
        errorObject.payload,
      );
    }
  }

  _checkKey(key: string) {
    const supervisor_key = this.configService.get(
      'supervisor.supervisorServerKey',
    );
    if (key !== supervisor_key) {
      throw new ApiError(
        'Error key. Access denied',
        ApiErrorCodes.ACCESS_DENIED,
      );
    }
  }
}
