// Entry detail — #/entry/:id (Task 9 item 3). Reached from the dashboard's
// ActivityList, an account's entry list, or a reversal link on another
// entry. Renders GET /api/c/entries/:id (server/src/reports.ts) in plain
// English: each line as "$500.00 into Chase Business Checking" / "$500.00
// from Consulting" (side 'in' -> "into", 'out' -> "from"), with the raw
// debit/credit table tucked behind an "Accountant view" <details> — the
// entry-detail counterpart to the trial balance's accountant view, per this
// task's brief.
//
// "Reverse this entry" (Task 11) is enabled only for a posted entry that
// hasn't already been reversed — the button itself gates on that so the
// common case never round-trips to the server just to learn what
// mapPgError's "already been reversed"/"only posted entries can be
// reversed" 409s already tell it for free (server/src/errors.ts). Clicking
// it opens an inline plain-English confirm (CLAUDE.md §9 — no browser
// window.confirm(), no scary dialog) rather than acting immediately; on
// success it hands off to the reversal entry via onSelectEntry and marks it
// (sessionStorage, see markJustReversed/consumeJustReversed below) so that
// entry's own mount shows a small "just reversed" banner — the "confirm →
// on success navigate to the reversal entry with a success banner" flow the
// task brief asks for, without threading extra state through the hash router
// (lib/useHashView.ts's NavTarget carries only a view+id, on purpose).

import { useEffect, useState } from "react";
import { useCompany } from "./lib/company";
import { useApiGet } from "./lib/useApiGet";
import { apiPost, ApiError } from "./lib/api";
import { fmtDate, todayIso } from "./lib/date";
import { fmtMoney } from "./lib/money";
import { importedFromLabel } from "./lib/importSource";
import { ErrorBanner } from "./components/ErrorBanner";
import { BackLink } from "./components/BackLink";

const JUST_REVERSED_PREFIX = "just-reversed:";

/** Best-effort sessionStorage flag — never fatal if storage is unavailable
 * (private browsing, disabled storage); worst case the banner just doesn't
 * show, which isn't worth failing the reversal itself over. */
function markJustReversed(entryId: string): void {
  try {
    window.sessionStorage.setItem(JUST_REVERSED_PREFIX + entryId, "1");
  } catch {
    // best-effort only
  }
}

function consumeJustReversed(entryId: string): boolean {
  try {
    const key = JUST_REVERSED_PREFIX + entryId;
    if (window.sessionStorage.getItem(key)) {
      window.sessionStorage.removeItem(key);
      return true;
    }
  } catch {
    // best-effort only
  }
  return false;
}

interface EntryLine {
  accountId: string;
  code: string;
  accountName: string;
  type: string;
  side: "in" | "out";
  amount: string;
  debit: string;
  credit: string;
  memo: string | null;
}

interface EntryDetailData {
  entryId: string;
  entryNo: string | null;
  date: string;
  memo: string | null;
  status: string;
  source: string | null;
  externalRef: string | null;
  importBatch: { filename: string } | null;
  reversesEntryId: string | null;
  reversedByEntryId: string | null;
  lines: EntryLine[];
}

function capitalize(s: string): string {
  return s.length === 0 ? s : s.charAt(0).toUpperCase() + s.slice(1);
}

export function EntryDetail({
  entryId,
  onSelectEntry,
}: {
  entryId: string;
  onSelectEntry: (entryId: string) => void;
}) {
  const { selectedCompanyId } = useCompany();
  const path = `/api/c/entries/${entryId}`;
  const { data, loading, error, retry } = useApiGet<EntryDetailData>(path, selectedCompanyId);

  const [confirming, setConfirming] = useState(false);
  const [reversing, setReversing] = useState(false);
  const [reverseError, setReverseError] = useState<string | null>(null);
  const [justReversed, setJustReversed] = useState(false);

  // Runs once per entry shown (including when navigating entry-to-entry
  // without an unmount, e.g. via the reversal link below) — see the file
  // header on why this is a sessionStorage flag rather than routed state.
  useEffect(() => {
    setJustReversed(consumeJustReversed(entryId));
    setConfirming(false);
    setReverseError(null);
  }, [entryId]);

  async function handleReverse() {
    if (!selectedCompanyId) return;
    setReversing(true);
    setReverseError(null);
    try {
      const res = await apiPost<{ reversalEntryId: string; entryNo: string; summary: string }>(
        `/api/c/entries/${entryId}/reverse`,
        { date: todayIso() },
        selectedCompanyId,
      );
      markJustReversed(res.reversalEntryId);
      setConfirming(false);
      onSelectEntry(res.reversalEntryId);
    } catch (err) {
      setReverseError(err instanceof ApiError ? err.message : "Couldn't reverse that entry. Try again.");
      setConfirming(false);
    } finally {
      setReversing(false);
    }
  }

  const canReverse = data?.status === "posted" && !data?.reversedByEntryId;

  return (
    <div className="wrap">
      <BackLink />

      {justReversed && (
        <div className="success-banner">
          <span>This entry reverses the one you just undid — the original stays untouched.</span>
          <button type="button" onClick={() => setJustReversed(false)}>
            Dismiss
          </button>
        </div>
      )}

      {error && <ErrorBanner message={error} onRetry={retry} />}
      {loading && !data && <p className="msg-inline">Loading&hellip;</p>}

      {data && (
        <>
          <header className="top">
            <h1>Entry #{data.entryNo ?? "—"}</h1>
            <span className="chip">{capitalize(data.status)}</span>
          </header>
          <div className="asof">{fmtDate(data.date)}</div>

          <div className="card" style={{ margin: "18px 0" }}>
            <div className="entry-memo">{data.memo ?? "No memo."}</div>
            <div className="badge-row">
              <span className="badge">
                {data.importBatch
                  ? importedFromLabel(data.source, data.importBatch.filename)
                  : "Recorded by hand"}
              </span>
            </div>

            {(data.reversesEntryId || data.reversedByEntryId) && (
              <div className="reversal-links">
                {data.reversesEntryId && (
                  <button type="button" className="acct-link" onClick={() => onSelectEntry(data.reversesEntryId!)}>
                    View the entry this reverses
                  </button>
                )}
                {data.reversedByEntryId && (
                  <button type="button" className="acct-link" onClick={() => onSelectEntry(data.reversedByEntryId!)}>
                    View the entry that reverses this one
                  </button>
                )}
              </div>
            )}
          </div>

          <div className="card">
            <h2>What happened</h2>
            <ul className="entry-lines">
              {data.lines.map((line, i) => (
                <li key={`${line.accountId}-${i}`}>
                  {fmtMoney(line.amount)} {line.side === "in" ? "into" : "from"} {line.accountName}
                  {line.memo ? ` — ${line.memo}` : ""}
                </li>
              ))}
            </ul>

            <div style={{ marginTop: 16 }}>
              {!confirming ? (
                <button
                  type="button"
                  className="btn"
                  disabled={!canReverse}
                  title={
                    !canReverse
                      ? data.reversedByEntryId
                        ? "This entry has already been reversed."
                        : "Only posted entries can be reversed."
                      : undefined
                  }
                  onClick={() => setConfirming(true)}
                >
                  Reverse this entry
                </button>
              ) : (
                <div className="confirm-panel">
                  <p>This creates a matching opposite entry dated today. The original stays untouched. Continue?</p>
                  <div className="wizard-actions">
                    <button type="button" className="btn" disabled={reversing} onClick={() => void handleReverse()}>
                      {reversing ? "Reversing…" : "Yes, reverse it"}
                    </button>
                    <button
                      type="button"
                      className="acct-link"
                      disabled={reversing}
                      onClick={() => setConfirming(false)}
                    >
                      Cancel
                    </button>
                  </div>
                </div>
              )}
              {reverseError && <ErrorBanner message={reverseError} onRetry={() => void handleReverse()} />}
            </div>
          </div>

          <details className="tableview" style={{ marginTop: 12 }}>
            <summary>Accountant view</summary>
            <table>
              <thead>
                <tr>
                  <th>Account</th>
                  <th className="num">Debit</th>
                  <th className="num">Credit</th>
                </tr>
              </thead>
              <tbody>
                {data.lines.map((line, i) => (
                  <tr key={`${line.accountId}-${i}`}>
                    <td>
                      {line.code} {line.accountName}
                    </td>
                    <td className="num">{fmtMoney(line.debit)}</td>
                    <td className="num">{fmtMoney(line.credit)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </details>
        </>
      )}
    </div>
  );
}
