import { ApplicationContext, StringUtils } from '@sankhyalabs/core';

class PrintUtils {
  constructor() {
    this.appletImpressao = new AppletImpressao();
    const application = ApplicationContext.getContextValue("__SNK__APPLICATION__");
    application.getDataFetcher().then(result => this.DataFetcher = result);
  }
  static getInstance() {
    if (!PrintUtils.instance) {
      PrintUtils.instance = new PrintUtils();
    }
    return PrintUtils.instance;
  }
  async processPendingPrinting(transactionId) {
    await this.findPendingPrints(transactionId);
  }
  async findPendingPrints(transactionId) {
    const requestBody = {
      transactionIds: [
        {
          transactionId: {
            $: transactionId
          }
        }
      ]
    };
    Promise.resolve(this.DataFetcher).then(async (res) => {
      res = await Promise.resolve(this.DataFetcher);
      while (!res || !res.application) {
        res = await Promise.resolve(this.DataFetcher);
      }
      const response = await res.application.callServiceBroker(PrintUtils.SERVICE_FIND_PENDING_PRINTS, JSON.stringify(requestBody));
      const printJobData = this.parsePrintData(response);
      const pendingPrints = await this.processDirectPrint(printJobData);
      if (!pendingPrints.length)
        return;
      this.openSnkPrintSelector(Object.assign(Object.assign({}, printJobData), { pendingPrinters: pendingPrints }));
    });
  }
  getLocalPrinters() {
    const localPrinters = this.appletImpressao.findLocalPrinters();
    const printers = [];
    for (const printerName of localPrinters) {
      printers.push({
        nome: printerName,
        printerUri: PrintUtils.LOCAL_SERVER_URI + printerName,
        isLocal: true
      });
    }
    return printers;
  }
  parsePrintData(payload) {
    const pendingPrintJobData = payload.pendindPrintJobData;
    if (!pendingPrintJobData)
      return undefined;
    let { printServers: { printServer }, pendingPrinters: { pendingPrinter } } = pendingPrintJobData;
    printServer = printServer ? (Array.isArray(printServer) ? printServer : [printServer]) : [];
    pendingPrinter = pendingPrinter ? (Array.isArray(pendingPrinter) ? pendingPrinter : [pendingPrinter]) : [];
    const normalizedPrintServer = printServer.map(item => {
      const printerList = Array.isArray(item.printerLocation) ? item.printerLocation : [item.printerLocation];
      return {
        printServerUri: item.printServerUri,
        printerList: printerList.map(this.normalize)
      };
    });
    const normalizedPendingPrinter = pendingPrinter.map(this.normalize);
    return {
      transactionId: pendingPrintJobData.transactionId.$,
      printServers: normalizedPrintServer,
      pendingPrinters: normalizedPendingPrinter,
      printServerActive: pendingPrintJobData.printServers.printServerActive === 'true'
    };
  }
  normalize(object) {
    const normalizedObject = {};
    Object.keys(object).forEach((key) => {
      normalizedObject[key] = PrintUtils.ENCODED_PROPERTIES.includes(key) ? window.atob(object[key].$) : object[key].$;
    });
    return normalizedObject;
  }
  processLocalPrinting(localPrintings) {
    const transactionIds = localPrintings.map((item) => item.transactionId);
    this.appletImpressao.doLocalPrinting(transactionIds.join());
  }
  async openSnkPrintSelector(printJobData) {
    let printSelector = document.querySelector('snk-print-selector');
    if (!printSelector) {
      printSelector = document.createElement('snk-print-selector');
      printSelector.setAttribute('id', StringUtils.generateUUID());
      window.document.body.appendChild(printSelector);
    }
    const { selectedPrinter, saveSubstitute } = await printSelector.openPrintSelector(printJobData);
    if (!selectedPrinter)
      return;
    const params = {
      transactionId: printJobData.transactionId,
      pendingPrinters: printJobData.pendingPrinters,
      printerUri: selectedPrinter.printerUri,
      saveSubstitutePrinters: saveSubstitute
    };
    await this.saveSubstitutePrinter(params);
  }
  async saveSubstitutePrinter(params) {
    const { transactionId, pendingPrinters, printerUri, saveSubstitutePrinters = false } = params;
    const parsedPendingPrinters = pendingPrinters.map((item) => ({
      printerUri: {
        $: item.printerUri || printerUri
      },
      originalPrinterName: {
        $: item.originalPrinterName
      },
      printJobCount: {
        $: item.printJobCount
      },
      docType: {
        $: item.docType
      },
      docTypeDescription: {
        $: item.docTypeDescription
      }
    }));
    const requestBody = {
      substitutePrintersRequest: {
        substitutePrinters: {
          pendingPrinter: parsedPendingPrinters
        },
        saveSubstitutePrinters: {
          $: saveSubstitutePrinters.toString()
        },
        transactionId: {
          $: transactionId
        }
      }
    };
    Promise.resolve(this.DataFetcher).then(async (res) => {
      await res.application.callServiceBroker(PrintUtils.SERVICE_SAVE_SUBSTITUTE_PRINTER, JSON.stringify(requestBody));
    });
  }
  async processDirectPrint(printJobData) {
    const isWCRunning = this.appletImpressao.checkWebConnection();
    if (!isWCRunning) {
      return [];
    }
    const localPrinters = this.getLocalPrinters();
    if (!localPrinters.length)
      return [];
    const directPendingPrints = [];
    const pendingPrints = printJobData.pendingPrinters.filter(item => {
      const defaultPrinterName = this.getDefaultPrinterName(item.originalPrinterName);
      const isDirectPrint = !!localPrinters.find(localPrinter => localPrinter.nome === defaultPrinterName) || this.isPrinterNameAFile(defaultPrinterName);
      if (isDirectPrint) {
        directPendingPrints.push(Object.assign(Object.assign({}, item), { printerUri: PrintUtils.LOCAL_SERVER_URI + defaultPrinterName }));
        return false;
      }
      return true;
    });
    if (!directPendingPrints.length)
      return pendingPrints;
    const params = {
      transactionId: printJobData.transactionId,
      pendingPrinters: directPendingPrints
    };
    await this.saveSubstitutePrinter(params);
    return pendingPrints;
  }
  getDefaultPrinterName(printerName) {
    const defaultPrinterName = this.appletImpressao.getDefaultPrinter();
    const isDefaultPrinterName = /padr[aã]o/ig.test(printerName);
    return (defaultPrinterName && isDefaultPrinterName) ? defaultPrinterName : printerName;
  }
  isPrinterNameAFile(printerName) {
    const regExp = new RegExp("^(\w:)?[/\\\w\s]+?\.\w{3}$", "mis");
    return regExp.test(printerName);
  }
}
PrintUtils.SERVICE_FIND_PENDING_PRINTS = 'mge@PrintServiceSP.findPendingPrinters';
PrintUtils.SERVICE_SAVE_SUBSTITUTE_PRINTER = 'mge@PrintServiceSP.saveSubstitutePrinter';
PrintUtils.LOCAL_SERVER_URI = 'LOCAL:0/';
PrintUtils.ENCODED_PROPERTIES = ['printerName', 'printerUri'];
class AppletImpressao {
  constructor() {
    this.appletImpressao = document.getElementById('centralNotaAppletImpressao');
  }
  checkWebConnection() {
    if (!this.appletImpressao)
      return false;
    if (!this.appletImpressao.testRunningWC)
      return false;
    const isRunning = this.appletImpressao.testRunningWC();
    return !!isRunning;
  }
  findLocalPrinters() {
    if (!this.appletImpressao)
      return [];
    const localPrinters = this.appletImpressao.getLocalPrinters();
    if (!localPrinters)
      return [];
    return localPrinters.split(",");
  }
  doLocalPrinting(transactionIds) {
    if (!this.appletImpressao)
      return;
    this.appletImpressao.doLocalPrinting(transactionIds);
  }
  getDefaultPrinter() {
    if (!this.appletImpressao)
      return '';
    return this.appletImpressao.getPrintDefault();
  }
}

export { PrintUtils as P };
