import { FinishedTranscriptMeta, Resolution, Todo } from "./parsing";
import { Attendance } from "./attendance";
import Token from "markdown-it/lib/token.mjs";

export function renderContainerToAlert(context: String, title?: String) {
    return function (tokens: Token[], idx: number) {
        return tokens[idx].nesting === 1
            ? `<div class="alert ${context}" role="alert"">\n${
                  title === undefined
                      ? ""
                      : `<h5 class="alert-heading">${title}</h5>\n`
              }`
            : "</div>\n";
    };
}

export function renderResolutionToHtml(resolution: Resolution): string {
    return `
  <div class="card mb-3 text-center" id="${resolution.number}">
    <div class="card-header" >Beschluss <code>${resolution.number}</code></div>
    <div class="card-body">
      <h5 class="card-title"></h5>
      <div class="card-text">${resolution.html}</div>
      <div class="container container-sm">
        <div class="row align-items-center">
            <div class="col">
                <span class="p-1 border-bottom border-success border-2">
                    Ja: ${resolution.votes!.yes}
                </span>
            </div>
            <div class="col">
                <span class="p-1 border-bottom border-danger border-2">
                    Nein: ${resolution.votes!.no}
                </span>
            </div>
            <div class="col">
                <span class="p-1 border-bottom border-info border-2">
                    Enthaltung: ${resolution.votes!.abstention}
                </span>
            </div>
        </div>
      </div>
    </div>
    <div class="card-footer ${
        resolution.accepted ? "bg-success" : "bg-danger"
    } bg-opacity-25">
      ${resolution.result}
    </div>
      ${
          resolution.provisional
              ? `<div class="card-footer bg-warning bg-opacity-25">
            <strong class="font-bold">Kommissarisch, weil:</strong> ${resolution.provisional}
           </div>`
              : ""
      }
  </div>`;
}

export function renderTodoToHtml(todo: Todo): string {
    return `
  <div class="card mb-3 text-center">
    <div class="card-header">Neues To-Do</div>
    <div class="card-body">
      <h5 class="card-title"></h5>
      <div class="card-text">${todo.html}</div>
    </div>
    <div class="card-footer bg-info bg-opacity-25">
      <div class="bg-opacity-100">
        ${
            todo.team
                ? `<span class="badge text-bg-info mb-1 me-1">Team: ${todo.team}</span>`
                : ""
        }
        ${
            todo.people
                ? `<span class="badge text-bg-info mb-1 me-1">Zuständig: ${todo.people}</span>`
                : ""
        }
      </div>
    </div>
  </div>`;
}

export function generateFooterPartialHtml(depth: number = 0): string {
    const depthTraversal = new Array(depth).fill("..").join("/");
    return `
      <footer class="my-5 container text-center">
        <p>
          <a href="https://fsinfo.cs.tu-dortmund.de/impressum">Impressum</a>
          |
          <a href="https://fsinfo.cs.tu-dortmund.de/datenschutz/start">Datenschutz</a>
        </p>
        <p class="text-muted">
          <small>
            <a href="./${depthTraversal}/index.json">JSON</a> <a href="./${depthTraversal}/index.xml">RSS-Feed</a>
          </small>
        </p>
        <p class="text-muted">
          <small>
            HTML generiert: ${new Date().toLocaleString("de-DE")}
          </small>
        </p>
      </footer>`;
}

export function renderTranscriptPageHtml(
    transcriptHtml: string,
    meta: any,
    options: CliOptions,
): string {
    return `<!DOCTYPE html>
  <html lang="de">
    <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>${meta.title}</title>
      <link href="../../bootstrap.min.css" rel="stylesheet">
      <style>
      div:target {
        border-width: 0.4rem;
        border-color: #f4c4aa;
        scroll-margin-top: 4rem;
      }
      /* Bootstrap <p> style. Necessary, since our internal architecture changed */
      .card-text:has(.resolution, .todo) {
        margin-top: 0;
        margin-bottom: 1rem;
      }
      h1 {
        border-top: solid 0.1rem;
        margin-top: 2rem;
        padding-top: 1rem;
      }
      @media print {
        .hide-in-print {
          display: none !important;
        }
      }
      </style>
      <script>
        window.addEventListener("beforeprint", () => document.getElementById("metadata").open = true );
        window.addEventListener("afterprint", () => document.getElementById("metadata").open = false );
      </script>
    </head>
    <body class="bg-body">
      <div>
        <header class="hide-in-print">
          <nav class="navbar navbar-expand bg-body-tertiary">
            <ul class="navbar-nav">
            <li class="nav-item">
              <a class="nav-link" href="../..">Protokolle</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="../../resolutions/">Beschlüsse</a>
            </li>
            <!-- WIP:
            <li class="nav-item">
              <a class="nav-link" href="../../relevant/">Relevante Beschlüsse</a>
            </li>
            -->
            ${
                options.attendance != "none"
                    ? `<li class="nav-item">
                        <a class="nav-link" href="../../attendance/">Anwesenheit</a>
                      </li>`
                    : ``
            }
            <li class="nav-item">
              <a class="nav-link" href="https://gitlab.fachschaften.org/search?group_id=29&project_id=77&repository_ref=master&scope=blobs&search=">Suche</a>
            </li>
          </ul>
          </nav>
        </header>
      </div>
      <div class="container mt-2 mb-2">
        <div class="bg-body-tertiary">
          <article class="p-4">
          <div class="p-2 rounded text-center" style="background-color: #DDDDDD">
            Fehler in diesem Protokoll können von Gremien-Mitgliedern
                <a href="https://gitlab.fachschaften.org/tudo-fsinfo/fsr/sitzungen/-/merge_requests">direkt per Merge Request</a>
              behoben werden. Andernfalls bitte an die Protokollanten (siehe <a href="https://oh14.de/fsr">oh14.de/fsr</a>) wenden.
          </div>
          <h1 class="mb-4">${meta.title}</h1>
          <div class="card mb-4">
          <details class="card-body" id="metadata">
            <summary class="hide-in-print"><b>Metadaten</b></summary>
            <p>
              Start: ${meta.start}, Ende: ${meta.end}
              <br />
              Sitzungsleitung: ${meta.head}, Protokoll: ${meta.author}
            </p>
            <div class="row">
              <div class="col">
                <b>Anwesend:</b>
                <ul>
                  ${meta.present.map((p: string) => "<li>" + p + "</li>").join("\n")}
                </ul>
              </div>
              <div class="col">
                <b>Abwesend:</b>
                <ul>
                  ${meta.absent.map((p: string) => "<li>" + p + "</li>").join("\n")}
                </ul>
              </div>
              <div class="col">
                <b>Gäste:</b>
                <ul>
                  ${meta.guests.map((p: string) => "<li>" + p + "</li>").join("\n")}
                </ul>
              </div>
            </div>
          </details>
          </div>
          <div class="ms-2 me-2">
            <p><i>Die Sitzung wird um ${meta.start} eröffnet.</i></p>
            ${transcriptHtml}
            <p><i>Die Sitzung wird um ${meta.end} geschlossen.</i></p>
          </div>
          </article>
        </div>
      </div>
      ${generateFooterPartialHtml(2)}
    </body>
  </html>`;
}

function getTranscriptFilename(t: FinishedTranscriptMeta) {
    return (
        [
            "fsr-sitzung",
            typeof t.number === "number"
                ? `${t.number}`.padStart(3, "0")
                : null,
            `${t.date}`,
            t.label && t.spec_version === -1
                ? `${t.label.toLowerCase()}`
                : null,
        ]
            .filter((x) => x !== null)
            .join("-") + (t.spec_version === -1 ? ".pdf" : "")
    );
}

function getTranscriptTitle(t: FinishedTranscriptMeta) {
    const interjection =
        (typeof t.number === "number" ? ` ${t.number}` : "")
        + (t.label ? ` ${t.label}` : "");

    return `FSR-Sitzung${interjection} (${t.date})`;
}

export function generateIndexHtml(
    transcripts: FinishedTranscriptMeta[],
    options: CliOptions,
): string {
    return `<!DOCTYPE html>
  <html lang="de">
    <head>
      <meta charset="UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>FSR-Protokolle</title>
      <link href="./bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
      <div class="">
        <header>
          <nav class="navbar navbar-expand bg-body-tertiary">
            <ul class="navbar-nav">
            <li class="nav-item">
              <a class="nav-link" href="#">Protokolle</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="./resolutions/">Beschlüsse</a>
            </li>
            <!-- WIP:
            <li class="nav-item">
              <a class="nav-link" href="./relevant/">Relevante Beschlüsse</a>
            </li>
            -->
            ${
                options.attendance != "none"
                    ? `<li class="nav-item">
                         <a class="nav-link" href="./attendance/">Anwesenheit</a>
                       </li>`
                    : ``
            }
            <li class="nav-item">
              <a class="nav-link" href="https://gitlab.fachschaften.org/search?group_id=29&project_id=77&repository_ref=master&scope=blobs&search=">Suche</a>
            </li>
          </ul>
          </nav>
        </header>
      </div>
      <div class="container mt-2 mb-2">
        <ul>
          ${transcripts
              .map((t) => {
                  const title = getTranscriptTitle(t);
                  return `<li>${!t.no_link ? `<a href="./protokolle/${getTranscriptFilename(t)}">${title}</a>` : title}</li>`;
              })
              .join("\n")}
        </ul>
      </div>
      ${generateFooterPartialHtml(0)}
    </body>
  </html>`;
}

export function generateResolutionsHtml(
    transcripts: FinishedTranscriptMeta[],
    modifiedBy: Map<string, Resolution[]>,
    revokedBy: Map<string, Resolution[]>,
    options: CliOptions,
    note?: string,
): string {
    return `<!DOCTYPE html>
    <html lang="de">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>FSR-Protokolle</title>
        <link href="../bootstrap.min.css" rel="stylesheet">
        <style>
          tr:target {
            border-width: 0.4rem;
            border-color: #f4c4aa;
            scroll-margin-top: 4rem;
          }
          details > summary {
            color: #a7a8aa;
          }
          details[open] > summary {
            color: #a7a8aa;
          }
        </style>
      </head>
      <body>
        <svg style="display: none" version="2.0">
          <defs>
            <symbol id="clipboard-icon" viewbox="0 0 16 16" fill="currentColor">
              <path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/>
              <path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/>
            </symbol>
          </defs>
          <use href="#clipboard-icon"/>
        </svg>

        <div class="">
          <header>
            <nav class="navbar navbar-expand bg-body-tertiary">
              <ul class="navbar-nav">
              <li class="nav-item">
                <a class="nav-link" href="../">Protokolle</a>
                <i class="fa fa-link"></i>    </li>
              <li class="nav-item">
                <a class="nav-link" href="../resolutions/">Beschlüsse</a>
              </li>
              <!-- WIP:
              <li class="nav-item">
                <a class="nav-link" href="../relevant/">Relevante Beschlüsse</a>
              </li>
              -->
              ${
                  options.attendance != "none"
                      ? `<li class="nav-item">
                           <a class="nav-link" href="../attendance/">Anwesenheit</a>
                         </li>`
                      : ``
              }
              <li class="nav-item">
                <a class="nav-link" href="https://gitlab.fachschaften.org/search?group_id=29&project_id=77&repository_ref=master&scope=blobs&search=">Suche</a>
              </li>
            </ul>
            </nav>
          </header>
        </div>
        ${note ?? ""}
        <div class="container mt-2 mb-2">
          <table class="table">
            <thead>
              <tr>
                  <th>
                      Datum
                  </th>
                  <th>
                      Nummer
                  </th>
                  <th>
                      Text
                  </th>
                  <th>
                      Ja/Nein/Enth.
                  </th>
                  <th>
                      Ergebnis
                  </th>
                  <th>
                  ⚓️
                  </th>
              </tr>
            </thead>
            <tbody>
              ${transcripts
                  .map((t) => {
                      const transcriptFilename = !t.no_link
                          ? getTranscriptFilename(t)
                          : undefined;
                      const resolutions = t.resolutions ?? [];

                      return resolutions.map((r) =>
                          generateResolutionRowHtml(
                              r,
                              t.date,
                              transcriptFilename,
                              modifiedBy.get(r.number),
                              revokedBy.get(r.number),
                          ),
                      );
                  })
                  .flat()
                  .join("\n")}
            </tbody>
          </table>
        </div>
      ${generateFooterPartialHtml(0)}
      </body>
    </html>`;
}

function generateResolutionRowHtml(
    resolution: Resolution,
    date: string,
    transcriptFileName?: string,
    modifiedBy?: Resolution[],
    revokedBy?: Resolution[],
): string {
    function samePageResolutionLink(other: Resolution | string) {
        const number = typeof other === "string" ? other : other.number;
        const link = `<a href=${"#" + number}>${number}</a>`;
        return typeof other === "string" || other.isActive !== false
            ? link
            : `<del>${link}</del>`;
    }
    function linkResolutions(
        name: string,
        others: Resolution[] | string[] | undefined,
    ) {
        function genInnerHTML(name: string, others: Resolution[] | string[]) {
            return `<strong>${name}:</strong> ${others.map(samePageResolutionLink).join(", ")}`;
        }
        if (others && others.length > 0)
            return `<p>${genInnerHTML(name, others)}</p>`;
        else return "";
    }

    let linkedNumber =
        transcriptFileName !== undefined
            ? `<a href="../protokolle/${transcriptFileName}/#${resolution.number}">${resolution.number}</a>`
            : `${resolution.number}`;

    let votes: string;
    if (resolution.votes === undefined) {
        votes = "";
    } else if (
        resolution.votes.yes === undefined
        || resolution.votes.no === undefined
    ) {
        votes = Object.keys(resolution.votes)
            .map(
                (r) =>
                    `${r === "abstention" ? "Enth." : r}: ${resolution.votes![r]}`,
            )
            .join("<br>");
    } else {
        votes = `${resolution.votes.yes} / ${resolution.votes.no} / ${resolution.votes.abstention}`;
    }

    return `<tr id="${resolution.number}">
    <td>
      ${date}
    </td>
    <td>
      ${linkedNumber}
    </td>
    <td>
      <div style="${resolution.isActive === false ? "color: red" : ""}">
        ${resolution.html}
        ${linkResolutions("Modifiziert", resolution.modifies)}
        ${linkResolutions("Widerruft", resolution.revokes)}
      </div>
      ${linkResolutions("Modifiziert durch", modifiedBy)}
      ${linkResolutions("Widerrufen durch", revokedBy)}
      ${
          resolution.provisional
              ? `<p><strong>Kommissarisch, weil:</strong> ${resolution.provisional}</p>`
              : ""
      }
      ${
          typeof resolution.money_granted === "string"
              ? generateHledgerDetailsHtml(resolution)
              : ""
      }
      ${
          resolution.note
              ? `<p><strong>Notiz:</strong> ${resolution.note}</p>`
              : ""
      }
    </td>
    <td>
      ${votes}
    </td>
    <td>
    ${resolution.result ?? "Unbekannt"}
    </td>
    <td>
        <a href="#${resolution.number}">
        🔗
        </a>
    </td>
</tr>`;
}

export function generateTranscriptsRssXml(
    transcripts: FinishedTranscriptMeta[],
    rootUrl: string | null = null,
): string {
    return `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>Protokolle</title>
    ${transcripts
        .map(
            (t) =>
                `<item>${
                    !t.no_link
                        ? `<link>${rootUrl ?? "."}/protokolle/${getTranscriptFilename(t)}</link>`
                        : ``
                }<description>${getTranscriptTitle(t)}</description><title>${getTranscriptTitle(t)}</title><pubDate>${t.date}</pubDate></item>`,
        )
        .join("\n    ")}
  </channel>
</rss>`;
}

function generateHledgerDetailsHtml(resolution: Resolution): string {
    const hledgerText = `account Gebunden:${resolution.number}
${resolution.date} ${resolution.number}: beschlossen
	[Gebunden:${resolution.number}]  ${resolution.money_granted}
	[Vermögen:FSR:Gebunden]`;

    return `<details><summary><small class="text-body-secondary fw-light">Hledger-Statement für die Buchhaltung</small></summary><pre><code id="reso-text-${resolution.number}">${hledgerText}</code></pre><button class="btn btn-outline-primary btn-sm" onclick="navigator.clipboard.writeText(document.getElementById(\'reso-text-${resolution.number}\').innerText)"><svg width="16" height="16" version="2.0"><use href="#clipboard-icon" /></svg> Hledger-Statement kopieren</button></details>`;
}

export function generateAttendanceHtmlWrapper(
    attendanceParts: [
        Map<string, Attendance>,
        string,
        number,
        number | undefined,
    ][],
): string {
    return `<!DOCTYPE html>
    <html lang="de">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta name="robots" content="noindex" />
        <title>FSR-Anwesenheit</title>
        <link href="../bootstrap.min.css" rel="stylesheet">
        <style>
          .percentage-bar {
            display: flex;
            height: 15px;
            width: 100%;
            border-radius: 8px;
            overflow: hidden;
            border: 1px solid #ccc;
            margin-top: 5px;
          }
          body:has(#grouped-bar-toggle:checked) .grouped-bar {
            visibility: visible;
            position: static;
          }
          body:has(#grouped-bar-toggle:checked) .ungrouped-bar {
            visibility: hidden;
            position: absolute;
          }
          .grouped-bar {
            visibility: hidden;
            position: absolute;
          }
          .ungrouped-bar {
            visibility: visible;
            position: static;
          }
        </style>
        <script>
          document.addEventListener('DOMContentLoaded', function () {
            const getCellValue = (tr, idx) => tr.children[idx].innerText || tr.children[idx].textContent;
          
            const comparer = (idx, asc) => (a, b) => {
              const v1 = getCellValue(a, idx);
              const v2 = getCellValue(b, idx);
          
              const f1 = parseFloat(v1);
              const f2 = parseFloat(v2);
              if (!isNaN(f1) && !isNaN(f2)) {
                return (f1 - f2) * (asc ? 1 : -1);
              } else {
                return v1.toString().localeCompare(v2) * (asc ? 1 : -1);
              }
            };
          
            const sortTable = (table, columnIndex, asc) => {
              const tbody = table.querySelector('tbody');
              const rows = Array.from(tbody.querySelectorAll('tr'));
              rows.sort(comparer(columnIndex, asc));
              rows.forEach(row => tbody.appendChild(row));
          
              // Clean up previous sort indicators
              table.querySelectorAll('th').forEach(th => th.classList.remove('asc', 'desc'));
              const th = table.querySelectorAll('th')[columnIndex];
              th.classList.toggle('asc', asc);
              th.classList.toggle('desc', !asc);
            };
          
            // Enable click-to-sort
            document.querySelectorAll('table').forEach(table => {
              table.querySelectorAll('th').forEach((th, idx) => {
                th.addEventListener('click', function () {
                  const isAsc = !th.classList.contains('asc');
                  sortTable(table, idx, isAsc);
                });
              });
          
              // Sort third column descending by default on page load
              sortTable(table, 2, false);
            });
          });
          </script>
      </head>
      <body>
        <div class="">
          <header>
            <nav class="navbar navbar-expand bg-body-tertiary">
              <ul class="navbar-nav">
                <li class="nav-item">
                  <a class="nav-link" href="../">Protokolle</a>
                  <i class="fa fa-link"></i>    </li>
                <li class="nav-item">
                  <a class="nav-link" href="../resolutions/">Beschlüsse</a>
                </li>
                <!-- WIP:
                <li class="nav-item">
                  <a class="nav-link" href="../relevant/">Relevante Beschlüsse</a>
                </li>
                -->
                <li class="nav-item">
                  <a class="nav-link" href="../attendance/">Anwesenheit</a>
                </li>
                <li class="nav-item">
                  <a class="nav-link" href="https://gitlab.fachschaften.org/search?group_id=29&project_id=77&repository_ref=master&scope=blobs&search=">Suche</a>
                </li> 
              </ul>
              <div class="ms-auto d-flex-flex align-items-center form-check me-3">
                <input class="form-check-input me-2" type="checkbox" id="grouped-bar-toggle" value="" checked />
                <label class="form-check-label" for="grouped-bar-toggle">
                  Group attendance bar
                </label>
              </div>
            </nav>
          </header>
        </div>
        ${attendanceParts.map(([attendanceMap, summary, from, to]) => generateAttendanceHtml(attendanceMap, summary, from, to)).join("\n")}
      ${generateFooterPartialHtml(0)}
      </body>
    </html>`;
}

export function generateAttendanceHtml(
    attendanceMap: Map<string, Attendance>,
    summary: string,
    from: number,
    to?: number,
): string {
    const open = to === undefined;

    return `
      <details class="border"${open ? " open" : ""}>
        <summary class="fs-4 mx-3 p-2">${summary} von Sitzung ${from} bis ${to === undefined ? "heute" : "Sitzung " + to}</summary>
        <div class="container my-2">
          <table class="table table-striped">
            <thead>
              <tr>
                  <th>
                      Name
                  </th>
                  <th>
                      Anwesehnheit
                  </th>
                  <th>
                      Vor Ort
                  </th>
                  <th>
                      Nicht da (entschuldigt)
                  </th>
                  <th>
                      Nicht da (unentschuldigt)
                  </th>
                  <th>
                      Anwesenheitsquotient
                  </th>
              </tr>
            </thead>
            <tbody>
              ${[...attendanceMap.entries()].map(([k, v]) => generateAttendanceRowHtml(k, v)).join("")}
            </tbody>
          </table>
        </div>
      </details>`;
}

function generateAttendanceRowHtml(name: string, data: Attendance): string {
    return `
      <tr>
        <td> ${name} </td> 
        <td> ${generateGroupedPercentageBar(data)} ${generateUngroupedPercentageBar(data)} </td> 
        <td> ${data.present} </td> 
        <td> ${data.noshow_excused} </td> 
        <td> ${data.noshow}</td> 
        <td> ${((data.present * 100) / data.sum()).toFixed(1)}% </td> 
      </tr>`;
}

function generateUngroupedPercentageBar(data: Attendance): string {
    const width = 100 / data.sum();

    const tagged: { value: number; color: string }[] = [
        ...data.presents.map((value) => ({ value, color: "darkgreen" })),
        ...data.noshows.map((value) => ({ value, color: "red" })),
        ...data.noshow_excuseds.map((value) => ({ value, color: "yellow" })),
    ];

    // Sort by number
    tagged.sort((a, b) => a.value - b.value);

    var barHtml = '<div class="percentage-bar ungrouped-bar">';
    // Iterate in order
    for (const { color } of tagged) {
        barHtml += `<div style="width: ${width}%;background-color: ${color}"></div>`;
    }
    barHtml += "</div>";
    return barHtml;
}

function generateGroupedPercentageBar(data: Attendance): string {
    return `
      <div class="percentage-bar grouped-bar">
        <div style="width: ${(data.present * 100) / data.sum()}%;background-color: darkgreen"></div>
        <div style="width: ${(data.noshow_excused * 100) / data.sum()}%;background-color: yellow"></div>
        <div style="width: ${(data.noshow * 100) / data.sum()}%;background-color: red"></div>
      </div>`;
}