/* eslint-disable @typescript-eslint/no-explicit-any */
/**
 * POST /api/eterra/sync-background
 *
 * Starts a background sync + enrichment job on the server.
 * Returns immediately with the jobId — work continues in-process.
 * Progress is tracked via /api/eterra/progress?jobId=...
 *
 * The user can close the browser and come back later;
 * data is written to PostgreSQL and persists across sessions.
 *
 * Body: { siruta, mode?: "base"|"magic", forceSync?: boolean, includeNoGeometry?: boolean }
 */

import {
  getSessionCredentials,
  registerJob,
  unregisterJob,
} from "@/modules/parcel-sync/services/session-store";
import {
  setProgress,
  clearProgress,
  type SyncProgress,
} from "@/modules/parcel-sync/services/progress-store";
import { syncLayer } from "@/modules/parcel-sync/services/sync-service";
import {
  enrichFeatures,
  getLayerFreshness,
  isFresh,
} from "@/modules/parcel-sync/services/enrich-service";
import { EterraClient } from "@/modules/parcel-sync/services/eterra-client";
import { syncNoGeometryParcels } from "@/modules/parcel-sync/services/no-geom-sync";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

type Body = {
  username?: string;
  password?: string;
  siruta?: string | number;
  mode?: "base" | "magic";
  forceSync?: boolean;
  includeNoGeometry?: boolean;
};

export async function POST(req: Request) {
  try {
    const body = (await req.json()) as Body;
    const session = getSessionCredentials();
    const username = String(
      body.username || session?.username || process.env.ETERRA_USERNAME || "",
    ).trim();
    const password = String(
      body.password || session?.password || process.env.ETERRA_PASSWORD || "",
    ).trim();
    const siruta = String(body.siruta ?? "").trim();
    const mode = body.mode === "magic" ? "magic" : "base";
    const forceSync = body.forceSync === true;
    const includeNoGeometry = body.includeNoGeometry === true;

    if (!username || !password) {
      return Response.json(
        { error: "Credențiale lipsă — conectează-te la eTerra." },
        { status: 401 },
      );
    }
    if (!/^\d+$/.test(siruta)) {
      return Response.json({ error: "SIRUTA invalid" }, { status: 400 });
    }

    const jobId = crypto.randomUUID();
    registerJob(jobId);

    // Set initial progress so the UI picks it up immediately
    setProgress({
      jobId,
      downloaded: 0,
      total: 100,
      status: "running",
      phase: "Pornire sincronizare fundal",
    });

    // Fire and forget — runs in the Node.js event loop after the response is sent.
    // In Docker standalone mode the Node.js process is long-lived.
    void runBackground({
      jobId,
      username,
      password,
      siruta,
      mode,
      forceSync,
      includeNoGeometry,
    });

    return Response.json(
      {
        jobId,
        message: `Sincronizare ${mode === "magic" ? "Magic" : "bază"} pornită în fundal.`,
      },
      { status: 202 },
    );
  } catch (error) {
    const msg = error instanceof Error ? error.message : "Eroare server";
    return Response.json({ error: msg }, { status: 500 });
  }
}

/* ────────────────────────────────────────────────── */
/*  Background worker                                  */
/* ────────────────────────────────────────────────── */

async function runBackground(params: {
  jobId: string;
  username: string;
  password: string;
  siruta: string;
  mode: "base" | "magic";
  forceSync: boolean;
  includeNoGeometry: boolean;
}) {
  const {
    jobId,
    username,
    password,
    siruta,
    mode,
    forceSync,
    includeNoGeometry,
  } = params;

  // Weighted progress (same logic as export-bundle)
  let completedWeight = 0;
  let currentWeight = 0;
  let phase = "Inițializare";
  let note: string | undefined;

  const push = (partial: Partial<SyncProgress>) => {
    setProgress({
      jobId,
      downloaded: 0,
      total: 100,
      status: "running",
      phase,
      note,
      ...partial,
    } as SyncProgress);
  };

  const updateOverall = (fraction = 0) => {
    const overall = completedWeight + currentWeight * fraction;
    push({
      downloaded: Number(Math.min(100, Math.max(0, overall)).toFixed(1)),
      total: 100,
    });
  };

  const setPhase = (next: string, weight: number) => {
    phase = next;
    currentWeight = weight;
    note = undefined;
    updateOverall(0);
  };

  const finishPhase = () => {
    completedWeight += currentWeight;
    currentWeight = 0;
    note = undefined;
    updateOverall(0);
  };

  try {
    const isMagic = mode === "magic";
    const hasNoGeom = includeNoGeometry;
    const weights = isMagic
      ? hasNoGeom
        ? { sync: 35, noGeom: 10, enrich: 55 }
        : { sync: 40, noGeom: 0, enrich: 60 }
      : hasNoGeom
        ? { sync: 70, noGeom: 30, enrich: 0 }
        : { sync: 100, noGeom: 0, enrich: 0 };

    /* ── Phase 1: Sync GIS layers ──────────────────────── */
    setPhase("Verificare date locale", weights.sync);

    const [terenuriStatus, cladiriStatus] = await Promise.all([
      getLayerFreshness(siruta, "TERENURI_ACTIVE"),
      getLayerFreshness(siruta, "CLADIRI_ACTIVE"),
    ]);

    const terenuriNeedsFullSync =
      forceSync || terenuriStatus.featureCount === 0;
    const cladiriNeedsFullSync =
      forceSync || cladiriStatus.featureCount === 0;

    // Always call syncLayer — it handles quick-count + VALID_FROM delta internally.
    // Only force full download when no local data or explicit forceSync.
    phase = "Sincronizare terenuri";
    push({});
    const terenuriResult = await syncLayer(username, password, siruta, "TERENURI_ACTIVE", {
      forceFullSync: terenuriNeedsFullSync,
      jobId,
      isSubStep: true,
    });
    if (terenuriResult.status === "error")
      throw new Error(terenuriResult.error ?? "Sync terenuri failed");
    updateOverall(0.5);

    phase = "Sincronizare clădiri";
    push({});
    const cladiriResult = await syncLayer(username, password, siruta, "CLADIRI_ACTIVE", {
      forceFullSync: cladiriNeedsFullSync,
      jobId,
      isSubStep: true,
    });
    if (cladiriResult.status === "error")
      throw new Error(cladiriResult.error ?? "Sync clădiri failed");

    // Sync admin layers — skip if synced within 24h
    for (const adminLayer of ["LIMITE_INTRAV_DYNAMIC", "LIMITE_UAT"]) {
      const adminStatus = await getLayerFreshness(siruta, adminLayer);
      if (!forceSync && isFresh(adminStatus.lastSynced, 24)) continue;
      phase = `Sincronizare ${adminLayer === "LIMITE_UAT" ? "limite UAT" : "limite intravilan"}`;
      push({});
      try {
        await syncLayer(username, password, siruta, adminLayer, {
          forceFullSync: forceSync,
          jobId,
          isSubStep: true,
        });
      } catch {
        note = `Avertisment: ${adminLayer} nu s-a sincronizat`;
        push({});
      }
    }

    const syncSummary = [
      terenuriResult.newFeatures > 0 ? `${terenuriResult.newFeatures} terenuri noi` : null,
      terenuriResult.validFromUpdated ? `${terenuriResult.validFromUpdated} terenuri actualizate` : null,
      cladiriResult.newFeatures > 0 ? `${cladiriResult.newFeatures} cladiri noi` : null,
      cladiriResult.validFromUpdated ? `${cladiriResult.validFromUpdated} cladiri actualizate` : null,
    ].filter(Boolean);
    note = syncSummary.length > 0 ? syncSummary.join(", ") : "Fără schimbări";
    finishPhase();

    /* ── Phase 2: No-geometry import (optional) ──────── */
    if (hasNoGeom && weights.noGeom > 0) {
      setPhase("Verificare parcele fără geometrie", weights.noGeom);
      // Skip no-geom import if recently done (within 48h) and not forced
      const { PrismaClient } = await import("@prisma/client");
      const _prisma = new PrismaClient();
      let skipNoGeom = false;
      try {
        const recentNoGeom = await _prisma.gisFeature.findFirst({
          where: {
            layerId: "TERENURI_ACTIVE",
            siruta,
            geometrySource: "NO_GEOMETRY",
            updatedAt: { gte: new Date(Date.now() - 48 * 60 * 60 * 1000) },
          },
          select: { id: true },
        });
        skipNoGeom = !forceSync && recentNoGeom != null;
      } catch { /* proceed with import */ }
      await _prisma.$disconnect();

      if (skipNoGeom) {
        note = "Parcele fără geometrie — actualizate recent, skip";
        push({});
      } else {
        phase = "Import parcele fără geometrie";
        push({});
        const noGeomClient = await EterraClient.create(username, password, {
          timeoutMs: 120_000,
        });
        const res = await syncNoGeometryParcels(noGeomClient, siruta, {
          onProgress: (done, tot, ph) => {
            phase = ph;
            push({});
          },
        });
        if (res.status === "error") {
          note = `Avertisment no-geom: ${res.error}`;
          push({});
        } else {
          const cleanNote =
            res.cleaned > 0 ? `, ${res.cleaned} vechi șterse` : "";
          note = `${res.imported} parcele noi importate${cleanNote}`;
          push({});
        }
      }
      finishPhase();
    }

    /* ── Phase 3: Enrich (magic mode only) ────────────── */
    if (isMagic) {
      setPhase("Verificare îmbogățire", weights.enrich);
      const enrichStatus = await getLayerFreshness(siruta, "TERENURI_ACTIVE");
      const needsEnrich =
        forceSync ||
        enrichStatus.enrichedCount === 0 ||
        enrichStatus.enrichedCount < enrichStatus.featureCount;

      if (needsEnrich) {
        phase = "Îmbogățire parcele (CF, proprietari, adrese)";
        push({});
        const client = await EterraClient.create(username, password, {
          timeoutMs: 120_000,
        });
        const enrichResult = await enrichFeatures(client, siruta, {
          onProgress: (done, tot, ph) => {
            phase = ph;
            const frac = tot > 0 ? done / tot : 0;
            updateOverall(frac);
          },
        });
        note =
          enrichResult.status === "done"
            ? `Îmbogățite ${enrichResult.enrichedCount}/${enrichResult.totalFeatures ?? "?"}`
            : `Eroare: ${enrichResult.error}`;
      } else {
        note = "Îmbogățire existentă — skip";
      }
      finishPhase();
    }

    /* ── Done ──────────────────────────────────────────── */
    setProgress({
      jobId,
      downloaded: 100,
      total: 100,
      status: "done",
      phase: "Sincronizare completă",
      message: `Datele sunt în baza de date. Descarcă ZIP-ul de acolo oricând.`,
      note,
    });
    unregisterJob(jobId);
    // Keep progress visible for 6 hours (background jobs can be very long)
    setTimeout(() => clearProgress(jobId), 6 * 3_600_000);
  } catch (error) {
    const msg = error instanceof Error ? error.message : "Eroare necunoscută";
    setProgress({
      jobId,
      downloaded: 0,
      total: 100,
      status: "error",
      phase: "Eroare sincronizare fundal",
      message: msg,
    });
    unregisterJob(jobId);
    setTimeout(() => clearProgress(jobId), 6 * 3_600_000);
  }
}
