defmodule ToweropsWeb.SiteLive.Show do
  @moduledoc false
  use ToweropsWeb, :live_view

  alias Towerops.Alerts
  alias Towerops.ConfigChanges
  alias Towerops.Dashboard
  alias Towerops.Devices
  alias Towerops.Monitoring
  alias Towerops.Preseem
  alias Towerops.Sites
  alias Towerops.Snmp
  alias Towerops.Workers.DiscoveryWorker

  @impl true
  def mount(_params, _session, socket) do
    {:ok, socket}
  end

  @impl true
  def handle_params(%{"id" => id}, _, socket) do
    organization = socket.assigns.current_scope.organization
    site = Sites.get_organization_site!(organization.id, id)
    devices = Devices.list_site_devices(site.id)
    device_ids = Enum.map(devices, & &1.id)
    latency_chart_data = load_site_latency_chart_data(devices)
    site_summary = Dashboard.get_site_summary(site.id)

    # QoE summary from Preseem
    qoe_summary = Preseem.get_site_qoe_summary(site.id)

    # Config changes in last 30 days
    thirty_days_ago = DateTime.add(DateTime.utc_now(), -30, :day)
    config_changes = ConfigChanges.list_site_changes(site.id, after: thirty_days_ago, limit: 10)

    # Active alerts
    active_alerts = Alerts.list_site_active_alerts(site.id)

    # Insights
    insights = Preseem.Insights.list_site_insights(site.id, limit: 10)

    # Device response times for health grid
    response_times = Monitoring.get_device_latest_response_times(device_ids)

    {:noreply,
     socket
     |> assign(:page_title, site.name)
     |> assign(:site, site)
     |> assign(:device, devices)
     |> assign(:latency_chart_data, latency_chart_data)
     |> assign(:site_summary, site_summary)
     |> assign(:qoe_summary, qoe_summary)
     |> assign(:config_changes, config_changes)
     |> assign(:active_alerts, active_alerts)
     |> assign(:insights, insights)
     |> assign(:response_times, response_times)}
  end

  @impl true
  def handle_event("force_rediscover_all", _params, socket) do
    devices = socket.assigns.device
    snmp_devices = Enum.filter(devices, & &1.snmp_enabled)

    if Enum.empty?(snmp_devices) do
      {:noreply, put_flash(socket, :error, t_equipment("No SNMP-enabled devices at this site"))}
    else
      # Enqueue discovery for all SNMP-enabled devices
      Enum.each(snmp_devices, fn device ->
        enqueue_discovery(device.id)
      end)

      count = length(snmp_devices)

      message =
        t_equipment("Discovery started for %{count} device", count: count) <>
          if(count == 1, do: "", else: "s")

      {:noreply, put_flash(socket, :info, message)}
    end
  end

  defp load_site_latency_chart_data(devices) do
    if Enum.empty?(devices) do
      nil
    else
      twenty_four_hours_ago = DateTime.add(DateTime.utc_now(), -24, :hour)

      datasets =
        devices
        |> Enum.map(fn device ->
          checks =
            device.id
            |> Monitoring.get_latency_data(since: twenty_four_hours_ago, limit: 1000)
            |> Enum.reverse()

          %{
            label: device.name,
            data: Enum.map(checks, &latency_check_to_data_point/1)
          }
        end)
        |> Enum.reject(fn dataset -> Enum.empty?(dataset.data) end)

      if Enum.empty?(datasets) do
        nil
      else
        Jason.encode!(%{datasets: datasets})
      end
    end
  end

  defp latency_check_to_data_point(check) do
    %{
      x: DateTime.to_unix(check.checked_at, :millisecond),
      y: check.response_time_ms
    }
  end

  defp format_mrr(nil), do: "$0"

  defp format_mrr(%Decimal{} = amount) do
    "$#{format_number(Decimal.to_integer(Decimal.round(amount, 0)))}"
  end

  defp format_mrr(amount) when is_number(amount), do: "$#{format_number(round(amount))}"
  defp format_mrr(_), do: "$0"

  defp format_number(number) when is_integer(number) do
    number
    |> Integer.to_string()
    |> String.graphemes()
    |> Enum.reverse()
    |> Enum.chunk_every(3)
    |> Enum.join(",")
    |> String.reverse()
  end

  defp format_number(number), do: to_string(number)

  defp qoe_color(nil), do: "text-gray-400 dark:text-gray-500"
  defp qoe_color(score) when score >= 8.0, do: "text-green-600 dark:text-green-400"
  defp qoe_color(score) when score >= 6.0, do: "text-yellow-600 dark:text-yellow-400"
  defp qoe_color(_), do: "text-red-600 dark:text-red-400"

  defp qoe_bg(nil), do: "bg-gray-50 border-gray-200 dark:bg-gray-800/50 dark:border-white/10"
  defp qoe_bg(score) when score >= 8.0, do: "bg-green-50 border-green-200 dark:bg-green-900/20 dark:border-green-800/30"
  defp qoe_bg(score) when score >= 6.0, do: "bg-yellow-50 border-yellow-200 dark:bg-yellow-900/20 dark:border-yellow-800/30"
  defp qoe_bg(_), do: "bg-red-50 border-red-200 dark:bg-red-900/20 dark:border-red-800/30"

  defp capacity_bar_color(nil), do: "bg-gray-300"
  defp capacity_bar_color(score) when score >= 70, do: "bg-green-500"
  defp capacity_bar_color(score) when score >= 40, do: "bg-yellow-500"
  defp capacity_bar_color(_), do: "bg-red-500"

  defp status_dot_class(:up), do: "bg-green-500"
  defp status_dot_class(:down), do: "bg-red-500"
  defp status_dot_class(_), do: "bg-gray-400"

  defp time_ago(nil), do: "—"

  defp time_ago(datetime) do
    seconds = DateTime.diff(DateTime.utc_now(), datetime, :second)

    cond do
      seconds < 60 -> "#{seconds}s ago"
      seconds < 3600 -> "#{div(seconds, 60)}m ago"
      seconds < 86400 -> "#{div(seconds, 3600)}h ago"
      true -> "#{div(seconds, 86400)}d ago"
    end
  end

  defp format_response_time(nil), do: "—"
  defp format_response_time(ms) when ms < 1, do: "<1ms"
  defp format_response_time(ms), do: "#{round(ms)}ms"

  defp change_size_label(size) when size < 100, do: "Small"
  defp change_size_label(size) when size < 500, do: "Medium"
  defp change_size_label(_), do: "Large"

  defp change_size_color(size) when size < 100, do: "badge-ghost"
  defp change_size_color(size) when size < 500, do: "badge-warning"
  defp change_size_color(_), do: "badge-error"

  defp insight_urgency_class("critical"), do: "text-red-600 dark:text-red-400"
  defp insight_urgency_class("warning"), do: "text-yellow-600 dark:text-yellow-400"
  defp insight_urgency_class(_), do: "text-blue-600 dark:text-blue-400"

  defp insight_urgency_icon("critical"), do: "hero-exclamation-triangle"
  defp insight_urgency_icon("warning"), do: "hero-exclamation-circle"
  defp insight_urgency_icon(_), do: "hero-information-circle"

  # Enqueue discovery job - safe to call in test environment
  defp enqueue_discovery(device_id) do
    if Application.get_env(:towerops, :env) == :test do
      # In test, run synchronously
      _ = Task.start(fn -> Snmp.discover_device(Devices.get_device!(device_id)) end)
    else
      # In dev/prod, enqueue to Oban
      DiscoveryWorker.enqueue(device_id)
    end
  end
end
