defmodule Berrypod.ExchangeRate do
  @moduledoc """
  Fetches and caches exchange rates for shipping cost conversion.

  Uses the frankfurter.app API (ECB data, free, no API key).
  Rates are fetched during product sync and cached in Settings so
  they survive restarts without an API call.
  """

  alias Berrypod.Settings

  require Logger

  @api_base "https://api.frankfurter.app"
  @settings_prefix "exchange_rate_"
  @default_rates %{"USD" => 0.80, "EUR" => 0.86}

  @doc """
  Fetches the latest exchange rates to GBP from the API and caches them.

  Returns `{:ok, rates_map}` or `{:error, reason}`.
  The rates map has currency codes as keys and GBP multipliers as values,
  e.g. `%{"USD" => 0.7892, "EUR" => 0.8534}`.
  """
  def fetch_and_cache do
    case fetch_from_api() do
      {:ok, rates} ->
        cache_rates(rates)
        {:ok, rates}

      {:error, reason} ->
        Logger.warning("Exchange rate fetch failed: #{inspect(reason)}, using cached rates")
        {:ok, get_cached_rates()}
    end
  end

  @doc """
  Returns cached exchange rates from Settings, falling back to defaults.
  """
  def get_cached_rates do
    Enum.reduce(@default_rates, %{}, fn {currency, default}, acc ->
      key = @settings_prefix <> String.downcase(currency) <> "_to_gbp"

      rate =
        case Settings.get_setting(key) do
          nil -> default
          val when is_binary(val) -> String.to_float(val)
          val when is_number(val) -> val / 1
        end

      Map.put(acc, currency, rate)
    end)
  end

  @doc """
  Returns the GBP rate for a given currency, using cached values.
  """
  def rate_for(currency) when currency in ["GBP", "gbp"], do: 1.0

  def rate_for(currency) do
    rates = get_cached_rates()

    Map.get(
      rates,
      String.upcase(currency),
      Map.get(@default_rates, String.upcase(currency), 0.80)
    )
  end

  # Fetch from frankfurter.app API
  defp fetch_from_api do
    url = "#{@api_base}/latest?from=USD&to=GBP,EUR"

    case Req.get(url, receive_timeout: 10_000) do
      {:ok, %{status: 200, body: %{"rates" => rates}}} ->
        # API returns rates FROM USD, so "GBP" value is the USD→GBP multiplier
        gbp_per_usd = rates["GBP"]
        # Derive EUR→GBP: if 1 USD = X GBP and 1 USD = Y EUR, then 1 EUR = X/Y GBP
        eur_rate = rates["EUR"]

        eur_to_gbp =
          if eur_rate && eur_rate > 0,
            do: gbp_per_usd / eur_rate,
            else: @default_rates["EUR"]

        {:ok, %{"USD" => gbp_per_usd, "EUR" => eur_to_gbp}}

      {:ok, %{status: status}} ->
        {:error, {:http_status, status}}

      {:error, reason} ->
        {:error, reason}
    end
  end

  defp cache_rates(rates) do
    Enum.each(rates, fn {currency, rate} ->
      key = @settings_prefix <> String.downcase(currency) <> "_to_gbp"
      Settings.put_setting(key, Float.to_string(rate))
    end)
  end
end
