defmodule Towerops.Preseem do
  @moduledoc """
  Context for querying Preseem integration data.
  """

  import Ecto.Query

  alias Towerops.Preseem.AccessPoint
  alias Towerops.Preseem.Insights
  alias Towerops.Preseem.SubscriberMetric
  alias Towerops.Repo

  @doc "Get the Preseem AP linked to a specific device, or nil."
  def get_access_point_for_device(device_id) do
    Repo.get_by(AccessPoint, device_id: device_id)
  end

  @doc "List all Preseem APs for an organization."
  def list_access_points(organization_id) do
    AccessPoint
    |> where(organization_id: ^organization_id)
    |> order_by(:name)
    |> Repo.all()
  end

  @doc "List unmatched or ambiguous Preseem APs for an organization."
  def list_unmatched_access_points(organization_id) do
    AccessPoint
    |> where(organization_id: ^organization_id)
    |> where([ap], ap.match_confidence in ["unmatched", "ambiguous"])
    |> order_by(:name)
    |> Repo.all()
  end

  @doc "List matched Preseem APs for an organization (everything except unmatched/ambiguous)."
  def list_matched_access_points(organization_id) do
    AccessPoint
    |> where(organization_id: ^organization_id)
    |> where([ap], ap.match_confidence not in ["unmatched", "ambiguous"])
    |> order_by(:name)
    |> Repo.all()
  end

  @doc "List recent subscriber metrics for a Preseem AP. Default limit 100."
  def list_subscriber_metrics(access_point_id, opts \\ []) do
    limit = Keyword.get(opts, :limit, 100)

    SubscriberMetric
    |> where(preseem_access_point_id: ^access_point_id)
    |> order_by(desc: :recorded_at)
    |> limit(^limit)
    |> Repo.all()
  end

  @doc "Link a Preseem AP to a device manually."
  def link_access_point(access_point_id, device_id) do
    case Repo.get(AccessPoint, access_point_id) do
      nil ->
        {:error, :not_found}

      ap ->
        ap
        |> AccessPoint.changeset(%{device_id: device_id, match_confidence: "manual"})
        |> Repo.update()
    end
  end

  @doc "Unlink a Preseem AP from its device."
  def unlink_access_point(access_point_id) do
    case Repo.get(AccessPoint, access_point_id) do
      nil ->
        {:error, :not_found}

      ap ->
        ap
        |> AccessPoint.changeset(%{device_id: nil, match_confidence: "unmatched"})
        |> Repo.update()
    end
  end

  @doc """
  Returns an aggregate QoE summary for all APs at a site.

  Returns a map with average QoE, latency, capacity info, and per-AP breakdowns.
  """
  def get_site_qoe_summary(site_id) do
    alias Towerops.Devices

    device_ids =
      site_id
      |> Devices.list_site_devices()
      |> Enum.map(& &1.id)

    if device_ids == [] do
      %{
        avg_qoe: nil,
        avg_capacity: nil,
        total_subscribers: 0,
        aps: [],
        aps_with_issues: 0,
        total_aps: 0
      }
    else
      aps =
        AccessPoint
        |> where([ap], ap.device_id in ^device_ids)
        |> Repo.all()

      qoe_scores = aps |> Enum.map(& &1.qoe_score) |> Enum.reject(&is_nil/1)
      capacity_scores = aps |> Enum.map(& &1.capacity_score) |> Enum.reject(&is_nil/1)

      avg_qoe =
        if qoe_scores != [],
          do: Float.round(Enum.sum(qoe_scores) / length(qoe_scores), 1),
          else: nil

      avg_capacity =
        if capacity_scores != [],
          do: Float.round(Enum.sum(capacity_scores) / length(capacity_scores), 1),
          else: nil

      total_subscribers =
        aps |> Enum.map(& &1.subscriber_count) |> Enum.reject(&is_nil/1) |> Enum.sum()

      # APs with issues: QoE < 6.0 (on 1-10 scale) or capacity < 50
      aps_with_issues =
        Enum.count(aps, fn ap ->
          (ap.qoe_score && ap.qoe_score < 6.0) ||
            (ap.capacity_score && ap.capacity_score < 50.0)
        end)

      %{
        avg_qoe: avg_qoe,
        avg_capacity: avg_capacity,
        total_subscribers: total_subscribers,
        aps: aps,
        aps_with_issues: aps_with_issues,
        total_aps: length(aps)
      }
    end
  end

  # --- Insight delegations ---

  defdelegate list_insights(organization_id, opts \\ []), to: Insights
  defdelegate list_device_insights(device_id), to: Insights
  defdelegate dismiss_insight(insight_id), to: Insights
  defdelegate dismiss_insights(insight_ids), to: Insights
end
