#!/usr/bin/env node
/**
 * Inserta reservas de demo usando service role.
 *
 * Modos:
 *   Por defecto: lee scripts/seed-reservations.default.json (o SEED_RESERVATIONS_JSON).
 *   SEED_AUTO=1: consulta users y resources en Supabase y genera filas (sin nombres hardcodeados).
 *
 * Requiere:
 *   NEXT_PUBLIC_SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY
 *
 * Opcional:
 *   SEED_DRY_RUN=1
 *   SEED_AUTO_USER_LIMIT (default 12), SEED_AUTO_RESOURCE_LIMIT (default 20)
 *   SEED_AUTO_DAYS (default 5), SEED_AUTO_COUNT (default 30, tope de filas)
 *   SEED_AUTO_FIRST_DATE=YYYY-MM-DD — primer día civil (interpretado con offset abajo); si falta, mañana UTC.
 *   SEED_LOCAL_ADD_MINUTES_TO_UTC (default 180) — Chile UTC-3: hora civil + este valor = instante UTC.
 *   SEED_USER_EMAIL_CONTAINS — ej. humand.co para filtrar usuarios (ilike %value%)
 */

import { createClient } from "@supabase/supabase-js"
import { readFileSync, existsSync } from "fs"
import { dirname, isAbsolute, join } from "path"
import { fileURLToPath } from "url"

const __dirname = dirname(fileURLToPath(import.meta.url))
const DEFAULT_DATA = join(__dirname, "seed-reservations.default.json")
const NOTES = "Seed demo hu-rooms"

const url = process.env.NEXT_PUBLIC_SUPABASE_URL?.trim()
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY?.trim()
const dryRun = process.env.SEED_DRY_RUN === "1" || process.env.SEED_DRY_RUN === "true"
const autoMode = process.env.SEED_AUTO === "1" || process.env.SEED_AUTO === "true"
const rawJsonPath = process.env.SEED_RESERVATIONS_JSON?.trim()
const dataPath = rawJsonPath
  ? isAbsolute(rawJsonPath)
    ? rawJsonPath
    : join(process.cwd(), rawJsonPath)
  : DEFAULT_DATA

function loadReservationsFromJson() {
  if (!existsSync(dataPath)) {
    throw new Error(`No se encontró el archivo de datos: ${dataPath}`)
  }
  const raw = JSON.parse(readFileSync(dataPath, "utf8"))
  const list = raw.reservations
  if (!Array.isArray(list) || list.length === 0) {
    throw new Error('JSON inválido: se esperaba { "reservations": [...] }')
  }
  return list
}

function validateRow(row, index) {
  const { id, userEmail, resourceName, startAt, endAt } = row
  if (!id || !userEmail || !resourceName || !startAt || !endAt) {
    throw new Error(`Fila ${index}: faltan id, userEmail, resourceName, startAt o endAt`)
  }
  const s = new Date(startAt).getTime()
  const e = new Date(endAt).getTime()
  if (Number.isNaN(s) || Number.isNaN(e) || e <= s) {
    throw new Error(`Fila ${index}: rango de fechas inválido`)
  }
}

function pad2(n) {
  return String(n).padStart(2, "0")
}

function todayYmdUtc() {
  const n = new Date()
  return `${n.getUTCFullYear()}-${pad2(n.getUTCMonth() + 1)}-${pad2(n.getUTCDate())}`
}

function addDaysYmd(ymd, delta) {
  const [y, m, d] = ymd.split("-").map(Number)
  const dt = new Date(Date.UTC(y, m - 1, d + delta, 12, 0, 0, 0))
  return `${dt.getUTCFullYear()}-${pad2(dt.getUTCMonth() + 1)}-${pad2(dt.getUTCDate())}`
}

function parsePgTime(t) {
  const s = String(t ?? "09:00:00")
  const [hh, mm = "0"] = s.split(":")
  return { h: Number(hh), mi: Number(mm) || 0 }
}

function localCivilToUtcIso(y, mo, d, h, mi) {
  const addMin = Number.parseInt(process.env.SEED_LOCAL_ADD_MINUTES_TO_UTC ?? "180", 10)
  return new Date(Date.UTC(y, mo - 1, d, h, mi, 0, 0) + addMin * 60 * 1000).toISOString()
}

function typeWindow(row) {
  const tr = row.resource_types
  const t = Array.isArray(tr) ? tr[0] : tr
  const start = parsePgTime(t?.booking_window_start ?? "09:00:00")
  const end = parsePgTime(t?.booking_window_end ?? "18:00:00")
  let startH = start.h
  let endH = end.h
  if (endH <= startH) {
    startH = 9
    endH = 18
  }
  const numOneHourStarts = Math.max(1, endH - startH - 1)
  return { startH, endH, numOneHourStarts }
}

function autoUuid(index) {
  const hex = index.toString(16).padStart(12, "0")
  return `a1000000-0000-4000-8000-${hex}`
}

async function buildAutoRows(supabase) {
  const userLimit = Number.parseInt(process.env.SEED_AUTO_USER_LIMIT ?? "12", 10)
  const resourceLimit = Number.parseInt(process.env.SEED_AUTO_RESOURCE_LIMIT ?? "20", 10)
  const days = Number.parseInt(process.env.SEED_AUTO_DAYS ?? "5", 10)
  const maxRows = Number.parseInt(process.env.SEED_AUTO_COUNT ?? "30", 10)
  const emailFilter = process.env.SEED_USER_EMAIL_CONTAINS?.trim()

  let userQuery = supabase.from("users").select("id,email").order("email").limit(userLimit)
  if (emailFilter) {
    userQuery = userQuery.ilike("email", `%${emailFilter}%`)
  }

  const { data: users, error: usersError } = await userQuery
  if (usersError) {
    throw new Error(`users: ${usersError.message}`)
  }
  if (!users?.length) {
    throw new Error(
      emailFilter
        ? `No hay usuarios que coincidan con SEED_USER_EMAIL_CONTAINS=${emailFilter}. Quitá el filtro o ajustalo.`
        : "No hay filas en public.users. Creá usuarios (login PoC) antes del seed automático.",
    )
  }

  const { data: resources, error: resError } = await supabase
    .from("resources")
    .select(
      `
      id,
      name,
      resource_type_id,
      resource_types (
        booking_window_start,
        booking_window_end
      )
    `,
    )
    .or("is_active.is.null,is_active.eq.true")
    .order("name")
    .limit(resourceLimit)

  if (resError) {
    throw new Error(`resources: ${resError.message}`)
  }
  if (!resources?.length) {
    throw new Error("No hay recursos activos en public.resources.")
  }

  const firstDate =
    process.env.SEED_AUTO_FIRST_DATE?.trim() || addDaysYmd(todayYmdUtc(), 1)

  const rows = []
  let seq = 0

  outer: for (let d = 0; d < days && rows.length < maxRows; d++) {
    const ymd = addDaysYmd(firstDate, d)
    const [y, mo, day] = ymd.split("-").map(Number)

    for (let ri = 0; ri < resources.length && rows.length < maxRows; ri++) {
      const res = resources[ri]
      const { startH, numOneHourStarts } = typeWindow(res)
      const hour = startH + ((d * resources.length + ri) % numOneHourStarts)
      const startIso = localCivilToUtcIso(y, mo, day, hour, 0)
      const endIso = localCivilToUtcIso(y, mo, day, hour + 1, 0)

      const u = users[rows.length % users.length]
      rows.push({
        id: autoUuid(seq),
        userEmail: u.email,
        resourceName: res.name,
        startAt: startIso,
        endAt: endIso,
        _resolvedUserId: u.id,
        _resolvedResourceId: res.id,
      })
      seq++
      if (rows.length >= maxRows) break outer
    }
  }

  console.log(
    `[auto] ${users.length} usuario(s), ${resources.length} recurso(s), ${rows.length} reserva(s) generadas (anchor ${firstDate}, +${days} días, tope ${maxRows}).`,
  )
  return rows
}

async function runRows(supabase, rows, dryRun) {
  const emails = [...new Set(rows.map((r) => String(r.userEmail).trim().toLowerCase()))]
  const names = [...new Set(rows.map((r) => String(r.resourceName).trim()))]

  const userByEmail = new Map()
  const resourceByName = new Map()

  if (rows.length && rows[0]._resolvedUserId != null) {
    for (const r of rows) {
      const k = String(r.userEmail).trim().toLowerCase()
      userByEmail.set(k, r._resolvedUserId)
      resourceByName.set(String(r.resourceName).trim(), r._resolvedResourceId)
    }
  } else {
    const orEmails = emails.map((e) => `email.ilike.${e}`).join(",")
    const { data: users, error: usersError } = await supabase.from("users").select("id,email").or(orEmails)

    if (usersError) {
      throw new Error(`users: ${usersError.message}`)
    }

    const { data: resources, error: resError } = await supabase
      .from("resources")
      .select("id,name")
      .in("name", names)
      .or("is_active.is.null,is_active.eq.true")

    if (resError) {
      throw new Error(`resources: ${resError.message}`)
    }

    for (const u of users ?? []) {
      userByEmail.set(String(u.email).trim().toLowerCase(), u.id)
    }
    for (const r of resources ?? []) {
      resourceByName.set(String(r.name).trim(), r.id)
    }
  }

  let inserted = 0
  let skippedConflict = 0
  let skippedOverlap = 0
  let skippedMissing = 0

  for (let i = 0; i < rows.length; i++) {
    const row = rows[i]
    const emailKey = String(row.userEmail).trim().toLowerCase()
    const userId = userByEmail.get(emailKey)
    const resourceId = resourceByName.get(String(row.resourceName).trim())

    if (!userId) {
      console.warn(`[omitir] fila ${i + 1}: sin usuario con email (ilike) ${row.userEmail}`)
      skippedMissing++
      continue
    }
    if (!resourceId) {
      console.warn(`[omitir] fila ${i + 1}: sin recurso activo con name = ${row.resourceName}`)
      skippedMissing++
      continue
    }

    const startIso = new Date(row.startAt).toISOString()
    const endIso = new Date(row.endAt).toISOString()

    const { data: conflicts, error: cErr } = await supabase
      .from("reservations")
      .select("id")
      .eq("resource_id", resourceId)
      .in("status", ["confirmed", "checked_in"])
      .lt("start_at", endIso)
      .gt("end_at", startIso)

    if (cErr) {
      throw new Error(`conflict check: ${cErr.message}`)
    }
    if (conflicts?.length) {
      const isOnlySelf = conflicts.length === 1 && conflicts[0].id === row.id
      if (!isOnlySelf) {
        console.warn(`[omitir] fila ${i + 1}: solape con otra reserva activa en el mismo recurso`)
        skippedOverlap++
        continue
      }
    }

    const payload = {
      id: row.id,
      resource_id: resourceId,
      user_id: userId,
      start_at: startIso,
      end_at: endIso,
      status: "confirmed",
      notes: NOTES,
      reconfirmation_status: "pending",
      reconfirmed_at: null,
      reminder_sent_at: null,
    }

    if (dryRun) {
      console.log("[dry-run]", JSON.stringify(payload))
      continue
    }

    const { error: insErr } = await supabase.from("reservations").insert(payload)
    if (insErr) {
      if (insErr.code === "23505") {
        skippedConflict++
        continue
      }
      throw new Error(`insert: ${insErr.message}`)
    }
    inserted++
  }

  console.log(
    dryRun
      ? `Dry-run OK (${rows.length} filas procesadas, sin insertar).`
      : `Listo. insertadas=${inserted}, ya existían (PK)=${skippedConflict}, solape=${skippedOverlap}, sin usuario/recurso=${skippedMissing}`,
  )
}

async function main() {
  if (!url || !serviceKey) {
    console.error("Faltan NEXT_PUBLIC_SUPABASE_URL o SUPABASE_SERVICE_ROLE_KEY en el entorno.")
    process.exit(1)
  }

  const supabase = createClient(url, serviceKey, {
    auth: { persistSession: false, autoRefreshToken: false },
  })

  let rows
  if (autoMode) {
    rows = await buildAutoRows(supabase)
  } else {
    rows = loadReservationsFromJson()
    rows.forEach(validateRow)
  }

  await runRows(supabase, rows, dryRun)
}

main().catch((err) => {
  console.error(err instanceof Error ? err.message : err)
  process.exit(1)
})
