defmodule ToweropsWeb.MikrotikBackupLive.Compare do
  @moduledoc false
  use ToweropsWeb, :live_view

  alias Towerops.Devices
  alias Towerops.Devices.MikrotikBackups
  alias Towerops.Devices.MikrotikBackups.Differ
  alias Towerops.Repo

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

  @impl true
  def handle_params(%{"device_id" => device_id, "backup_a" => backup_a_id, "backup_b" => backup_b_id}, _uri, socket) do
    organization = socket.assigns.current_scope.organization

    # Verify device access
    device = Devices.get_device!(device_id)
    device_with_site = Repo.preload(device, site: :organization)

    if device_with_site.site.organization_id == organization.id do
      load_and_compare_backups(socket, device, device_id, backup_a_id, backup_b_id)
    else
      {:noreply,
       socket
       |> put_flash(:error, "You don't have access to this device")
       |> push_navigate(to: ~p"/devices")}
    end
  end

  @impl true
  def handle_params(_params, _uri, socket) do
    {:noreply,
     socket
     |> put_flash(:error, "Missing required parameters")
     |> push_navigate(to: ~p"/devices")}
  end

  defp load_and_compare_backups(socket, device, device_id, backup_a_id, backup_b_id) do
    # Load backups
    backup_a = MikrotikBackups.get_backup!(backup_a_id)
    backup_b = MikrotikBackups.get_backup!(backup_b_id)

    # Verify backups belong to this device
    if backup_a.device_id != device_id || backup_b.device_id != device_id do
      {:noreply,
       socket
       |> put_flash(:error, "Backups do not belong to this device")
       |> push_navigate(to: ~p"/devices/#{device_id}?tab=backups")}
    else
      generate_and_display_diff(socket, device, backup_a, backup_b, device_id)
    end
  end

  defp generate_and_display_diff(socket, device, backup_a, backup_b, device_id) do
    # Decompress configs
    config_a = MikrotikBackups.decompress_config(backup_a.config_compressed)
    config_b = MikrotikBackups.decompress_config(backup_b.config_compressed)

    # Mask sensitive data
    config_a_masked = Differ.mask_sensitive_data(config_a)
    config_b_masked = Differ.mask_sensitive_data(config_b)

    # Generate diff
    case Differ.generate_unified_diff(config_a_masked, config_b_masked) do
      {:ok, diff_output} ->
        stats = Differ.calculate_diff_stats(diff_output)

        {:noreply,
         socket
         |> assign(:page_title, "Compare Backups - #{device.name}")
         |> assign(:device, device)
         |> assign(:backup_a, backup_a)
         |> assign(:backup_b, backup_b)
         |> assign(:diff_output, diff_output)
         |> assign(:stats, stats)}

      {:error, reason} ->
        {:noreply,
         socket
         |> put_flash(:error, "Failed to generate diff: #{reason}")
         |> push_navigate(to: ~p"/devices/#{device_id}?tab=backups")}
    end
  end

  @impl true
  def handle_event("download_config", %{"backup" => "a"}, socket) do
    backup = socket.assigns.backup_a
    config_text = MikrotikBackups.decompress_config(backup.config_compressed)
    device = socket.assigns.device

    filename = "#{device.name}_backup_#{Calendar.strftime(backup.backed_up_at, "%Y%m%d_%H%M%S")}.rsc"

    {:noreply, push_event(socket, "download", %{content: config_text, filename: filename, mime_type: "text/plain"})}
  end

  @impl true
  def handle_event("download_config", %{"backup" => "b"}, socket) do
    backup = socket.assigns.backup_b
    config_text = MikrotikBackups.decompress_config(backup.config_compressed)
    device = socket.assigns.device

    filename = "#{device.name}_backup_#{Calendar.strftime(backup.backed_up_at, "%Y%m%d_%H%M%S")}.rsc"

    {:noreply, push_event(socket, "download", %{content: config_text, filename: filename, mime_type: "text/plain"})}
  end

  # Helper function to format byte sizes
  defp format_bytes(bytes) when is_integer(bytes) and bytes >= 0 do
    cond do
      bytes >= 1_073_741_824 -> "#{Float.round(bytes / 1_073_741_824, 1)} GB"
      bytes >= 1_048_576 -> "#{Float.round(bytes / 1_048_576, 1)} MB"
      bytes >= 1_024 -> "#{Float.round(bytes / 1_024, 1)} KB"
      true -> "#{bytes} B"
    end
  end

  defp format_bytes(_), do: "Invalid byte count"
end
