"use client";

import React, {
  FormEvent,
  useCallback,
  useEffect,
  useState,
} from "react";
import type {
  ProjectHistoryStateDto,
  ProjectRevisionPageDto,
  ProjectRevisionSummaryDto,
  ProjectSnapshotMetadataDto,
} from "../types";
import {
  createNamedProjectSnapshot,
  getProjectHistory,
  listProjectRevisions,
  listProjectSnapshots,
  redoProjectCommand,
  restoreProjectSnapshot,
  undoProjectCommand,
} from "../utils/api";
import {
  getProjectRevisionDescription,
  getProjectRevisionSourceLabel,
  getProjectSnapshotKindLabel,
  mergeProjectRevisionPages,
} from "../utils/project-version-history";

interface ProjectVersionHistoryProps {
  projectId: string;
  currentRevision: number;
  onProjectStateChange: () => void;
}

const revisionPageSize = 10;

export function ProjectVersionHistory({
  projectId,
  currentRevision,
  onProjectStateChange,
}: ProjectVersionHistoryProps) {
  const [history, setHistory] =
    useState<ProjectHistoryStateDto | null>(null);
  const [snapshots, setSnapshots] = useState<ProjectSnapshotMetadataDto[]>(
    []
  );
  const [revisions, setRevisions] = useState<ProjectRevisionSummaryDto[]>(
    []
  );
  const [nextBeforeRevision, setNextBeforeRevision] = useState<
    number | null
  >(null);
  const [snapshotName, setSnapshotName] = useState("");
  const [snapshotDescription, setSnapshotDescription] = useState("");
  const [restoreCandidate, setRestoreCandidate] =
    useState<ProjectSnapshotMetadataDto | null>(null);
  const [isOpen, setIsOpen] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const [isHistoryLoading, setIsHistoryLoading] = useState(true);
  const [isBusy, setIsBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const loadHistory = useCallback(async () => {
    setIsHistoryLoading(true);
    try {
      setHistory(await getProjectHistory(projectId));
    } catch (loadError) {
      setError(
        getVersionHistoryError(
          loadError,
          "Rückgängig-Status konnte nicht geladen werden."
        )
      );
    } finally {
      setIsHistoryLoading(false);
    }
  }, [projectId]);

  const loadOverview = useCallback(async () => {
    setIsLoading(true);
    setError(null);
    try {
      const [loadedSnapshots, revisionPage] = await Promise.all([
        listProjectSnapshots(projectId),
        listProjectRevisions(projectId, { limit: revisionPageSize }),
      ]);
      setSnapshots(loadedSnapshots);
      setRevisions(revisionPage.revisions);
      setNextBeforeRevision(revisionPage.nextBeforeRevision);
    } catch (loadError) {
      setError(
        getVersionHistoryError(
          loadError,
          "Versionshistorie konnte nicht geladen werden."
        )
      );
    } finally {
      setIsLoading(false);
    }
  }, [projectId]);

  useEffect(() => {
    void loadHistory();
  }, [currentRevision, loadHistory]);

  useEffect(() => {
    if (isOpen) {
      void loadOverview();
    }
  }, [loadOverview, currentRevision, isOpen]);

  async function handleCreateSnapshot(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const name = snapshotName.trim();
    if (!name) {
      return;
    }
    setIsBusy(true);
    setError(null);
    try {
      const created = await createNamedProjectSnapshot(
        projectId,
        currentRevision,
        name,
        snapshotDescription.trim() || undefined
      );
      setSnapshots((current) => [created, ...current]);
      setSnapshotName("");
      setSnapshotDescription("");
    } catch (createError) {
      setError(
        getVersionHistoryError(
          createError,
          "Projektstand konnte nicht gespeichert werden."
        )
      );
    } finally {
      setIsBusy(false);
    }
  }

  async function handleLoadOlderRevisions() {
    if (nextBeforeRevision === null) {
      return;
    }
    setIsBusy(true);
    setError(null);
    try {
      const page: ProjectRevisionPageDto = await listProjectRevisions(
        projectId,
        {
          limit: revisionPageSize,
          beforeRevision: nextBeforeRevision,
        }
      );
      setRevisions((current) =>
        mergeProjectRevisionPages(current, page.revisions)
      );
      setNextBeforeRevision(page.nextBeforeRevision);
    } catch (loadError) {
      setError(
        getVersionHistoryError(
          loadError,
          "Ältere Revisionen konnten nicht geladen werden."
        )
      );
    } finally {
      setIsBusy(false);
    }
  }

  async function handleRestoreSnapshot() {
    if (!restoreCandidate) {
      return;
    }
    setIsBusy(true);
    setError(null);
    try {
      await restoreProjectSnapshot(
        projectId,
        restoreCandidate.id,
        currentRevision
      );
      setRestoreCandidate(null);
      setIsBusy(false);
      onProjectStateChange();
    } catch (restoreError) {
      setError(
        getVersionHistoryError(
          restoreError,
          "Projektstand konnte nicht wiederhergestellt werden."
        )
      );
      setRestoreCandidate(null);
      setIsBusy(false);
    }
  }

  async function handleHistoryCommand(direction: "undo" | "redo") {
    setIsBusy(true);
    setError(null);
    try {
      const result =
        direction === "undo"
          ? await undoProjectCommand(projectId, currentRevision)
          : await redoProjectCommand(projectId, currentRevision);
      setHistory(result.history);
      onProjectStateChange();
    } catch (historyError) {
      setError(
        getVersionHistoryError(
          historyError,
          direction === "undo"
            ? "Die letzte Änderung konnte nicht rückgängig gemacht werden."
            : "Die letzte Änderung konnte nicht wiederholt werden."
        )
      );
    } finally {
      setIsBusy(false);
    }
  }

  const historyIsCurrent =
    history?.currentRevision === currentRevision;

  return (
    <section className="card shadow-sm">
      <div className="card-header d-flex flex-wrap justify-content-between align-items-center gap-2">
        <span>Versionen und Sicherungspunkte</span>
        <div className="d-flex align-items-center gap-2">
          <span className="badge text-bg-secondary">
            Aktuelle Revision {currentRevision}
          </span>
          <button
            className="btn btn-sm btn-outline-secondary"
            disabled={
              isBusy ||
              isHistoryLoading ||
              !historyIsCurrent ||
              !history?.undoDepth
            }
            onClick={() => void handleHistoryCommand("undo")}
            title={
              history?.undoDepth
                ? `${history.undoDepth} Änderung(en) können rückgängig gemacht werden`
                : "Keine Änderung zum Rückgängigmachen"
            }
            type="button"
          >
            Rückgängig
          </button>
          <button
            className="btn btn-sm btn-outline-secondary"
            disabled={
              isBusy ||
              isHistoryLoading ||
              !historyIsCurrent ||
              !history?.redoDepth
            }
            onClick={() => void handleHistoryCommand("redo")}
            title={
              history?.redoDepth
                ? `${history.redoDepth} Änderung(en) können wiederholt werden`
                : "Keine Änderung zum Wiederholen"
            }
            type="button"
          >
            Wiederholen
          </button>
          <button
            aria-expanded={isOpen}
            className="btn btn-sm btn-outline-secondary"
            onClick={() => setIsOpen((current) => !current)}
            type="button"
          >
            {isOpen ? "Ausblenden" : "Anzeigen"}
          </button>
        </div>
      </div>
      {error ? (
        <div className="alert alert-warning m-3 mb-0" role="alert">
          {error}
        </div>
      ) : null}
      {isOpen ? (
        <>
      <div className="card-body border-bottom">
        <h2 className="h6">Neuen Sicherungspunkt anlegen</h2>
        <p className="small text-secondary">
          Ein Sicherungspunkt speichert den vollständigen Projektstand, ohne
          die Revision oder Rückgängig-Historie zu verändern.
        </p>
        <form className="row g-2" onSubmit={handleCreateSnapshot}>
          <div className="col-12 col-lg-4">
            <label className="form-label" htmlFor="snapshot-name">
              Name
            </label>
            <input
              className="form-control"
              id="snapshot-name"
              maxLength={100}
              placeholder="z. B. Vor Planfreigabe"
              value={snapshotName}
              onChange={(event) => setSnapshotName(event.target.value)}
            />
          </div>
          <div className="col-12 col-lg-6">
            <label className="form-label" htmlFor="snapshot-description">
              Beschreibung (optional)
            </label>
            <input
              className="form-control"
              id="snapshot-description"
              maxLength={500}
              placeholder="Was kennzeichnet diesen Stand?"
              value={snapshotDescription}
              onChange={(event) =>
                setSnapshotDescription(event.target.value)
              }
            />
          </div>
          <div className="col-12 col-lg-2 d-flex align-items-end">
            <button
              className="btn btn-primary w-100"
              disabled={isBusy || !snapshotName.trim()}
              type="submit"
            >
              Stand speichern
            </button>
          </div>
        </form>
      </div>

      <div className="card-body border-bottom">
        <h2 className="h6">Gespeicherte Sicherungspunkte</h2>
        {isLoading ? (
          <p className="text-secondary mb-0">Sicherungspunkte werden geladen …</p>
        ) : snapshots.length ? (
          <div className="table-responsive">
            <table className="table table-sm align-middle mb-0">
              <thead>
                <tr>
                  <th>Name</th>
                  <th>Projektstand</th>
                  <th>Erstellt</th>
                  <th className="text-end">Aktion</th>
                </tr>
              </thead>
              <tbody>
                {snapshots.map((snapshot) => (
                  <tr key={snapshot.id}>
                    <td>
                      <strong>{snapshot.name}</strong>
                      <span
                        className={`badge ms-2 ${
                          snapshot.kind === "automatic"
                            ? "text-bg-info"
                            : "text-bg-secondary"
                        }`}
                      >
                        {getProjectSnapshotKindLabel(snapshot.kind)}
                      </span>
                      {snapshot.description ? (
                        <div className="small text-secondary">
                          {snapshot.description}
                        </div>
                      ) : null}
                    </td>
                    <td>Revision {snapshot.sourceRevision}</td>
                    <td>{formatDateTime(snapshot.createdAtIso)}</td>
                    <td className="text-end">
                      <button
                        className="btn btn-sm btn-outline-danger"
                        disabled={isBusy}
                        onClick={() => setRestoreCandidate(snapshot)}
                        type="button"
                      >
                        Wiederherstellen
                      </button>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        ) : (
          <p className="text-secondary mb-0">
            Noch keine Sicherungspunkte vorhanden.
          </p>
        )}

        {restoreCandidate ? (
          <div className="alert alert-danger mt-3 mb-0" role="alert">
            <h3 className="h6">
              „{restoreCandidate.name}“ wirklich wiederherstellen?
            </h3>
            <p className="mb-2">
              Verteiler, Stromkreise, Projektgeräte, Räume und
              Projekteinstellungen werden auf Revision{" "}
              {restoreCandidate.sourceRevision} zurückgesetzt. Der aktuelle
              Stand bleibt als neue rückgängig machbare Revision erhalten.
            </p>
            <div className="d-flex flex-wrap gap-2">
              <button
                className="btn btn-danger"
                disabled={isBusy}
                onClick={() => void handleRestoreSnapshot()}
                type="button"
              >
                Jetzt wiederherstellen
              </button>
              <button
                className="btn btn-outline-secondary"
                disabled={isBusy}
                onClick={() => setRestoreCandidate(null)}
                type="button"
              >
                Abbrechen
              </button>
            </div>
          </div>
        ) : null}
      </div>

      <div className="card-body">
        <h2 className="h6">Letzte Änderungen</h2>
        {isLoading ? (
          <p className="text-secondary mb-0">Historie wird geladen …</p>
        ) : revisions.length ? (
          <>
            <div className="table-responsive">
              <table className="table table-sm align-middle mb-0">
                <thead>
                  <tr>
                    <th>Revision</th>
                    <th>Zeitpunkt</th>
                    <th>Art</th>
                    <th>Änderung</th>
                  </tr>
                </thead>
                <tbody>
                  {revisions.map((revision) => (
                    <tr key={revision.revisionId}>
                      <td>{revision.revisionNumber}</td>
                      <td>{formatDateTime(revision.createdAtIso)}</td>
                      <td>
                        {getProjectRevisionSourceLabel(revision.source)}
                      </td>
                      <td>{getProjectRevisionDescription(revision)}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
            {nextBeforeRevision !== null ? (
              <button
                className="btn btn-sm btn-outline-secondary mt-3"
                disabled={isBusy}
                onClick={() => void handleLoadOlderRevisions()}
                type="button"
              >
                Ältere Änderungen laden
              </button>
            ) : null}
          </>
        ) : (
          <p className="text-secondary mb-0">
            Für dieses Projekt wurden noch keine versionierten Änderungen
            gespeichert.
          </p>
        )}
      </div>
        </>
      ) : null}
    </section>
  );
}

function formatDateTime(value: string) {
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    return value;
  }
  return new Intl.DateTimeFormat("de-DE", {
    dateStyle: "medium",
    timeStyle: "short",
  }).format(date);
}

function getVersionHistoryError(error: unknown, fallback: string) {
  const details = error instanceof Error ? error.message : "";
  if (details.includes("PROJECT_SNAPSHOT_NAME_CONFLICT")) {
    return "Ein Sicherungspunkt mit diesem Namen ist bereits vorhanden.";
  }
  if (
    details.includes("PROJECT_REVISION_CONFLICT") ||
    details.includes("PROJECT_STATE_CONFLICT")
  ) {
    return "Das Projekt wurde inzwischen geändert. Bitte lade die Seite neu und versuche es erneut.";
  }
  return fallback;
}
