defmodule SimpleshopThemeWeb.StripeWebhookController do
  use SimpleshopThemeWeb, :controller

  alias SimpleshopTheme.Orders
  alias SimpleshopTheme.Orders.{OrderNotifier, OrderSubmissionWorker}

  require Logger

  def handle(conn, _params) do
    raw_body = conn.assigns[:raw_body] || ""
    signature = List.first(get_req_header(conn, "stripe-signature")) || ""
    signing_secret = Application.get_env(:stripity_stripe, :signing_secret) || ""

    case Stripe.Webhook.construct_event(raw_body, signature, signing_secret) do
      {:ok, %Stripe.Event{} = event} ->
        handle_event(event)
        json(conn, %{received: true})

      {:error, reason} ->
        Logger.warning("Stripe webhook verification failed: #{inspect(reason)}")

        conn
        |> put_status(401)
        |> json(%{error: "Invalid signature"})
    end
  end

  defp handle_event(%Stripe.Event{type: "checkout.session.completed", data: %{object: session}}) do
    order_id = get_in(session, [:metadata, "order_id"]) || session.metadata["order_id"]

    case Orders.get_order(order_id) do
      nil ->
        Logger.warning("Stripe webhook: order not found for id=#{order_id}")

      order ->
        payment_intent_id = session.payment_intent
        {:ok, order} = Orders.mark_paid(order, payment_intent_id)

        # Update shipping address if collected by Stripe
        order =
          if session.shipping_details do
            {:ok, updated} = update_shipping(order, session.shipping_details)
            updated
          else
            order
          end

        # Update customer email from Stripe session
        order =
          if session.customer_details && session.customer_details.email do
            {:ok, updated} =
              Orders.update_order(order, %{customer_email: session.customer_details.email})

            updated
          else
            order
          end

        # Reload items for the email (update_order doesn't preload)
        order = Orders.get_order(order.id)

        # Broadcast to success page via PubSub
        Phoenix.PubSub.broadcast(
          SimpleshopTheme.PubSub,
          "order:#{order.id}:status",
          {:order_paid, order}
        )

        OrderNotifier.deliver_order_confirmation(order)

        # Submit to fulfilment provider
        if order.shipping_address && order.shipping_address != %{} do
          OrderSubmissionWorker.enqueue(order.id)
        else
          Logger.warning(
            "Order #{order.order_number} paid but no shipping address — manual submit needed"
          )
        end

        Logger.info("Order #{order.order_number} marked as paid")
    end
  end

  defp handle_event(%Stripe.Event{type: "checkout.session.expired", data: %{object: session}}) do
    order_id = get_in(session, [:metadata, "order_id"]) || session.metadata["order_id"]

    case Orders.get_order(order_id) do
      nil -> :ok
      order -> Orders.mark_failed(order)
    end

    Logger.info("Stripe checkout session expired for order #{order_id}")
  end

  defp handle_event(%Stripe.Event{type: type}) do
    Logger.debug("Unhandled Stripe event: #{type}")
  end

  defp update_shipping(order, shipping_details) do
    address = shipping_details.address || %{}

    shipping_address = %{
      "name" => shipping_details.name,
      "line1" => address.line1,
      "line2" => address.line2,
      "city" => address.city,
      "postal_code" => address.postal_code,
      "state" => address.state,
      "country" => address.country
    }

    Orders.update_order(order, %{shipping_address: shipping_address})
  end
end
