import { useState } from 'react';

import type { ParserDiagnostic } from '../onenote/model/diagnostics.js';
import {
  DEFAULT_UI_RENDER_LIMITS,
  limitUiString,
  resolveUiLimit,
} from './ui-render-budget.js';

interface DiagnosticsPanelProps {
  diagnostics: ParserDiagnostic[];
  title?: string;
  pageSize?: number;
}

function formatOffset(offset: number): string {
  return `0x${offset.toString(16).toUpperCase()}`;
}

export function DiagnosticsPanel({
  diagnostics,
  title = 'Diagnostics',
  pageSize: requestedPageSize,
}: DiagnosticsPanelProps) {
  const [requestedPage, setRequestedPage] = useState(0);
  const pageSize = resolveUiLimit(
    requestedPageSize,
    DEFAULT_UI_RENDER_LIMITS.maxDiagnosticsPerPage
  );
  const pageCount = Math.max(1, Math.ceil(diagnostics.length / pageSize));
  const page = Math.min(requestedPage, pageCount - 1);
  const firstDiagnostic = page * pageSize;
  const limitedTitle = limitUiString(
    title,
    DEFAULT_UI_RENDER_LIMITS.maxPanelTitleCharacters
  );
  const displayedDiagnostics = diagnostics
    .slice(firstDiagnostic, firstDiagnostic + pageSize)
    .map((diagnostic, index) => {
      const code = limitUiString(
        diagnostic.code,
        DEFAULT_UI_RENDER_LIMITS.maxDiagnosticCodeCharacters
      );
      const message = limitUiString(
        diagnostic.message,
        DEFAULT_UI_RENDER_LIMITS.maxDiagnosticMessageCharacters
      );
      const structure =
        diagnostic.structure === undefined
          ? undefined
          : limitUiString(
              diagnostic.structure,
              DEFAULT_UI_RENDER_LIMITS.maxDiagnosticContextCharacters
            );
      const propertyId =
        diagnostic.propertyId === undefined
          ? undefined
          : limitUiString(
              diagnostic.propertyId,
              DEFAULT_UI_RENDER_LIMITS.maxDiagnosticContextCharacters
            );
      return {
        diagnostic,
        index: firstDiagnostic + index,
        code: code.value,
        message: message.value,
        structure: structure?.value,
        propertyId: propertyId?.value,
        truncated:
          code.truncated ||
          message.truncated ||
          structure?.truncated === true ||
          propertyId?.truncated === true,
      };
    });
  const textTruncated =
    limitedTitle.truncated ||
    displayedDiagnostics.some((diagnostic) => diagnostic.truncated);

  return (
    <section className="diagnostics" aria-labelledby="diagnostics-title">
      <div className="panel-heading">
        <div>
          <p className="eyebrow">Parser details</p>
          <h2 id="diagnostics-title">{limitedTitle.value}</h2>
        </div>
        <span className="count-badge">{diagnostics.length}</span>
      </div>

      {diagnostics.length === 0 ? (
        <p className="empty-message">No parser diagnostics for this view.</p>
      ) : (
        <ul className="diagnostic-list">
          {displayedDiagnostics.map((displayed) => (
            <li
              className={`diagnostic diagnostic--${displayed.diagnostic.severity}`}
              key={displayed.index}
            >
              <div className="diagnostic__summary">
                <strong>{displayed.code}</strong>
                <span>{displayed.diagnostic.severity}</span>
              </div>
              <p>{displayed.message}</p>
              {displayed.structure !== undefined ||
              displayed.diagnostic.offset !== undefined ||
              displayed.propertyId !== undefined ? (
                <dl className="diagnostic__context">
                  {displayed.structure !== undefined ? (
                    <>
                      <dt>Structure</dt>
                      <dd>{displayed.structure}</dd>
                    </>
                  ) : null}
                  {displayed.diagnostic.offset !== undefined ? (
                    <>
                      <dt>Offset</dt>
                      <dd>{formatOffset(displayed.diagnostic.offset)}</dd>
                    </>
                  ) : null}
                  {displayed.propertyId !== undefined ? (
                    <>
                      <dt>Property</dt>
                      <dd>{displayed.propertyId}</dd>
                    </>
                  ) : null}
                </dl>
              ) : null}
            </li>
          ))}
        </ul>
      )}
      {diagnostics.length > 0 ? (
        <nav className="panel-heading" aria-label="Diagnostic pages">
          <p>
            Showing diagnostics {firstDiagnostic + 1}–
            {firstDiagnostic + displayedDiagnostics.length} of{' '}
            {diagnostics.length}.
          </p>
          <div>
            <button
              type="button"
              disabled={page === 0}
              onClick={() => setRequestedPage(Math.max(0, page - 1))}
            >
              Previous
            </button>{' '}
            <span>
              Page {page + 1} of {pageCount}
            </span>{' '}
            <button
              type="button"
              disabled={page + 1 >= pageCount}
              onClick={() =>
                setRequestedPage(Math.min(pageCount - 1, page + 1))
              }
            >
              Next
            </button>
          </div>
        </nav>
      ) : null}
      {textTruncated ? (
        <p className="render-limit-notice" role="status">
          Long diagnostic text was shortened for safe browser display.
        </p>
      ) : null}
    </section>
  );
}
