defmodule BerrypodWeb.CheckoutController do
  use BerrypodWeb, :controller

  alias Berrypod.{Analytics, Cart, Settings}
  alias Berrypod.Orders
  alias Berrypod.Shipping

  require Logger

  def create(conn, _params) do
    unless Settings.has_secret?("stripe_api_key") do
      conn
      |> put_flash(:error, "Checkout isn't available yet")
      |> redirect(to: R.cart())
    else
      cart_items = Cart.get_from_session(get_session(conn))
      hydrated = Cart.hydrate(cart_items)

      cond do
        hydrated == [] ->
          conn
          |> put_flash(:error, "Your basket is empty")
          |> redirect(to: R.cart())

        Enum.any?(hydrated, &(&1.is_available == false)) ->
          conn
          |> put_flash(:error, "Some items in your basket are no longer available")
          |> redirect(to: R.cart())

        true ->
          track_checkout_start(conn)
          create_checkout(conn, hydrated)
      end
    end
  end

  defp create_checkout(conn, hydrated_items) do
    # Create a pending order with price snapshots
    case Orders.create_order(%{items: hydrated_items}) do
      {:ok, order} ->
        create_stripe_session(conn, order, hydrated_items)

      {:error, _changeset} ->
        Logger.error("Failed to create order")

        conn
        |> put_flash(:error, "Something went wrong. Please try again.")
        |> redirect(to: R.cart())
    end
  end

  defp create_stripe_session(conn, order, hydrated_items) do
    line_items =
      Enum.map(hydrated_items, fn item ->
        product_name =
          if item.variant,
            do: "#{item.name} — #{item.variant}",
            else: item.name

        %{
          price_data: %{
            currency: "gbp",
            unit_amount: item.price,
            product_data: %{name: product_name}
          },
          quantity: item.quantity
        }
      end)

    params =
      %{
        mode: "payment",
        line_items: line_items,
        success_url: R.url("/checkout/complete") <> "?session_id={CHECKOUT_SESSION_ID}",
        cancel_url: R.url(R.cart()),
        metadata: %{"order_id" => order.id},
        shipping_address_collection: %{
          allowed_countries: ["GB", "US", "CA", "AU", "DE", "FR", "NL", "IE", "AT", "BE"]
        }
      }
      |> maybe_add_shipping_options(hydrated_items)
      |> maybe_add_cart_recovery_notice()

    case Stripe.Checkout.Session.create(params) do
      {:ok, session} ->
        {:ok, _order} = Orders.set_stripe_session(order, session.id)

        conn
        |> redirect(external: session.url)

      {:error, %Stripe.Error{message: message}} ->
        Logger.error("Stripe session creation failed: #{message}")
        Orders.mark_failed(order)

        conn
        |> put_flash(:error, "Payment setup failed. Please try again.")
        |> redirect(to: R.cart())

      {:error, reason} ->
        Logger.error("Stripe session creation failed: #{inspect(reason)}")
        Orders.mark_failed(order)

        conn
        |> put_flash(:error, "Payment setup failed. Please try again.")
        |> redirect(to: R.cart())
    end
  end

  defp maybe_add_cart_recovery_notice(params) do
    if Berrypod.Settings.abandoned_cart_recovery_enabled?() do
      Map.put(params, :custom_text, %{
        after_submit: %{
          message:
            "If your payment doesn't complete, we may send you one follow-up email. You can unsubscribe at any time."
        }
      })
    else
      params
    end
  end

  defp maybe_add_shipping_options(params, hydrated_items) do
    gb_result = Shipping.calculate_for_cart(hydrated_items, "GB")
    us_result = Shipping.calculate_for_cart(hydrated_items, "US")

    options =
      []
      |> maybe_add_option(gb_result, "UK delivery", 5, 10)
      |> maybe_add_option(us_result, "International delivery", 10, 20)

    if options == [] do
      params
    else
      Map.put(params, :shipping_options, options)
    end
  end

  defp maybe_add_option(options, {:ok, cost}, name, min_days, max_days) when cost > 0 do
    option = %{
      shipping_rate_data: %{
        type: "fixed_amount",
        display_name: name,
        fixed_amount: %{amount: cost, currency: "gbp"},
        delivery_estimate: %{
          minimum: %{unit: "business_day", value: min_days},
          maximum: %{unit: "business_day", value: max_days}
        }
      }
    }

    options ++ [option]
  end

  defp maybe_add_option(options, _result, _name, _min, _max), do: options

  defp track_checkout_start(conn) do
    visitor_hash = get_session(conn, "analytics_visitor_hash")

    if visitor_hash do
      Analytics.track_event("checkout_start", %{
        pathname: "/checkout",
        visitor_hash: visitor_hash,
        browser: get_session(conn, "analytics_browser"),
        os: get_session(conn, "analytics_os"),
        screen_size: get_session(conn, "analytics_screen_size"),
        country_code: get_session(conn, "country_code")
      })
    end
  end
end
