defmodule Towerops.Gaiia.ImpactAnalysis do
  @moduledoc """
  Calculates subscriber impact when a Towerops device goes down.

  Three layers of impact analysis (broadest to most precise):
  1. Site-level: Device → Site → Gaiia Network Site → all subscribers at that site
  2. Device-level: Device → Gaiia Inventory Item → directly assigned subscriber
  """

  alias Towerops.Devices
  alias Towerops.Gaiia

  @doc """
  Analyze the subscriber impact of a device going down.

  Returns a map with:
  - `:device_level` - direct subscriber impact (from inventory item assignment)
  - `:site_level` - broader site-level impact (from network site mapping)
  - `:total_subscribers` - broadest subscriber count (site > device)
  - `:total_mrr` - broadest MRR estimate
  - `:analyzed_at` - timestamp of analysis
  """
  @spec analyze_device_impact(String.t(), String.t()) :: map()
  def analyze_device_impact(organization_id, device_id) do
    device = Devices.get_device(device_id)
    device_level = analyze_device_level(organization_id, device_id)
    site_level = analyze_site_level(organization_id, device)

    {total_subscribers, total_mrr} = compute_totals(device_level, site_level)

    %{
      device_level: device_level,
      site_level: site_level,
      total_subscribers: total_subscribers,
      total_mrr: total_mrr,
      analyzed_at: DateTime.truncate(DateTime.utc_now(), :second)
    }
  end

  @doc """
  Convert impact analysis result to a JSON-serializable map for storage.
  """
  @spec to_json(map()) :: map()
  def to_json(impact) do
    %{
      "device_level" => encode_level(impact.device_level),
      "site_level" => encode_level(impact.site_level),
      "total_subscribers" => impact.total_subscribers,
      "total_mrr" => to_string(impact.total_mrr),
      "analyzed_at" => DateTime.to_iso8601(impact.analyzed_at)
    }
  end

  defp analyze_device_level(organization_id, device_id) do
    case Gaiia.get_inventory_item_for_device(device_id) do
      nil ->
        nil

      item ->
        {subscribers, mrr} = get_item_subscriber_impact(organization_id, item)

        if subscribers > 0 do
          %{
            subscriber_count: subscribers,
            mrr: mrr,
            inventory_item_name: item.name,
            confidence: :device_level
          }
        end
    end
  end

  defp analyze_site_level(_organization_id, nil), do: nil

  defp analyze_site_level(_organization_id, %{site_id: nil}), do: nil

  defp analyze_site_level(_organization_id, device) do
    case Gaiia.get_network_site_for_site(device.site_id) do
      nil ->
        nil

      network_site ->
        count = network_site.account_count || 0
        mrr = network_site.total_mrr || Decimal.new("0")

        %{
          subscriber_count: count,
          mrr: mrr,
          site_name: network_site.name,
          confidence: :site_level
        }
    end
  end

  defp get_item_subscriber_impact(organization_id, item) do
    case item.assigned_account_gaiia_id do
      nil ->
        {0, Decimal.new("0")}

      account_gaiia_id ->
        case Gaiia.get_account(organization_id, account_gaiia_id) do
          nil -> {1, Decimal.new("0")}
          account -> {1, account.mrr || Decimal.new("0")}
        end
    end
  end

  defp compute_totals(nil, nil), do: {0, Decimal.new("0")}
  defp compute_totals(nil, site), do: {site.subscriber_count, site.mrr}
  defp compute_totals(device, nil), do: {device.subscriber_count, device.mrr}

  defp compute_totals(_device, site) do
    # Site-level is the broadest impact estimate
    {site.subscriber_count, site.mrr}
  end

  defp encode_level(nil), do: nil

  defp encode_level(level) do
    level
    |> maybe_from_struct()
    |> Map.update(:mrr, "0", &to_string/1)
    |> Map.update(:confidence, nil, &to_string/1)
  end

  defp maybe_from_struct(%_{} = struct), do: Map.from_struct(struct)
  defp maybe_from_struct(map) when is_map(map), do: map
end
