import beautify from "js-beautify";
import Action from "../Action";
import ActionInteraction from "../ActionInteraction";
import ActionToMappingService from "../ActionToMappingService";
import Exploration from "../Exploration";
import Interaction from "../Interaction";
import IPrinter from "../IPrinter";
import Session from "../Session";
import PuppeteerActionMapper from "./PuppeteerActionMapper";

const DEFAULT_TIMEOUT = 5000;
const DEFAULT_HEADLESS = true;
export default class PuppeteerPrinter implements IPrinter {

    public printSession(session: Session, options: {
      headless?: boolean,
      timeout?: number,
    } = {headless: DEFAULT_HEADLESS, timeout: DEFAULT_TIMEOUT}): string {
      if (options.headless === undefined) {
        options.headless = DEFAULT_HEADLESS;
      }
      if (options.timeout === undefined) {
        options.timeout = DEFAULT_TIMEOUT;
      }

      const assertDeclarationFilename = "";
      const code = [this.buildTestHeader(`Session ${session.id}`, assertDeclarationFilename, options)];

      session.explorationList.forEach((exploration) => {
            code.push(this.printExploration(exploration, session, options));
        });

      code.push(this.buildTestFooter());
      return beautify(code.join(""));
    }

    public printExploration(exploration: Exploration, session: Session, options): string {
      const description = exploration.interactionList.map((interation: Interaction) => interation.toString());
      const code = [this.buildTestCaseHeader(`Exploration ${exploration.explorationNumber}: ${description}`, options)];

      exploration.interactionList.filter((interaction) => interaction instanceof ActionInteraction)
        .forEach((actionInteraction, index) => {
          code.push(this.printAction((actionInteraction as ActionInteraction).action, session, options) + "\n");
        });
      code.push(this.buildTestCaseFooter());

      return code.join("");
    }

    public printAction(action: Action, session: Session, options): string {
        const mapping = ActionToMappingService.findMappingForAction(action, session.webSite.mappingList);
        return PuppeteerActionMapper.getActionCode(action, session, mapping, options);
    }

    private buildTestHeader(description, assertDeclarationFilename: string, options): string {
        return `
        const puppeteer = require("puppeteer")
        const expect = require("chai").expect
        ${assertDeclarationFilename ? `const Asserts = require("${assertDeclarationFilename}")` : ""}
        let browser;
        let page;
        let element;
        let tag;

        before(async () => {
          browser = await puppeteer.launch({timeout: 10000, headless: ${options.headless}});
          page = await browser.newPage();
        })

        after(() => {
          return browser.close()
        })

        describe("${description}", function () {
          this.timeout(${options.timeout});

        `;
      }

    private buildTestFooter(): string {
        return `
        });`;
    }

    private buildTestCaseHeader(description, options): string {
        return `it('${description}', async () => {`;
    }

    private buildTestCaseFooter(): string {
        return `
      });`;
    }
}
