/*
 * format-html-title.ts
 *
 * Copyright (C) 2020-2022 Posit Software, PBC
 */

import { existsSync } from "../../deno_ral/fs.ts";
import { dirname, isAbsolute, join } from "../../deno_ral/path.ts";
import { kDateFormat, kTocLocation } from "../../config/constants.ts";
import { Format, Metadata, PandocFlags } from "../../config/types.ts";
import { Document, Element } from "../../core/deno-dom.ts";
import { formatResourcePath } from "../../core/resources.ts";
import { sassLayer } from "../../core/sass.ts";
import { TempContext } from "../../core/temp-types.ts";
import { MarkdownPipeline } from "../../core/markdown-pipeline.ts";
import {
  HtmlPostProcessResult,
  PandocInputTraits,
  RenderedFormat,
} from "../../command/render/types.ts";
import { InternalError } from "../../core/lib/error.ts";

export const kTitleBlockStyle = "title-block-style";
const kTitleBlockBanner = "title-block-banner";
const ktitleBlockColor = "title-block-banner-color";
const kTitleBlockCategories = "title-block-categories";

export interface DocumentTitleContext {
  pipeline: MarkdownPipeline;
}

export function documentTitleScssLayer(format: Format) {
  if (
    format.metadata[kTitleBlockStyle] === false ||
    format.metadata[kTitleBlockStyle] === "none" ||
    format.metadata[kTitleBlockStyle] === "plain"
  ) {
    return undefined;
  } else if (format.metadata[kTitleBlockStyle] === "manuscript") {
    // TODO: Tweak style for manuscript
    // This code path is here so that we can add manuscript-specific styles
    // For now it is just identical to non-manuscript
    const titleBlockScss = formatResourcePath(
      "html",
      join("templates", "title-block.scss"),
    );
    return sassLayer(titleBlockScss);
  } else {
    const titleBlockScss = formatResourcePath(
      "html",
      join("templates", "title-block.scss"),
    );
    return sassLayer(titleBlockScss);
  }
}

export function documentTitleMetadata(
  format: Format,
) {
  if (
    format.metadata[kTitleBlockStyle] !== false &&
    format.metadata[kTitleBlockStyle] !== "none" &&
    format.metadata[kDateFormat] === undefined
  ) {
    return {
      [kDateFormat]: "long",
    };
  } else {
    return undefined;
  }
}

export function documentTitleIncludeInHeader(
  input: string,
  format: Format,
  temp: TempContext,
) {
  // Inject variables
  const headingVars: string[] = [];
  const containerVars: string[] = [];

  const banner = format.metadata[kTitleBlockBanner] as string | boolean;
  if (banner) {
    // $title-banner-bg
    // $title-banner-color
    // $title-banner-image
    const titleBlockColor = titleColor(format.metadata[ktitleBlockColor]);
    if (titleBlockColor) {
      const color = `color: ${titleBlockColor};`;
      headingVars.push(color);
      containerVars.push(color);
    }

    if (banner === true) {
      // The default appearance, use navbar color
    } else if (isBannerImage(input, banner)) {
      // An image background
      containerVars.push(`background-image: url(${banner});`);
      containerVars.push(`background-size: cover;`);
    } else {
      containerVars.push(`background: ${banner};`);
    }
  }

  if (headingVars.length || containerVars.length) {
    const styles: string[] = ["<style>"];
    if (headingVars.length) {
      styles.push(`
      .quarto-title-block .quarto-title-banner h1,
      .quarto-title-block .quarto-title-banner h2,
      .quarto-title-block .quarto-title-banner h3,
      .quarto-title-block .quarto-title-banner h4,
      .quarto-title-block .quarto-title-banner h5,
      .quarto-title-block .quarto-title-banner h6
      {
        ${headingVars.join("\n")}
      }`);
    }
    if (containerVars.length) {
      styles.push(`
      .quarto-title-block .quarto-title-banner {
        ${containerVars.join("\n")}
      }`);
    }

    styles.push("</style>");
    const file = temp.createFile({ suffix: ".css" });
    Deno.writeTextFileSync(file, styles.join("\n"));
    return file;
  }
}

export function documentTitlePartial(
  format: Format,
) {
  if (
    format.metadata[kTitleBlockStyle] === false ||
    format.metadata[kTitleBlockStyle] === "none"
  ) {
    return {
      partials: [],
      templateParams: {},
    };
  } else {
    const partials = [];
    const templateParams: Metadata = {};

    // Note whether we should be showing categories
    templateParams[kTitleBlockCategories] =
      format.metadata[kTitleBlockCategories] !== false ? "true" : "";

    // Select the appropriate title block partial (banner vs no banner)
    const banner = format.metadata[kTitleBlockBanner] as string | boolean;
    const manuscriptTitle = format.metadata[kTitleBlockStyle] === "manuscript";

    partials.push("_title-meta-author.html");
    partials.push("title-metadata.html");

    if (manuscriptTitle) {
      partials.push("manuscript/title-block.html");
      partials.push("manuscript/title-metadata.html");
    } else if (banner) {
      partials.push("banner/title-block.html");
    } else {
      partials.push("title-block.html");
    }

    // For banner partials, configure the options and pass them along in the metadata
    if (banner || manuscriptTitle) {
      // When the toc is on the left, be sure to add the special grid notation
      const tocLeft = format.metadata[kTocLocation] === "left";
      if (tocLeft) {
        templateParams["banner-header-class"] = "toc-left";
      }
    }

    return {
      partials: partials.map((partial) => {
        return formatResourcePath("html", join("templates", partial));
      }),
      templateParams,
    };
  }
}

export async function canonicalizeTitlePostprocessor(
  doc: Document,
  _options: {
    inputMetadata: Metadata;
    inputTraits: PandocInputTraits;
    renderedFormats: RenderedFormat[];
    quiet?: boolean;
  },
): Promise<HtmlPostProcessResult> {
  // https://github.com/quarto-dev/quarto-cli/issues/10567
  // this fix cannot happen in `processDocumentTitle` because
  // that's too late in the postprocessing order
  const titleBlock = doc.querySelector("header.quarto-title-block");

  const main = doc.querySelector("main");
  // if no main element exists, this is likely a revealjs presentation
  // which will generally have a title slide instead of a title block
  // so we don't need to do anything

  if (!titleBlock && main) {
    const header = doc.createElement("header");
    header.id = "title-block-header";
    header.classList.add("quarto-title-block");
    main.insertBefore(header, main.firstChild);
    const h1s = Array.from(doc.querySelectorAll("h1"));
    for (const h1n of h1s) {
      const h1 = h1n as Element;
      if (h1.classList.contains("quarto-secondary-nav-title")) {
        continue;
      }

      // Now we need to check whether this is a plausible title element.
      if (h1.parentElement?.tagName === "SECTION") {
        // If the parent element is a section, then we need to check if there's
        // any content before the section. If there is, then this is not a title
        if (
          h1.parentElement?.parentElement?.firstElementChild !==
            h1.parentElement
        ) {
          continue;
        }
      } else {
        // If the parent element is not a section, then we need to check if there's
        // any content before the h1. If there is, then this is not a title
        if (h1.parentElement?.firstElementChild !== h1) {
          continue;
        }
      }

      const div = doc.createElement("div");
      div.classList.add("quarto-title-banner");
      h1.classList.add("title");
      header.appendChild(h1);
      break;
    }
  }

  return {
    resources: [],
    supporting: [],
  };
}

export function processDocumentTitle(
  input: string,
  format: Format,
  _flags: PandocFlags,
  doc: Document,
) {
  const resources: string[] = [];

  // when in banner mode, note on the main content region and
  // add any image to resources
  const banner = format.metadata[kTitleBlockBanner] as string | boolean;
  const manuscriptTitle = format.metadata[kTitleBlockStyle] === "manuscript";
  if (banner || manuscriptTitle) {
    // Move the header above the content
    const headerEl = doc.getElementById("title-block-header");
    const contentEl = doc.getElementById("quarto-content");
    if (contentEl && headerEl) {
      headerEl.remove();
      contentEl.parentElement?.insertBefore(headerEl, contentEl);
    }

    const mainEl = doc.querySelector("main.content");
    mainEl?.classList.add("quarto-banner-title-block");

    if (isBannerImage(input, banner)) {
      resources.push(banner as string);
    }

    // Decorate the header
    const quartoHeaderEl = doc.getElementById("quarto-header");
    if (quartoHeaderEl) {
      quartoHeaderEl.classList.add("quarto-banner");
    }
  }

  return resources;
}

function isBannerImage(input: string, banner: unknown) {
  if (typeof banner === "string") {
    let path;

    if (isAbsolute(banner)) {
      path = banner;
    } else {
      path = join(dirname(input), banner);
    }
    return existsSync(path);
  } else {
    return false;
  }
}

const titleColor = (block: unknown) => {
  if (block === "body" || block === "body-bg") {
    return undefined;
  } else {
    return block;
  }
};

const _titleColorClass = (block: unknown) => {
  if (block === "body") {
    return "body";
  } else if (block === "body-bg" || block === undefined) {
    return "body-bg";
  } else {
    return "none";
  }
};
