defmodule Towerops.Monitoring.Executors.HttpExecutor do
  @moduledoc """
  Executes HTTP/HTTPS checks using Req library.

  Config format:
  %{
    "url" => "https://example.com/api/health",
    "method" => "GET",  # optional, default: GET
    "expected_status" => 200,  # optional, default: 200
    "verify_ssl" => true,  # optional, default: true
    "headers" => %{"Authorization" => "Bearer ..."},  # optional
    "body" => "...",  # optional, for POST/PUT
    "regex" => "OK",  # optional, content match regex
    "follow_redirects" => true  # optional, default: true
  }
  """

  require Logger

  @default_timeout 5000

  @doc """
  Executes an HTTP check.

  Returns:
  - {:ok, response_time_ms, output} on success
  - {:error, reason} on failure
  """
  def execute(config, timeout_ms \\ @default_timeout) do
    start_time = System.monotonic_time(:millisecond)
    req_opts = build_req_opts(config, timeout_ms)

    case Req.request(req_opts) do
      {:ok, response} ->
        response_time = System.monotonic_time(:millisecond) - start_time
        validate_response(response, config, response_time)

      {:error, error} ->
        format_error(error, timeout_ms)
    end
  rescue
    e ->
      Logger.error("HTTP check exception: #{inspect(e)}")
      {:error, "Exception: #{Exception.message(e)}"}
  end

  defp build_req_opts(config, timeout_ms) do
    method = String.downcase(Map.get(config, "method", "GET"))
    verify_ssl = Map.get(config, "verify_ssl", true)

    opts = [
      method: String.to_atom(method),
      url: Map.fetch!(config, "url"),
      headers: Map.get(config, "headers", %{}),
      receive_timeout: timeout_ms,
      connect_options: [
        transport_opts: [
          verify: if(verify_ssl, do: :verify_peer, else: :verify_none)
        ]
      ],
      redirect: Map.get(config, "follow_redirects", true)
    ]

    case Map.get(config, "body") do
      nil -> opts
      body -> Keyword.put(opts, :body, body)
    end
  end

  defp validate_response(%Req.Response{status: status, body: body}, config, response_time) do
    expected_status = Map.get(config, "expected_status", 200)
    regex = Map.get(config, "regex")

    cond do
      status != expected_status ->
        {:error, "HTTP #{status}, expected #{expected_status}"}

      regex && !content_matches?(body, regex) ->
        {:error, "Content does not match pattern: #{regex}"}

      true ->
        {:ok, response_time, "HTTP #{status} OK"}
    end
  end

  defp format_error(%Req.TransportError{reason: :timeout}, timeout_ms) do
    {:error, "Connection timeout after #{timeout_ms}ms"}
  end

  defp format_error(%Req.TransportError{reason: :econnrefused}, _timeout_ms) do
    {:error, "Connection refused"}
  end

  defp format_error(%Req.TransportError{reason: reason}, _timeout_ms) do
    {:error, "Transport error: #{inspect(reason)}"}
  end

  defp format_error(reason, _timeout_ms) do
    {:error, "Request failed: #{inspect(reason)}"}
  end

  defp content_matches?(body, regex_str) when is_binary(body) do
    case Regex.compile(regex_str) do
      {:ok, regex} ->
        Regex.match?(regex, body)

      {:error, _} ->
        Logger.warning("Invalid regex pattern: #{regex_str}")
        false
    end
  end

  defp content_matches?(_body, _regex), do: false
end
