"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { AppShell } from "@/components/AppShell";
import { useCan } from "@/lib/abilities";
import {
  OPS_KIND_LABELS,
  OPS_STATUS_LABELS,
  formatBytes,
  formatDateTime,
} from "@/lib/labels";
import {
  backupDownloadUrl,
  deleteBackup,
  deleteIngest,
  getOpsJob,
  listBackups,
  listIngest,
  listOpsJobs,
  startOpsJob,
  uploadIngest,
} from "@/lib/api";
import type {
  BackupFile,
  IngestFile,
  OpsJob,
  OpsJobKind,
} from "@/lib/types";

const INGEST_MAX_BYTES = 2 * 1024 * 1024 * 1024;

export default function OperacionesPage() {
  return (
    <AppShell>
      <Operaciones />
    </AppShell>
  );
}

type ConfirmState =
  | { kind: "REIMPORT" }
  | { kind: "SYNC" }
  | { kind: "RESTORE"; file: string }
  | null;

function Operaciones() {
  const allowed = useCan("db:manage");

  const [ingest, setIngest] = useState<IngestFile[] | null>(null);
  const [backups, setBackups] = useState<BackupFile[] | null>(null);
  const [jobs, setJobs] = useState<OpsJob[] | null>(null);
  const [activeJob, setActiveJob] = useState<OpsJob | null>(null);

  const [error, setError] = useState<string | null>(null);
  const [notice, setNotice] = useState<string | null>(null);
  const [confirm, setConfirm] = useState<ConfirmState>(null);
  const [confirmText, setConfirmText] = useState("");
  const [uploading, setUploading] = useState<string | null>(null);
  const [starting, setStarting] = useState(false);

  const fileInputs = useRef<Record<string, HTMLInputElement | null>>({});

  const refreshLists = useCallback(() => {
    listIngest().then(setIngest).catch(() => setIngest([]));
    listBackups().then(setBackups).catch(() => setBackups([]));
    listOpsJobs()
      .then((rows) => {
        setJobs(rows);
        const running = rows.find((j) => j.status === "RUNNING");
        if (running) setActiveJob(running);
      })
      .catch(() => setJobs([]));
  }, []);

  useEffect(() => {
    if (allowed) refreshLists();
  }, [allowed, refreshLists]);

  // Poll the active job while it runs; refresh everything when it finishes.
  useEffect(() => {
    if (!activeJob || activeJob.status !== "RUNNING") return;
    const id = activeJob.id;
    const timer = setInterval(() => {
      getOpsJob(id)
        .then((job) => {
          setActiveJob(job);
          if (job.status !== "RUNNING") {
            clearInterval(timer);
            refreshLists();
            setNotice(
              job.status === "SUCCESS"
                ? `${OPS_KIND_LABELS[job.kind]} completada.`
                : `${OPS_KIND_LABELS[job.kind]} terminó con error. Revise el registro.`,
            );
          }
        })
        .catch(() => {
          /* transient — keep polling */
        });
    }, 1500);
    return () => clearInterval(timer);
  }, [activeJob, refreshLists]);

  if (!allowed) {
    return (
      <div className="page-head">
        <h1 className="page-title">Operaciones</h1>
        <div className="state-box state-error">
          No tiene permisos para administrar la base de datos.
        </div>
      </div>
    );
  }

  const jobRunning = activeJob?.status === "RUNNING";

  async function handleUpload(name: string, file: File | undefined) {
    if (!file) return;
    setError(null);
    setNotice(null);
    setUploading(name);
    try {
      await uploadIngest(name, file);
      setNotice(`${name} cargado.`);
      refreshLists();
    } catch (e) {
      setError((e as Error)?.message ?? "No se pudo cargar el archivo.");
    } finally {
      setUploading(null);
      const input = fileInputs.current[name];
      if (input) input.value = "";
    }
  }

  async function handleDeleteIngest(name: string) {
    setError(null);
    try {
      await deleteIngest(name);
      refreshLists();
    } catch (e) {
      setError((e as Error)?.message ?? "No se pudo eliminar.");
    }
  }

  async function handleDeleteBackup(name: string) {
    setError(null);
    try {
      await deleteBackup(name);
      refreshLists();
    } catch (e) {
      setError((e as Error)?.message ?? "No se pudo eliminar el respaldo.");
    }
  }

  async function start(kind: OpsJobKind, file?: string) {
    setError(null);
    setNotice(null);
    setStarting(true);
    try {
      const job = await startOpsJob(kind, file);
      setActiveJob(job);
      setJobs((prev) => (prev ? [job, ...prev] : [job]));
    } catch (e) {
      setError((e as Error)?.message ?? "No se pudo iniciar la operación.");
    } finally {
      setStarting(false);
    }
  }

  function askConfirm(state: ConfirmState) {
    setConfirm(state);
    setConfirmText("");
    setError(null);
    setNotice(null);
  }

  async function runConfirmed() {
    if (!confirm) return;
    const c = confirm;
    setConfirm(null);
    if (c.kind === "REIMPORT") await start("REIMPORT");
    else if (c.kind === "SYNC") await start("SYNC");
    else await start("RESTORE", c.file);
  }

  const ingestReady = (ingest ?? []).every((f) => f.present);

  return (
    <>
      <div className="page-head">
        <h1 className="page-title">Operaciones de base de datos</h1>
      </div>

      {error && <div className="state-box state-error">{error}</div>}
      {notice && <div className="state-box">{notice}</div>}

      {/* Active / running job with live log */}
      {activeJob && (
        <div className="card" style={{ padding: 20, marginBottom: 20 }}>
          <div className="row-actions" style={{ justifyContent: "space-between" }}>
            <h2 className="section-title" style={{ margin: 0 }}>
              {OPS_KIND_LABELS[activeJob.kind]}{" "}
              <span
                className={`badge ${
                  activeJob.status === "SUCCESS"
                    ? "badge-positive"
                    : activeJob.status === "FAILED"
                      ? "badge-negative"
                      : "badge-neutral"
                }`}
              >
                {jobRunning && <span className="spinner" aria-hidden style={{ marginRight: 6 }} />}
                {OPS_STATUS_LABELS[activeJob.status]}
              </span>
            </h2>
            {!jobRunning && (
              <button className="btn btn-ghost" type="button" onClick={() => setActiveJob(null)}>
                Ocultar
              </button>
            )}
          </div>
          <pre className="ops-log">{activeJob.log || "Iniciando…"}</pre>
        </div>
      )}

      {/* Ingest folder */}
      <div className="card" style={{ padding: 20, marginBottom: 20 }}>
        <h2 className="section-title">Carpeta de ingesta</h2>
        <p className="inline-form-note">
          Los cuatro archivos originales de Access. La reimportación y la
          sincronización leen de aquí. Tamaño máximo por archivo: {formatBytes(INGEST_MAX_BYTES)}.
        </p>
        <div className="tx-scroll">
          <table className="tx-table">
            <thead>
              <tr>
                <th>Archivo</th>
                <th>Estado</th>
                <th className="num">Tamaño</th>
                <th>Modificado</th>
                <th className="num">Acciones</th>
              </tr>
            </thead>
            <tbody>
              {(ingest ?? []).map((f) => (
                <tr key={f.name}>
                  <td className="mono">{f.name}</td>
                  <td>
                    <span className={`badge ${f.present ? "badge-positive" : "badge-negative"}`}>
                      {f.present ? "Presente" : "Falta"}
                    </span>
                  </td>
                  <td className="num">{formatBytes(f.size)}</td>
                  <td>{formatDateTime(f.modifiedAt)}</td>
                  <td>
                    <div className="row-actions">
                      <input
                        ref={(el) => {
                          fileInputs.current[f.name] = el;
                        }}
                        type="file"
                        style={{ display: "none" }}
                        onChange={(e) => handleUpload(f.name, e.target.files?.[0])}
                      />
                      <button
                        className="btn btn-outline"
                        type="button"
                        disabled={uploading === f.name}
                        onClick={() => fileInputs.current[f.name]?.click()}
                      >
                        {uploading === f.name ? "Cargando…" : f.present ? "Reemplazar" : "Cargar"}
                      </button>
                      {f.present && (
                        <button
                          className="btn btn-ghost"
                          type="button"
                          onClick={() => handleDeleteIngest(f.name)}
                        >
                          Eliminar
                        </button>
                      )}
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      {/* Operations */}
      <div className="card" style={{ padding: 20, marginBottom: 20 }}>
        <h2 className="section-title">Operaciones</h2>
        <div className="form-grid">
          <OpTile
            title="Respaldo"
            desc="Genera un volcado comprimido de la base de datos actual."
            action="Crear respaldo"
            tone="primary"
            disabled={jobRunning || starting}
            onClick={() => start("BACKUP")}
          />
          <OpTile
            title="Reimportar (purga)"
            desc="Respalda, borra TODO y reconstruye desde los archivos de ingesta. Se pierden los datos capturados manualmente."
            action="Reimportar"
            tone="danger"
            disabled={jobRunning || starting || !ingestReady}
            onClick={() => askConfirm({ kind: "REIMPORT" })}
          />
          <OpTile
            title="Sincronizar"
            desc="Respalda, luego importa lo nuevo del legado. Borra del sistema los registros del legado que ya no aparecen en los archivos de ingesta. Se conservan los datos capturados a mano."
            action="Sincronizar"
            tone="primary"
            disabled={jobRunning || starting || !ingestReady}
            onClick={() => askConfirm({ kind: "SYNC" })}
          />
        </div>
        {!ingestReady && (
          <p className="inline-form-note" style={{ marginTop: 12 }}>
            La reimportación requiere que los cuatro archivos estén presentes.
          </p>
        )}
      </div>

      {/* Backups */}
      <div className="card" style={{ padding: 20, marginBottom: 20 }}>
        <h2 className="section-title">Respaldos</h2>
        <p className="inline-form-note">
          Restaurar sobreescribe la base de datos completa con el respaldo elegido.
        </p>
        <div className="tx-scroll">
          <table className="tx-table">
            <thead>
              <tr>
                <th>Archivo</th>
                <th className="num">Tamaño</th>
                <th>Creado</th>
                <th className="num">Acciones</th>
              </tr>
            </thead>
            <tbody>
              {backups === null ? (
                <tr>
                  <td colSpan={4}>
                    <span className="spinner" aria-label="Cargando" />
                  </td>
                </tr>
              ) : backups.length === 0 ? (
                <tr>
                  <td colSpan={4} className="muted">
                    Sin respaldos.
                  </td>
                </tr>
              ) : (
                backups.map((b) => (
                  <tr key={b.name}>
                    <td className="mono">{b.name}</td>
                    <td className="num">{formatBytes(b.size)}</td>
                    <td>{formatDateTime(b.createdAt)}</td>
                    <td>
                      <div className="row-actions">
                        <a className="btn btn-outline" href={backupDownloadUrl(b.name)}>
                          Descargar
                        </a>
                        <button
                          className="btn btn-outline"
                          type="button"
                          disabled={jobRunning || starting}
                          onClick={() => askConfirm({ kind: "RESTORE", file: b.name })}
                        >
                          Restaurar
                        </button>
                        <button
                          className="btn btn-ghost"
                          type="button"
                          onClick={() => handleDeleteBackup(b.name)}
                        >
                          Eliminar
                        </button>
                      </div>
                    </td>
                  </tr>
                ))
              )}
            </tbody>
          </table>
        </div>
      </div>

      {/* Recent jobs */}
      <div className="card" style={{ padding: 20 }}>
        <h2 className="section-title">Historial</h2>
        <div className="tx-scroll">
          <table className="tx-table">
            <thead>
              <tr>
                <th>Operación</th>
                <th>Estado</th>
                <th>Inicio</th>
                <th>Fin</th>
                <th className="num"></th>
              </tr>
            </thead>
            <tbody>
              {(jobs ?? []).map((j) => (
                <tr key={j.id}>
                  <td>{OPS_KIND_LABELS[j.kind]}</td>
                  <td>
                    <span
                      className={`badge ${
                        j.status === "SUCCESS"
                          ? "badge-positive"
                          : j.status === "FAILED"
                            ? "badge-negative"
                            : "badge-neutral"
                      }`}
                    >
                      {OPS_STATUS_LABELS[j.status]}
                    </span>
                  </td>
                  <td>{formatDateTime(j.startedAt)}</td>
                  <td>{formatDateTime(j.finishedAt)}</td>
                  <td className="num">
                    <button
                      className="btn btn-ghost"
                      type="button"
                      onClick={() => setActiveJob(j)}
                    >
                      Ver registro
                    </button>
                  </td>
                </tr>
              ))}
              {jobs && jobs.length === 0 && (
                <tr>
                  <td colSpan={5} className="muted">
                    Sin operaciones registradas.
                  </td>
                </tr>
              )}
            </tbody>
          </table>
        </div>
      </div>

      {/* Destructive-op confirm */}
      {confirm && (
        <div className="modal-backdrop" role="dialog" aria-modal="true">
          <div className="card" style={{ padding: 24, maxWidth: 480 }}>
            <h2 className="section-title" style={{ marginTop: 0 }}>
              {confirm.kind === "REIMPORT"
                ? "Confirmar reimportación"
                : confirm.kind === "SYNC"
                  ? "Confirmar sincronización"
                  : "Confirmar restauración"}
            </h2>
            <p className="inline-form-note">
              {confirm.kind === "REIMPORT"
                ? "Esto BORRA todos los datos actuales (incluidos los capturados a mano) y reconstruye desde los archivos de ingesta. Se creará un respaldo previo automático."
                : confirm.kind === "SYNC"
                  ? "Se creará un respaldo previo automático. Luego se importarán al sistema los registros nuevos del legado y se eliminarán los del legado que ya no aparezcan en los archivos de ingesta. Los datos capturados a mano NO se borran."
                  : `Esto sobreescribe la base de datos completa con “${confirm.file}”. Se recomienda crear un respaldo antes.`}
            </p>
            <label className="field">
              <span className="field-label">Escriba CONFIRMAR para continuar</span>
              <input
                className="input"
                value={confirmText}
                onChange={(e) => setConfirmText(e.target.value)}
                autoFocus
              />
            </label>
            <div className="form-actions">
              <button
                className={confirm.kind === "SYNC" ? "btn btn-primary" : "btn btn-danger"}
                type="button"
                disabled={confirmText !== "CONFIRMAR" || starting}
                onClick={runConfirmed}
              >
                {confirm.kind === "REIMPORT"
                  ? "Reimportar"
                  : confirm.kind === "SYNC"
                    ? "Sincronizar"
                    : "Restaurar"}
              </button>
              <button className="btn btn-outline" type="button" onClick={() => setConfirm(null)}>
                Cancelar
              </button>
            </div>
          </div>
        </div>
      )}
    </>
  );
}

function OpTile({
  title,
  desc,
  action,
  tone,
  disabled,
  onClick,
}: {
  title: string;
  desc: string;
  action: string;
  tone: "primary" | "danger" | "muted";
  disabled: boolean;
  onClick: () => void;
}) {
  const btnClass =
    tone === "danger" ? "btn btn-danger" : tone === "muted" ? "btn btn-outline" : "btn btn-primary";
  return (
    <div className="card" style={{ padding: 16 }}>
      <h3 className="section-title" style={{ fontSize: 15, margin: "0 0 4px" }}>
        {title}
      </h3>
      <p className="inline-form-note" style={{ minHeight: 48 }}>
        {desc}
      </p>
      <button className={btnClass} type="button" disabled={disabled} onClick={onClick}>
        {action}
      </button>
    </div>
  );
}
