/**
 * Self-contained auto-refresh scheduler for ParcelSync.
 *
 * Runs inside the existing Node.js process — no external dependencies.
 * Checks every 30 minutes; during the night window (1–5 AM) it picks
 * stale UATs one at a time with random delays between them.
 *
 * Activated by importing this module (side-effect). The globalThis guard
 * ensures only one scheduler runs per process, surviving HMR in dev.
 */

import { PrismaClient } from "@prisma/client";
import { syncLayer } from "./sync-service";
import { getLayerFreshness, isFresh } from "./enrich-service";
import { isEterraAvailable } from "./eterra-health";
import { isWeekendWindow, runWeekendDeepSync } from "./weekend-deep-sync";

const prisma = new PrismaClient();
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

/* ------------------------------------------------------------------ */
/*  Configuration                                                       */
/* ------------------------------------------------------------------ */

/** Night window: only run between these hours (server local time) */
const NIGHT_START_HOUR = 1;
const NIGHT_END_HOUR = 5;

/** How often to check if we should run (ms) */
const CHECK_INTERVAL_MS = 30 * 60_000; // 30 minutes

/** Delay between UATs: 3–10s (delta sync is fast) */
const MIN_DELAY_MS = 3_000;
const MAX_DELAY_MS = 10_000;

/** Enrichment ratio threshold — UATs with >30% enriched get magic mode */
const MAGIC_THRESHOLD = 0.3;

/* ------------------------------------------------------------------ */
/*  Singleton guard                                                     */
/* ------------------------------------------------------------------ */

const g = globalThis as {
  __autoRefreshTimer?: ReturnType<typeof setInterval>;
  __parcelSyncRunning?: boolean; // single flag for all sync modes
  __autoRefreshLastRun?: string; // ISO date of last completed run
};

/* ------------------------------------------------------------------ */
/*  Core logic                                                          */
/* ------------------------------------------------------------------ */

async function runAutoRefresh() {
  // Prevent concurrent runs (shared with weekend sync)
  if (g.__parcelSyncRunning) return;

  const hour = new Date().getHours();
  const dayOfWeek = new Date().getDay(); // 0=Sun, 6=Sat
  const isWeekday = dayOfWeek >= 1 && dayOfWeek <= 5;
  if (!isWeekday || hour < NIGHT_START_HOUR || hour >= NIGHT_END_HOUR) return;

  // Only run once per night (check date)
  const today = new Date().toISOString().slice(0, 10);
  if (g.__autoRefreshLastRun === today) return;

  const username = process.env.ETERRA_USERNAME;
  const password = process.env.ETERRA_PASSWORD;
  if (!username || !password) return;

  if (!isEterraAvailable()) {
    console.log("[auto-refresh] eTerra indisponibil, skip.");
    return;
  }

  g.__parcelSyncRunning = true;
  console.log("[auto-refresh] Pornire delta refresh nocturn (toate UAT-urile)...");

  try {
    // Find all UATs with features + enrichment ratio
    const uats = await prisma.$queryRawUnsafe<
      Array<{ siruta: string; name: string | null; total: number; enriched: number }>
    >(
      `SELECT f.siruta, u.name, COUNT(*)::int as total,
       COUNT(*) FILTER (WHERE f."enrichedAt" IS NOT NULL)::int as enriched
       FROM "GisFeature" f LEFT JOIN "GisUat" u ON f.siruta = u.siruta
       WHERE f."layerId" IN ('TERENURI_ACTIVE','CLADIRI_ACTIVE') AND f."objectId" > 0
       GROUP BY f.siruta, u.name ORDER BY total DESC`,
    );

    if (uats.length === 0) {
      console.log("[auto-refresh] Niciun UAT in DB, skip.");
      g.__autoRefreshLastRun = today;
      g.__parcelSyncRunning = false;
      return;
    }

    console.log(`[auto-refresh] ${uats.length} UAT-uri de procesat.`);
    let processed = 0;
    let errors = 0;

    for (let i = 0; i < uats.length; i++) {
      const uat = uats[i]!;
      const uatName = uat.name ?? uat.siruta;
      const ratio = uat.total > 0 ? uat.enriched / uat.total : 0;
      const isMagic = ratio > MAGIC_THRESHOLD;

      // Small delay between UATs
      if (i > 0) {
        const delay = MIN_DELAY_MS + Math.random() * (MAX_DELAY_MS - MIN_DELAY_MS);
        await sleep(delay);
      }

      // Check we're still in the night window
      if (new Date().getHours() >= NIGHT_END_HOUR) {
        console.log(`[auto-refresh] Fereastra nocturna s-a inchis la ${i}/${uats.length} UATs.`);
        break;
      }

      if (!isEterraAvailable()) {
        console.log("[auto-refresh] eTerra a devenit indisponibil, opresc.");
        break;
      }

      const start = Date.now();
      try {
        // Delta sync: quick-count + VALID_FROM for TERENURI + CLADIRI
        const tRes = await syncLayer(username, password, uat.siruta, "TERENURI_ACTIVE", { uatName });
        const cRes = await syncLayer(username, password, uat.siruta, "CLADIRI_ACTIVE", { uatName });

        let enrichNote = "";
        if (isMagic) {
          const { EterraClient } = await import("./eterra-client");
          const { enrichFeatures } = await import("./enrich-service");
          const client = await EterraClient.create(username, password, { timeoutMs: 120_000 });
          const eRes = await enrichFeatures(client, uat.siruta);
          enrichNote = eRes.status === "done"
            ? ` | enrich:${eRes.enrichedCount}/${eRes.totalFeatures ?? "?"}`
            : ` | enrich ERR:${eRes.error}`;
        }

        const dur = ((Date.now() - start) / 1000).toFixed(1);
        const tNote = tRes.newFeatures > 0 || (tRes.validFromUpdated ?? 0) > 0
          ? `T:+${tRes.newFeatures}/${tRes.validFromUpdated ?? 0}vf`
          : "T:ok";
        const cNote = cRes.newFeatures > 0 || (cRes.validFromUpdated ?? 0) > 0
          ? `C:+${cRes.newFeatures}/${cRes.validFromUpdated ?? 0}vf`
          : "C:ok";
        console.log(
          `[auto-refresh] [${i + 1}/${uats.length}] ${uatName} (${isMagic ? "magic" : "base"}): ${tNote}, ${cNote}${enrichNote} (${dur}s)`,
        );
        processed++;
      } catch (err) {
        errors++;
        const msg = err instanceof Error ? err.message : String(err);
        console.error(`[auto-refresh] [${i + 1}/${uats.length}] ${uatName}: ERR ${msg}`);
      }
    }

    g.__autoRefreshLastRun = today;
    console.log(`[auto-refresh] Finalizat: ${processed}/${uats.length} UATs, ${errors} erori.`);

    // Trigger PMTiles rebuild
    const { firePmtilesRebuild } = await import("./pmtiles-webhook");
    await firePmtilesRebuild("auto-refresh-complete", { uatCount: processed, errors });
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    console.error(`[auto-refresh] Eroare generala: ${msg}`);
  } finally {
    g.__parcelSyncRunning = false;
  }
}

/* ------------------------------------------------------------------ */
/*  Weekend deep sync wrapper                                           */
/* ------------------------------------------------------------------ */

async function runWeekendCheck() {
  if (g.__parcelSyncRunning) return;
  if (!isWeekendWindow()) return;

  g.__parcelSyncRunning = true;
  try {
    await runWeekendDeepSync();
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    console.error(`[weekend-sync] Eroare: ${msg}`);
  } finally {
    g.__parcelSyncRunning = false;
  }
}

/* ------------------------------------------------------------------ */
/*  Start scheduler (once per process)                                  */
/* ------------------------------------------------------------------ */

if (!g.__autoRefreshTimer) {
  g.__autoRefreshTimer = setInterval(() => {
    // Weekend nights (Fri/Sat/Sun 23-04): deep sync for large cities
    // Weekday nights (1-5 AM): incremental refresh for existing data
    if (isWeekendWindow()) {
      void runWeekendCheck();
    } else {
      void runAutoRefresh();
    }
  }, CHECK_INTERVAL_MS);

  // Also check once shortly after startup (60s delay to let everything init)
  setTimeout(() => {
    if (isWeekendWindow()) {
      void runWeekendCheck();
    } else {
      void runAutoRefresh();
    }
  }, 60_000);

  const now = new Date();
  console.log(
    `[auto-refresh] Scheduler pornit — verificare la fiecare ${CHECK_INTERVAL_MS / 60_000} min`,
  );
  console.log(
    `[auto-refresh] Server time: ${now.toLocaleString("ro-RO")} (TZ=${process.env.TZ ?? "system"}, offset=${now.getTimezoneOffset()}min)`,
  );
  console.log(
    `[auto-refresh] Luni-Vineri ${NIGHT_START_HOUR}:00–${NIGHT_END_HOUR}:00: delta sync ALL UATs (quick-count + VALID_FROM + rolling doc)`,
  );
  console.log(
    `[auto-refresh] Weekend Vin/Sam/Dum 23:00–04:00: deep sync municipii (forceFullSync)`,
  );
  console.log(
    `[auto-refresh] ETERRA creds: ${process.env.ETERRA_USERNAME ? "OK" : "MISSING"}`,
  );
}
