import {
  DeesElement,
  property,
  html,
  customElement,
  type TemplateResult,
  css,
  domtools,
} from "@design.estate/dees-element";
import * as plugins from "../plugins.js";

export const defaultDocumentSettings: plugins.shared.interfaces.IDocumentSettings =
  {
    enableTopDraftText: true,
    enableDefaultHeader: true,
    enableDefaultFooter: true,
    enableFoldMarks: true,
    enableInvoiceContractRefSection: true,
    languageCode: "EN",
    vatGroupPositions: true,
    dateStyle: "short",
  };

import { DePage } from "./page.js";
import { DeContentInvoice } from "./contentinvoice.js";

import { demoFunc } from "./document.demo.js";
import { dedocumentSharedStyle } from "../style.js";
import type { TInvoice } from "@tsclass/tsclass/dist_ts/finance/invoice.js";

declare global {
  interface HTMLElementTagNameMap {
    "dedocument-dedocument": DeDocument;
  }
}

@customElement("dedocument-dedocument")
export class DeDocument extends DeesElement {
  public static demo = demoFunc;

  @property({
    type: String,
    reflect: true,
  })
  public format: "a4" = "a4";

  @property({
    type: Number,
    reflect: true,
  })
  public viewWidth: number = null;

  @property({
    type: Number,
    reflect: true,
  })
  public viewHeight: number = null;

  @property({
    type: Boolean,
    reflect: true,
  })
  printMode = false;

  @property({
    type: Object,
    reflect: true,
    converter: (valueArg) => {
      if (typeof valueArg === "string") {
        return plugins.smartjson.parseBase64(valueArg);
      } else {
        return valueArg;
      }
    },
  })
  public letterData: plugins.tsclass.business.TLetter;

  @property({
    type: Object,
    reflect: true,
    converter: (valueArg) => {
      if (typeof valueArg === "string") {
        return plugins.smartjson.parseBase64(valueArg);
      } else {
        return valueArg;
      }
    },
  })
  public documentSettings: plugins.shared.interfaces.IDocumentSettings =
    defaultDocumentSettings;

  constructor() {
    super();
    domtools.DomTools.setupDomTools();
  }

  public static styles = [
    domtools.elementBasic.staticStyles,
    dedocumentSharedStyle,
    css`
      :host {
        display: block;
      }

      .betweenPagesSpacer {
        height: 16px;
      }
    `,
  ];

  public render(): TemplateResult {
    return html` <div class="documentContainer"></div> `;
  }

  public async firstUpdated(
    _changedProperties: Map<string | number | symbol, unknown>
  ) {
    domtools.plugins.smartdelay.delayFor(0).then(async () => {
      this.documentSettings = {
        ...defaultDocumentSettings,
        ...this.documentSettings,
      };
    });
    const resizeObserver = new ResizeObserver((entries) => {
      for (const entry of entries) {
        const width = entry.contentRect.width;
        const height = entry.contentRect.height;
        // Handle the new dimensions here
        this.adjustDePageScaling();
      }
    });
    resizeObserver.observe(this);
    this.registerGarbageFunction(() => {
      resizeObserver.disconnect();
    });
  }

  public latestDocumentSettings: plugins.shared.interfaces.IDocumentSettings =
    null;
  public latestRenderedLetterData: plugins.tsclass.business.TLetter = null;
  public cleanupStore: any[] = [];

  public async renderDocument() {
    this.latestDocumentSettings = this.documentSettings;
    this.latestRenderedLetterData = this.letterData;

    const cleanUpStoreCurrentRender = [];
    const cleanUpStoreNextRender = [];

    const domtools = await this.domtoolsPromise;
    const documentBuildContainer = document.createElement("div");
    cleanUpStoreCurrentRender.push(documentBuildContainer);
    document.body.appendChild(documentBuildContainer);

    let pages: DePage[] = [];
    let pageCounter = 0;
    let complete = false;

    // lets append the content
    const content: DeContentInvoice = new DeContentInvoice();
    cleanUpStoreCurrentRender.push(content);
    content.letterData = this.letterData as unknown as TInvoice;
    content.documentSettings = this.documentSettings;
    document.body.appendChild(content);

    await domtools.convenience.smartdelay.delayFor(0);
    let overallContentOffset: number = 0;
    let currentContentOffset: number;
    let trimmed: number;
    while (!complete) {
      pageCounter++;
      const currentContent = content.cloneNode(true) as DeContentInvoice;
      cleanUpStoreNextRender.push(currentContent);
      const newPage = new DePage();
      newPage.printMode = this.printMode;
      newPage.letterData = this.letterData;
      newPage.documentSettings = this.documentSettings;
      pages.push(newPage);
      newPage.pageNumber = pageCounter;
      newPage.append(currentContent);
      newPage.pageTotalNumber = pageCounter;

      // store current page
      cleanUpStoreNextRender.push(newPage);
      documentBuildContainer.append(newPage);

      await currentContent.elementDomReady;
      await currentContent.trimStartToOffset(overallContentOffset);
      let newPageOverflows = await newPage.checkOverflow();
      trimmed = 0;
      while (newPageOverflows) {
        await currentContent.trimEndByOne();
        trimmed++;
        newPageOverflows = await newPage.checkOverflow();
      }
      currentContentOffset = await currentContent.getContentLength();
      overallContentOffset = overallContentOffset + currentContentOffset;
      if (trimmed === 0) {
        complete = true;
      }
    }

    for (const cleanUp of this.cleanupStore) {
      cleanUp.remove();
    }
    this.cleanupStore = cleanUpStoreNextRender;

    cleanUpStoreCurrentRender.forEach((cleanUp) => {
      cleanUp.remove();
    });

    const documentContainer =
      this.shadowRoot.querySelector(".documentContainer");
    if (documentContainer) {
      const children = Array.from(documentContainer.children);
      children.forEach((child) => {
        documentContainer.removeChild(child);
        child.remove();
      });
    }
    for (const page of pages) {
      page.pageTotalNumber = pageCounter;
      documentContainer.append(page);
      // betweenPagesSpacer
      if (!this.printMode) {
        const betweenPagesSpacerDiv = document.createElement("div");
        betweenPagesSpacerDiv.classList.add("betweenPagesSpacer");
        documentContainer.appendChild(betweenPagesSpacerDiv);
      }
    }
    this.adjustDePageScaling();
  }

  async updated(
    changedProperties: Map<string | number | symbol, unknown>
  ): Promise<void> {
    super.updated(changedProperties);
    const domtools = await this.domtoolsPromise;
    let renderedDocIsUpToDate =
      domtools.convenience.smartjson.deepEqualObjects(
        this.letterData,
        this.latestRenderedLetterData
      ) &&
      domtools.convenience.smartjson.deepEqualObjects(
        this.documentSettings,
        this.latestDocumentSettings
      );
    if (!renderedDocIsUpToDate) {
      this.renderDocument();
    }

    if (
      changedProperties.has("viewHeight") ||
      changedProperties.has("viewWidth")
    ) {
      this.adjustDePageScaling();
    }
  }

  private adjustDePageScaling() {
    if (this.printMode) {
      return;
    }
    this.viewWidth = this.clientWidth;
    // Find all DePage instances within this DeDocument
    const pages = this.shadowRoot.querySelectorAll("dedocument-page");

    // Update each DePage instance's viewHeight and viewWidth
    pages.forEach((page: DePage) => {
      if (this.viewHeight) {
        page.viewHeight = this.viewHeight;
      }
      if (this.viewWidth) {
        page.viewWidth = this.viewWidth;
      }
    });
  }
}