defmodule ToweropsWeb.Org.IntegrationsLive do
  @moduledoc false
  use ToweropsWeb, :live_view

  alias Towerops.Gaiia.Client, as: GaiiaClient
  alias Towerops.Integrations
  alias Towerops.Integrations.Integration
  alias Towerops.Preseem.Client, as: PreseemClient

  require Logger

  @providers [
    %{
      id: "preseem",
      name: "Preseem",
      description: "QoE monitoring and subscriber experience analytics for wireless ISPs.",
      icon: "hero-signal"
    },
    %{
      id: "gaiia",
      name: "Gaiia",
      description:
        "Billing and subscriber management. Enables outage impact analysis, inventory reconciliation, and subscriber-aware monitoring.",
      icon: "hero-user-group"
    }
  ]

  @impl true
  def mount(_params, _session, socket) do
    organization = socket.assigns.current_scope.organization
    integrations = load_integrations(organization.id)

    {:ok,
     socket
     |> assign(:organization, organization)
     |> assign(:timezone, socket.assigns.current_scope.timezone)
     |> assign(:providers, @providers)
     |> assign(:integrations, integrations)
     |> assign(:configuring, nil)
     |> assign(:form, nil)
     |> assign(:test_result, nil)}
  end

  @impl true
  def handle_event("configure", %{"provider" => provider}, socket) do
    integration = Map.get(socket.assigns.integrations, provider)

    form =
      case integration do
        nil ->
          %Integration{}
          |> Integrations.change_integration(%{provider: provider})
          |> to_form()

        existing ->
          existing
          |> Integrations.change_integration(%{})
          |> to_form()
      end

    {:noreply,
     socket
     |> assign(:configuring, provider)
     |> assign(:form, form)
     |> assign(:test_result, nil)}
  end

  @impl true
  def handle_event("close_config", _params, socket) do
    {:noreply,
     socket
     |> assign(:configuring, nil)
     |> assign(:form, nil)
     |> assign(:test_result, nil)}
  end

  @impl true
  def handle_event("validate", %{"integration" => params}, socket) do
    integration = get_current_integration(socket)

    changeset =
      integration
      |> Integrations.change_integration(normalize_params(params, integration))
      |> Map.put(:action, :validate)

    {:noreply, assign(socket, :form, to_form(changeset))}
  end

  @impl true
  def handle_event("save", %{"integration" => params}, socket) do
    organization = socket.assigns.organization
    provider = socket.assigns.configuring
    existing = Map.get(socket.assigns.integrations, provider)
    attrs = normalize_params(params, existing)

    result =
      case existing do
        nil ->
          Integrations.create_integration(
            organization.id,
            attrs |> Map.put(:provider, provider) |> Map.put(:enabled, true)
          )

        integration ->
          Integrations.update_integration(integration, attrs)
      end

    case result do
      {:ok, _integration} ->
        integrations = load_integrations(organization.id)

        {:noreply,
         socket
         |> assign(:integrations, integrations)
         |> assign(:configuring, nil)
         |> assign(:form, nil)
         |> assign(:test_result, nil)
         |> put_flash(:info, "Integration saved successfully")}

      {:error, changeset} ->
        {:noreply, assign(socket, :form, to_form(changeset))}
    end
  end

  @impl true
  def handle_event("test_connection", _params, socket) do
    api_key = extract_api_key(socket)
    provider = socket.assigns.configuring

    if api_key == "" or is_nil(api_key) do
      {:noreply, assign(socket, :test_result, {:error, "Please enter an API key first"})}
    else
      result = test_provider_connection(provider, api_key)
      {:noreply, assign(socket, :test_result, format_connection_result(result))}
    end
  end

  @impl true
  def handle_event("regenerate_webhook_secret", _params, socket) do
    case Map.get(socket.assigns.integrations, "gaiia") do
      nil ->
        {:noreply, socket}

      integration ->
        new_secret = generate_webhook_secret()

        updated_credentials =
          Map.put(integration.credentials, "webhook_secret", new_secret)

        case Integrations.update_integration(integration, %{credentials: updated_credentials}) do
          {:ok, _updated} ->
            integrations = load_integrations(socket.assigns.organization.id)

            {:noreply,
             socket
             |> assign(:integrations, integrations)
             |> put_flash(:info, "Webhook secret regenerated")}

          {:error, _changeset} ->
            {:noreply, put_flash(socket, :error, "Failed to regenerate webhook secret")}
        end
    end
  end

  @impl true
  def handle_event("toggle_enabled", %{"provider" => provider}, socket) do
    case Map.get(socket.assigns.integrations, provider) do
      nil ->
        {:noreply, socket}

      integration ->
        case Integrations.update_integration(integration, %{enabled: !integration.enabled}) do
          {:ok, _updated} ->
            integrations = load_integrations(socket.assigns.organization.id)

            {:noreply,
             socket
             |> assign(:integrations, integrations)
             |> put_flash(:info, "Integration updated")}

          {:error, _changeset} ->
            {:noreply, put_flash(socket, :error, "Failed to update integration")}
        end
    end
  end

  defp test_provider_connection("preseem", api_key), do: PreseemClient.test_connection(api_key)
  defp test_provider_connection("gaiia", api_key), do: GaiiaClient.test_connection(api_key)
  defp test_provider_connection(_, _api_key), do: {:error, "Unknown provider"}

  defp load_integrations(organization_id) do
    organization_id
    |> Integrations.list_integrations()
    |> Map.new(fn integration -> {integration.provider, integration} end)
  end

  defp get_current_integration(socket) do
    provider = socket.assigns.configuring

    case Map.get(socket.assigns.integrations, provider) do
      nil -> %Integration{}
      integration -> integration
    end
  end

  defp next_sync_minutes(nil, _interval), do: nil

  defp next_sync_minutes(last_synced_at, interval) do
    next_sync = DateTime.add(last_synced_at, interval * 60, :second)
    diff = DateTime.diff(next_sync, DateTime.utc_now(), :second)
    max(0, div(diff, 60))
  end

  defp normalize_params(params, existing_integration) do
    api_key = Map.get(params, "api_key", "")
    sync_interval = Map.get(params, "sync_interval_minutes")

    webhook_secret =
      case existing_integration do
        %Integration{credentials: %{"webhook_secret" => secret}} when secret != "" ->
          secret

        _ ->
          generate_webhook_secret()
      end

    attrs = %{credentials: %{"api_key" => api_key, "webhook_secret" => webhook_secret}}

    if sync_interval && sync_interval != "" do
      Map.put(attrs, :sync_interval_minutes, sync_interval)
    else
      attrs
    end
  end

  defp generate_webhook_secret do
    32 |> :crypto.strong_rand_bytes() |> Base.encode16(case: :lower)
  end

  defp webhook_url(organization_id) do
    ToweropsWeb.Endpoint.url() <> "/api/v1/webhooks/gaiia/#{organization_id}"
  end

  defp get_credential(nil, _key), do: ""

  defp get_credential(%Integration{credentials: credentials}, key) when is_map(credentials) do
    Map.get(credentials, key, "")
  end

  defp get_credential(_integration, _key), do: ""

  defp extract_api_key(socket) do
    changeset = socket.assigns.form.source
    data = Ecto.Changeset.apply_changes(changeset)

    case data.credentials do
      %{"api_key" => key} -> key
      _ -> nil
    end
  end

  defp format_connection_result({:ok, _body}), do: {:ok, "Connection successful"}
  defp format_connection_result({:error, :unauthorized}), do: {:error, "Invalid API key"}
  defp format_connection_result({:error, :forbidden}), do: {:error, "Access forbidden"}

  defp format_connection_result({:error, {:unexpected_status, status}}),
    do: {:error, "API returned unexpected status: #{status}"}

  defp format_connection_result({:error, %{reason: :timeout}}), do: {:error, "Connection timeout - unable to reach API"}

  defp format_connection_result({:error, %{reason: :econnrefused}}),
    do: {:error, "Connection refused - check API endpoint"}

  defp format_connection_result({:error, %{reason: :nxdomain}}), do: {:error, "DNS lookup failed - check API endpoint"}

  defp format_connection_result({:error, {:graphql_errors, errors}}),
    do: {:error, "API query failed: #{inspect(Enum.map(errors, & &1["message"]))}"}

  # Never expose internal error details to users
  defp format_connection_result({:error, reason}) do
    Logger.warning("Integration connection test failed: #{inspect(reason)}")
    {:error, "Connection failed - please check your API credentials and try again"}
  end
end
