defmodule ToweropsWeb.ConfigTimelineLive do
  @moduledoc """
  Timeline visualization showing config change events overlaid with
  QoE metrics and check results for a device.
  """
  use ToweropsWeb, :live_view

  alias Towerops.ConfigChanges
  alias Towerops.Devices
  alias Towerops.Preseem.AccessPoint
  alias Towerops.Preseem.SubscriberMetric
  alias Towerops.Repo
  alias ToweropsWeb.Live.Helpers.AccessControl

  import Ecto.Query

  @ranges %{
    "24h" => 24,
    "7d" => 24 * 7,
    "30d" => 24 * 30
  }

  @impl true
  def mount(%{"device_id" => device_id}, _session, socket) do
    organization = socket.assigns.current_scope.organization

    case AccessControl.verify_device_access(device_id, organization.id) do
      {:ok, _} ->
        device = Devices.get_device!(device_id)
        {:ok, assign(socket, device: device, page_title: "Config Timeline — #{device.name}")}

      {:error, _} ->
        {:ok,
         socket
         |> put_flash(:error, "Device not found")
         |> redirect(to: ~p"/devices")}
    end
  end

  @impl true
  def handle_params(params, _url, socket) do
    range = Map.get(params, "range", "7d")
    hours = Map.get(@ranges, range, 24 * 7)
    since = DateTime.add(DateTime.utc_now(), -hours * 3600, :second)

    device = socket.assigns.device

    change_events = ConfigChanges.list_device_changes(device.id, after: since, limit: 100)
    qoe_data = load_qoe_data(device.id, since)
    check_data = load_check_data(device.id, since)

    {:noreply,
     socket
     |> assign(:range, range)
     |> assign(:change_events, change_events)
     |> assign(:qoe_data, qoe_data)
     |> assign(:check_data, check_data)
     |> assign(:selected_event, nil)}
  end

  @impl true
  def handle_event("select_range", %{"range" => range}, socket) do
    {:noreply, push_patch(socket, to: ~p"/devices/#{socket.assigns.device.id}/config-timeline?range=#{range}")}
  end

  @impl true
  def handle_event("select_event", %{"id" => event_id}, socket) do
    event = ConfigChanges.get_change_event_with_preloads!(event_id)
    {:noreply, assign(socket, :selected_event, event)}
  end

  @impl true
  def handle_event("close_event", _, socket) do
    {:noreply, assign(socket, :selected_event, nil)}
  end

  @impl true
  def render(assigns) do
    ~H"""
    <div class="space-y-6">
      <div class="flex items-center justify-between">
        <div>
          <h1 class="text-2xl font-bold">Config Timeline</h1>
          <p class="text-base-content/60">
            <.link navigate={~p"/devices/#{@device.id}?tab=overview"} class="link link-hover">
              {@device.name}
            </.link>
            — Config changes vs performance metrics
          </p>
        </div>
        <div class="join">
          <button
            :for={r <- ["24h", "7d", "30d"]}
            class={"join-item btn btn-sm #{if @range == r, do: "btn-primary", else: "btn-ghost"}"}
            phx-click="select_range"
            phx-value-range={r}
          >
            {r}
          </button>
        </div>
      </div>

      <%!-- Timeline chart area --%>
      <div class="card bg-base-100 shadow-sm">
        <div class="card-body">
          <h2 class="card-title text-lg">Performance & Config Changes</h2>

          <%!-- QoE metrics chart placeholder — rendered via hook --%>
          <div
            id="config-timeline-chart"
            phx-hook="ConfigTimelineChart"
            data-qoe={Jason.encode!(@qoe_data)}
            data-changes={Jason.encode!(Enum.map(@change_events, &timeline_event/1))}
            data-checks={Jason.encode!(@check_data)}
            class="h-64 w-full"
          >
            <div class="flex items-center justify-center h-full text-base-content/40">
              Loading chart...
            </div>
          </div>
        </div>
      </div>

      <%!-- Change events list --%>
      <div class="card bg-base-100 shadow-sm">
        <div class="card-body">
          <h2 class="card-title text-lg">Config Changes ({length(@change_events)})</h2>

          <%= if Enum.empty?(@change_events) do %>
            <p class="text-base-content/50 py-4">No config changes in this period.</p>
          <% else %>
            <div class="overflow-x-auto">
              <table class="table table-sm">
                <thead>
                  <tr>
                    <th>When</th>
                    <th>Sections</th>
                    <th>Size</th>
                    <th></th>
                  </tr>
                </thead>
                <tbody>
                  <tr :for={event <- @change_events} class="hover:bg-base-200/50 cursor-pointer" phx-click="select_event" phx-value-id={event.id}>
                    <td class="whitespace-nowrap">
                      {Calendar.strftime(event.changed_at, "%b %d, %H:%M UTC")}
                    </td>
                    <td>
                      <span :for={section <- event.sections_changed} class="badge badge-sm badge-ghost mr-1">
                        {section}
                      </span>
                    </td>
                    <td>
                      <span class={"badge badge-sm #{change_size_class(event.change_size)}"}>
                        {event.change_size} lines
                      </span>
                    </td>
                    <td>
                      <.link
                        navigate={~p"/devices/#{@device.id}/backups/compare?before=#{event.backup_before_id}&after=#{event.backup_after_id}"}
                        class="btn btn-xs btn-ghost"
                      >
                        View Diff
                      </.link>
                    </td>
                  </tr>
                </tbody>
              </table>
            </div>
          <% end %>
        </div>
      </div>

      <%!-- Selected event detail modal --%>
      <%= if @selected_event do %>
        <div class="card bg-base-100 shadow-sm border border-primary/20">
          <div class="card-body">
            <div class="flex items-center justify-between">
              <h2 class="card-title text-lg">
                Change Details — {Calendar.strftime(@selected_event.changed_at, "%b %d, %H:%M UTC")}
              </h2>
              <button class="btn btn-sm btn-ghost" phx-click="close_event">✕</button>
            </div>

            <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mt-2">
              <div>
                <span class="text-sm font-medium text-base-content/60">Sections Changed</span>
                <div class="mt-1">
                  <span :for={s <- @selected_event.sections_changed} class="badge badge-sm badge-outline mr-1">
                    {s}
                  </span>
                  <span :if={Enum.empty?(@selected_event.sections_changed)} class="text-base-content/40">Unknown</span>
                </div>
              </div>
              <div>
                <span class="text-sm font-medium text-base-content/60">Lines Changed</span>
                <p class="font-mono">{@selected_event.change_size}</p>
              </div>
              <div>
                <.link
                  navigate={~p"/devices/#{@device.id}/backups/compare?before=#{@selected_event.backup_before_id}&after=#{@selected_event.backup_after_id}"}
                  class="btn btn-sm btn-primary"
                >
                  Full Diff View →
                </.link>
              </div>
            </div>

            <%= if @selected_event.diff_summary do %>
              <div class="mt-4">
                <span class="text-sm font-medium text-base-content/60">Diff Summary</span>
                <pre class="bg-base-200 p-3 rounded-lg mt-1 text-xs overflow-x-auto max-h-48 overflow-y-auto"><code>{@selected_event.diff_summary}</code></pre>
              </div>
            <% end %>
          </div>
        </div>
      <% end %>
    </div>
    """
  end

  # -- Data loaders --

  defp load_qoe_data(device_id, since) do
    ap_ids =
      AccessPoint
      |> where(device_id: ^device_id)
      |> select([ap], ap.id)
      |> Repo.all()

    if Enum.empty?(ap_ids) do
      []
    else
      SubscriberMetric
      |> where([m], m.preseem_access_point_id in ^ap_ids)
      |> where([m], m.recorded_at >= ^since)
      |> order_by(asc: :recorded_at)
      |> select([m], %{
        t: m.recorded_at,
        latency: m.avg_latency,
        throughput: m.avg_throughput,
        loss: m.avg_loss
      })
      |> Repo.all()
      |> Enum.map(fn m ->
        %{
          t: DateTime.to_iso8601(m.t),
          latency: m.latency,
          throughput: m.throughput,
          loss: m.loss
        }
      end)
    end
  end

  defp load_check_data(device_id, since) do
    check_ids =
      Towerops.Monitoring.Check
      |> where(device_id: ^device_id)
      |> select([c], c.id)
      |> Repo.all()

    if Enum.empty?(check_ids) do
      []
    else
      Towerops.Monitoring.CheckResult
      |> where([r], r.check_id in ^check_ids)
      |> where([r], r.checked_at >= ^since)
      |> order_by(asc: :checked_at)
      |> select([r], %{t: r.checked_at, status: r.status})
      |> limit(1000)
      |> Repo.all()
      |> Enum.map(fn r ->
        %{t: DateTime.to_iso8601(r.t), status: r.status}
      end)
    end
  end

  defp timeline_event(event) do
    %{
      id: event.id,
      t: DateTime.to_iso8601(event.changed_at),
      sections: event.sections_changed,
      size: event.change_size
    }
  end

  defp change_size_class(size) when size > 50, do: "badge-error"
  defp change_size_class(size) when size > 20, do: "badge-warning"
  defp change_size_class(_), do: "badge-info"
end
