defmodule Microwaveprop.Propagation.GridTaskEnqueuer do
  @moduledoc """
  Seeds `grid_tasks` rows for the Rust `prop-grid-rs` worker.

  Called from `PropagationGridWorker.seed_chain/0`. Rust claims
  kind='forecast' and kind='analysis' rows, with analysis lanes
  taking priority (see `claim_next_analysis` in the Rust db module).

  Inserts are idempotent via the `(run_time, forecast_hour, kind)`
  unique index — re-seeding the same cycle is a no-op.
  """

  alias Microwaveprop.Repo

  require Logger

  @max_forecast_hour 18

  @doc """
  Seed one kind='analysis' row (f00) plus 18 kind='forecast' rows
  (f01..f18) for `run_time`. This is the Phase 3 Stream A cutover
  shape: Rust owns the entire chain end-to-end.
  """
  @spec seed_with_analysis(DateTime.t()) :: {:ok, non_neg_integer()} | {:error, term()}
  def seed_with_analysis(%DateTime{} = run_time) do
    run_time = DateTime.truncate(run_time, :second)
    now = DateTime.truncate(DateTime.utc_now(), :microsecond)

    analysis_row = %{
      id: Ecto.UUID.bingenerate(),
      run_time: run_time,
      forecast_hour: 0,
      valid_time: run_time,
      status: "queued",
      attempt: 0,
      kind: "analysis",
      claimed_at: nil,
      completed_at: nil,
      error: nil,
      inserted_at: now,
      updated_at: now
    }

    forecast_rows =
      for fh <- 1..@max_forecast_hour do
        %{
          id: Ecto.UUID.bingenerate(),
          run_time: run_time,
          forecast_hour: fh,
          valid_time: DateTime.add(run_time, fh * 3600, :second),
          status: "queued",
          attempt: 0,
          kind: "forecast",
          claimed_at: nil,
          completed_at: nil,
          error: nil,
          inserted_at: now,
          updated_at: now
        }
      end

    do_insert([analysis_row | forecast_rows], run_time)
  end

  @doc """
  Legacy: seed only kind='forecast' rows (f01..f18). Retained for
  tooling that needs to re-seed the forecast lane without touching
  the analysis row. `seed_with_analysis/1` is the production path.
  """
  @spec seed(DateTime.t()) :: {:ok, non_neg_integer()} | {:error, term()}
  def seed(%DateTime{} = run_time) do
    run_time = DateTime.truncate(run_time, :second)
    now = DateTime.truncate(DateTime.utc_now(), :microsecond)

    rows =
      for fh <- 1..@max_forecast_hour do
        %{
          id: Ecto.UUID.bingenerate(),
          run_time: run_time,
          forecast_hour: fh,
          valid_time: DateTime.add(run_time, fh * 3600, :second),
          status: "queued",
          attempt: 0,
          kind: "forecast",
          claimed_at: nil,
          completed_at: nil,
          error: nil,
          inserted_at: now,
          updated_at: now
        }
      end

    do_insert(rows, run_time)
  end

  defp do_insert(rows, run_time) do
    {count, _} =
      Repo.insert_all("grid_tasks", rows,
        on_conflict: :nothing,
        conflict_target: [:run_time, :forecast_hour, :kind]
      )

    if count > 0 do
      Logger.info("GridTaskEnqueuer: seeded #{count} grid_tasks for run_time=#{run_time}")
    else
      Logger.info("GridTaskEnqueuer: run_time=#{run_time} already seeded")
    end

    {:ok, count}
  rescue
    e ->
      Logger.error("GridTaskEnqueuer: seed failed: #{inspect(e)}")
      {:error, e}
  end
end
