import * as cryptoJs from 'crypto-js';
import { Validators } from '@angular/forms';
import { inject, Injectable } from '@angular/core';
import { DatePipe, DecimalPipe } from '@angular/common';

import { Api } from '../../../providers/api/api';
import { ValidateNumbersNamedParameters, ValidateUnicodeNamedParameters, ValidatorsNamedParameters } from './named-parameters.model';

@Injectable()
export class FunctionService
{
  private readonly datepipe = inject(DatePipe);
  private readonly decimalpipe = inject(DecimalPipe);

  public encrypt(data: any): string
  {
    if (!Api.secretKey)
      Api.secretKey = localStorage.getItem('EaseO2C.storage1')

    data = cryptoJs.AES.encrypt(data, atob(Api.secretKey));
    data = data.toString();
    return data;
  }

  public decrypt(data: any): string
  {
    if (!Api.secretKey)
      Api.secretKey = localStorage.getItem('EaseO2C.storage1')

    data = cryptoJs.AES.decrypt(data, atob(Api.secretKey));
    data = data.toString(cryptoJs.enc.Utf8);
    return data;
  }

  public cipherData(data: string): string
  {
    if (!Api.cipherSecretKey)
      Api.cipherSecretKey = localStorage.getItem('EaseO2C.storage2')

    const key = cryptoJs.enc.Utf8.parse(atob(Api.cipherSecretKey));
    let encrypted = cryptoJs.AES.encrypt(cryptoJs.enc.Utf8.parse(data), key,
      {
        keySize: 128 / 8,
        iv: key,
        mode: cryptoJs.mode.CBC,
        padding: cryptoJs.pad.Pkcs7
      });

    return encrypted.toString();
  }

  public decipherData(data: any): string
  {
    if (!Api.cipherSecretKey)
      Api.cipherSecretKey = localStorage.getItem('EaseO2C.storage2')

    const keyParsed = cryptoJs.enc.Utf8.parse(atob(Api.cipherSecretKey));
    const decrypted = cryptoJs.AES.decrypt(data, keyParsed, {
      keySize: 128 / 8,
      iv: keyParsed,
      mode: cryptoJs.mode.CBC,
      padding: cryptoJs.pad.Pkcs7
    });
    data = decrypted.toString(cryptoJs.enc.Utf8);

    return data;
  }

  public getDate(date: any): string
  {
    if (date == undefined)
      return null;

    let offsetMnts = date.getTimezoneOffset();
    let newDate = new Date(Math.abs(date.getFullYear()), date.getMonth(), date.getDate(), 0, 0, 0, 0);
    newDate.setMinutes(-1 * offsetMnts);

    return newDate.toISOString();
  }

  public getDatetime(date: any): string
  {
    if (date == undefined)
      return null;

    let offsetMnts = (-1 * date.getTimezoneOffset()) + date.getMinutes();
    let newDate = new Date(Math.abs(date.getFullYear()), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
    newDate.setMinutes(offsetMnts);
    return newDate.toISOString();
  }

  public mergeTime(date: any, time: Date): string
  {
    if (date == undefined)
      return null;

    let offsetMnts = (-1 * time.getTimezoneOffset()) + time.getMinutes();
    let newDate = new Date(Math.abs(date.getFullYear()), date.getMonth(), date.getDate(), time.getHours(), time.getMinutes(), time.getSeconds(), time.getMilliseconds());
    newDate.setMinutes(offsetMnts);

    return newDate.toISOString();
  }

  public getFirstDayOfMonth(year: number, month: number): Date
  {
    return new Date(year, month, 1);
  }

  public getLastDayOfMonth(year: number, month: number): Date
  {
    return new Date(year, month + 1, 0);
  }

  public toFormattedDate(date: any, formatOption: string = 'dd-MMM-yyyy'): string
  {
    if (date == undefined)
      return '';

    return this.datepipe.transform(date, formatOption);
  }

  public toFormattedNumber(value: any, digitsInfo: string = '0.2-2'): string
  {
    value = +(value || '0');

    return this.decimalpipe.transform(value, digitsInfo);
  }

  public setValidatorsToToControl({ control, required, regExp, minLen, maxLen, minValue, maxValue }: ValidatorsNamedParameters): void
  {
    let validators: any = [];
    control.clearValidators();

    if (required === 1)
      validators.push(Validators.required);

    if (regExp)
      validators.push(Validators.pattern(regExp));

    if (minLen && minLen > 0)
      validators.push(Validators.minLength(minLen));

    if (maxLen && maxLen > 0)
      validators.push(Validators.maxLength(maxLen));

    if (minValue != undefined && minValue != null)
      validators.push(Validators.min(minValue));

    if (maxValue != undefined && maxValue != null)
      validators.push(Validators.max(maxValue));

    control.setValidators(validators);
    control.updateValueAndValidity();
  }

  public validateNumbers({ event, includeDecimal, includePlus, includeMinus, includeComma, includeSpace }: ValidateNumbersNamedParameters): boolean
  {
    //Plus     (+)  => Ascii 43
    //Minus    (-)  => Ascii 43
    //Comma    (,)  => Ascii 43
    //Space    ( )  => Ascii 43
    //Decimal  (.)  => Ascii 46
    //Numbers (0-9) => Ascii 48-57 

    const charCode = event.which ? event.which : event.keyCode;
    if (includeDecimal && includePlus && includeMinus && includeComma && includeSpace)
    {
      if (charCode == 32 || charCode == 43 || charCode == 44 || charCode == 45 || charCode == 46 || (charCode >= 48 && charCode <= 57))
      {
        return true;
      }
      else
      {
        event.preventDefault();
        return false;
      }
    }
    else if (includePlus && includeMinus && includeComma && includeSpace)
    {
      if (charCode == 32 || charCode == 43 || charCode == 44 || charCode == 45 || (charCode >= 48 && charCode <= 57))
      {
        return true;
      }
      else
      {
        event.preventDefault();
        return false;
      }
    }
    else if (includeDecimal && includePlus)
    {
      if (charCode == 43 || charCode == 46 || (charCode >= 48 && charCode <= 57))
      {
        return true;
      }
      else
      {
        event.preventDefault();
        return false;
      }
    }
    else if (includeDecimal)
    {
      if (charCode == 46 || (charCode >= 48 && charCode <= 57))
      {
        return true;
      }
      else
      {
        event.preventDefault();
        return false;
      }
    }
    else if (includePlus)
    {
      if (charCode == 43 || (charCode >= 48 && charCode <= 57))
      {
        return true;
      }
      else
      {
        event.preventDefault();
        return false;
      }
    }
    else if (charCode >= 48 && charCode <= 57)
    {
      return true;
    }
    else
    {
      event.preventDefault();
      return false;
    }
  }

  public unicodeBangla({ event, includeNewline }: ValidateUnicodeNamedParameters): boolean
  {
    const minUniCode = 0x0980;
    const maxUniCode = 0x09FF;
    const minAUpCode = 0x0041;
    const maxAUpCode = 0x005A;
    const minALwCode = 0x0061;
    const maxALwCode = 0x007A;
    const charCode = event.which ?? event.keyCode;
    const charCodes: Array<number> = includeNewline ? [10, 13, 32, 35, 44, 45, 47, 124] : [32, 35, 44, 45, 47, 124];
    if (charCodes.includes(charCode)
      || (charCode >= minUniCode && charCode <= maxUniCode)
      || (charCode >= minAUpCode && charCode <= maxAUpCode)
      || (charCode >= minALwCode && charCode <= maxALwCode))
    {
      return true;
    }
    else
    {
      event.preventDefault();
      return false;
    }
  }

  public getCOALevelIdName(levelId: number): string
  {
    switch (levelId)
    {
      case 2:
        return 'Group';

      case 3:
        return 'Subgroup';

      case 4:
        return 'Posting';

      default:
        return 'Control';
    }
  }

  public getPHLevelIdName(levelId: number, setting: any): string
  {
    switch (levelId)
    {
      case 2:
        return setting?.label09;

      case 3:
        return setting?.label10;

      case 4:
        return setting?.label11;

      case 5:
        return setting?.label12;

      case 6:
        return setting?.label13;

      case 7:
        return setting?.label14;

      case 8:
        return setting?.label15;

      case 9:
        return setting.label16;

      case 10:
        return setting?.label17;

      default:
        return setting?.label08;
    }
  }

  public getCHLevelIdName(levelId: number, setting: any): string
  {
    switch (levelId)
    {
      case 2:
        return setting?.label08;

      case 3:
        return setting?.label09;

      default:
        return setting?.label07;
    }
  }

  public getMHLevelIdName(levelId: number, setting: any): string
  {
    switch (levelId)
    {
      case 2:
        return setting?.label08;

      case 3:
        return setting?.label09;

      case 4:
        return setting?.label10;

      case 5:
        return setting?.label11;

      case 6:
        return setting?.label12;

      case 7:
        return setting?.label13;

      case 8:
        return setting?.label14;

      case 9:
        return setting.label15;

      case 10:
        return setting?.label16;

      default:
        return setting?.label07;
    }
  }


  public IsNumber(inputValue?: any): boolean
  {
    const numeric: number = Number(inputValue);

    return isNaN(numeric);
  }

  public base64ToBlob(base64String: any, contentType: string = '')
  {
    const byteCharacters = atob(base64String);
    const byteArrays = [];

    for (let i = 0; i < byteCharacters.length; i++)
    {
      byteArrays.push(byteCharacters.charCodeAt(i));
    }

    const byteArray = new Uint8Array(byteArrays);
    return new Blob([byteArray], { type: contentType });
  }

  public getColor(fieldName: string, value: string, moduleId: number = 1): string
  {
    let colorCode = '';
    if (moduleId === 1 && value && fieldName !== 'Total')
    {
      let numValue: number = 0;
      let values = value.split(' ');
      if (values.length == 2)
      {
        if (values[0].endsWith('h'))
        {
          let x = values[0].replace('h', '');
          numValue = +(x) * 60;

          x = values[1].replace('m', '');
          numValue += +(x);
        }
      }
      else if (values.length == 1)
      {
        if (values[0].endsWith('h'))
        {
          let x = values[0].replace('h', '');
          numValue = +(x) * 60;
        }
      }

      if (numValue > 480)
        colorCode = 'red'
    }

    return colorCode
  }

  public getCellColor(fieldName: string, value: number, moduleId: number = 1): string
  {
    let colorCode = '';
    if (moduleId === 1 && value && fieldName !== 'Total')
    {
      if (value > 480)
        colorCode = 'red'
    }

    return colorCode
  }

  public convertToHourMinutes(minutes: number) : string
  {
    let s: string = '';
    if (minutes >= 60)
    {
      let rem: number = minutes % 60;
      s = `${(minutes - rem) / 60}h`;
      minutes = rem;
    }

    if (minutes > 0)
    {
      if (s.length > 0)
        s += ' ';

      s += `${minutes}m`;
    }

    return s;
  }
}
