defmodule Mix.Tasks.HrrrNativeDeriveFields do
  @shortdoc "Compute derived turbulence fields on hrrr_native_profiles"
  @moduledoc """
  Walks all `hrrr_native_profiles` rows that are missing derived
  fields (bulk_richardson is nil) and populates them from the raw
  level arrays using the Inversion and ThetaE modules.

  Idempotent: rows that already have derived fields are skipped.

      mix hrrr_native_derive_fields           # process all pending
      mix hrrr_native_derive_fields --limit 100
  """
  use Mix.Task

  import Ecto.Query

  alias Microwaveprop.Propagation.Duct
  alias Microwaveprop.Propagation.Inversion
  alias Microwaveprop.Repo
  alias Microwaveprop.Weather.HrrrNativeProfile
  alias Microwaveprop.Weather.ThetaE

  @impl Mix.Task
  def run(argv) do
    Mix.Task.run("app.start")
    Oban.pause_all_queues(Oban)

    {opts, _, _} = OptionParser.parse(argv, switches: [limit: :integer])
    limit = Keyword.get(opts, :limit, 10_000)

    profiles =
      HrrrNativeProfile
      |> where([p], is_nil(p.bulk_richardson) and p.level_count > 2)
      |> limit(^limit)
      |> Repo.all()

    Mix.shell().info("Deriving fields for #{length(profiles)} profiles...")

    count =
      Enum.count(profiles, fn profile ->
        derived = derive(profile)

        {1, _} =
          HrrrNativeProfile
          |> where([p], p.id == ^profile.id)
          |> Repo.update_all(set: derived)

        true
      end)

    Mix.shell().info("Updated #{count} profiles with derived turbulence fields.")
  end

  defp derive(profile) do
    p = %{
      heights_m: profile.heights_m,
      temp_k: profile.temp_k,
      spfh: profile.spfh,
      pressure_pa: profile.pressure_pa,
      u_wind_ms: profile.u_wind_ms,
      v_wind_ms: profile.v_wind_ms,
      tke_m2s2: profile.tke_m2s2,
      level_count: profile.level_count
    }

    # Phase 2: turbulence features
    inversion_fields =
      case Inversion.find_inversion_top(p) do
        {:ok, %{height_m: top_h, level_idx: top_idx, base_idx: base_idx}} ->
          ri = Inversion.bulk_richardson(p, base_idx, top_idx)
          shear = Inversion.shear_magnitude(p, base_idx, top_idx)
          theta_e_jump = ThetaE.theta_e_jump(p, base_idx, top_idx)

          [
            inversion_top_m: top_h,
            bulk_richardson: ri,
            shear_at_top_ms: shear,
            theta_e_jump_k: theta_e_jump
          ]

        :none ->
          [
            inversion_top_m: nil,
            bulk_richardson: nil,
            shear_at_top_ms: nil,
            theta_e_jump_k: nil
          ]
      end

    # Phase 4: duct geometry
    duct_result = Duct.analyze(p)

    duct_fields = [
      ducts: duct_result.ducts,
      best_duct_band_ghz: duct_result.best_duct_band_ghz
    ]

    inversion_fields ++ duct_fields
  end
end
