defmodule SimpleshopTheme.Providers.Printify do
  @moduledoc """
  Printify provider implementation.

  Handles product sync and order submission for Printify.
  """

  @behaviour SimpleshopTheme.Providers.Provider

  alias SimpleshopTheme.Clients.Printify, as: Client
  alias SimpleshopTheme.Products.ProviderConnection

  @impl true
  def provider_type, do: "printify"

  @impl true
  def test_connection(%ProviderConnection{} = conn) do
    with api_key when is_binary(api_key) <- ProviderConnection.get_api_key(conn),
         :ok <- set_api_key(api_key),
         {:ok, shops} <- Client.get_shops() do
      shop = List.first(shops)

      {:ok,
       %{
         shop_id: shop["id"],
         shop_name: shop["title"],
         shop_count: length(shops)
       }}
    else
      nil -> {:error, :no_api_key}
      {:error, _} = error -> error
    end
  end

  @impl true
  def fetch_products(%ProviderConnection{config: config} = conn) do
    shop_id = config["shop_id"]

    if is_nil(shop_id) do
      {:error, :no_shop_id}
    else
      with api_key when is_binary(api_key) <- ProviderConnection.get_api_key(conn),
           :ok <- set_api_key(api_key),
           {:ok, response} <- Client.list_products(shop_id) do
        products =
          response["data"]
          |> Enum.map(&normalize_product/1)

        {:ok, products}
      else
        nil -> {:error, :no_api_key}
        {:error, _} = error -> error
      end
    end
  end

  @impl true
  def submit_order(%ProviderConnection{config: config} = conn, order) do
    shop_id = config["shop_id"]

    if is_nil(shop_id) do
      {:error, :no_shop_id}
    else
      with api_key when is_binary(api_key) <- ProviderConnection.get_api_key(conn),
           :ok <- set_api_key(api_key),
           order_data <- build_order_payload(order),
           {:ok, response} <- Client.create_order(shop_id, order_data) do
        {:ok, %{provider_order_id: response["id"]}}
      else
        nil -> {:error, :no_api_key}
        {:error, _} = error -> error
      end
    end
  end

  @impl true
  def get_order_status(%ProviderConnection{config: config} = conn, provider_order_id) do
    shop_id = config["shop_id"]

    if is_nil(shop_id) do
      {:error, :no_shop_id}
    else
      with api_key when is_binary(api_key) <- ProviderConnection.get_api_key(conn),
           :ok <- set_api_key(api_key),
           {:ok, response} <- Client.get_order(shop_id, provider_order_id) do
        {:ok, normalize_order_status(response)}
      else
        nil -> {:error, :no_api_key}
        {:error, _} = error -> error
      end
    end
  end

  # =============================================================================
  # Data Normalization
  # =============================================================================

  defp normalize_product(raw) do
    %{
      provider_product_id: to_string(raw["id"]),
      title: raw["title"],
      description: raw["description"],
      category: extract_category(raw),
      images: normalize_images(raw["images"] || []),
      variants: normalize_variants(raw["variants"] || []),
      provider_data: %{
        blueprint_id: raw["blueprint_id"],
        print_provider_id: raw["print_provider_id"],
        tags: raw["tags"] || [],
        options: raw["options"] || [],
        raw: raw
      }
    }
  end

  defp normalize_images(images) do
    images
    |> Enum.with_index()
    |> Enum.map(fn {img, index} ->
      %{
        src: img["src"],
        position: img["position"] || index,
        alt: nil
      }
    end)
  end

  defp normalize_variants(variants) do
    Enum.map(variants, fn var ->
      %{
        provider_variant_id: to_string(var["id"]),
        title: var["title"],
        sku: var["sku"],
        price: var["price"],
        cost: var["cost"],
        options: normalize_variant_options(var),
        is_enabled: var["is_enabled"] == true,
        is_available: var["is_available"] == true
      }
    end)
  end

  defp normalize_variant_options(variant) do
    # Printify variants have options as a list of option value IDs
    # We need to build the human-readable map from the variant title
    # Format: "Size / Color" -> %{"Size" => "Large", "Color" => "Blue"}

    title = variant["title"] || ""
    parts = String.split(title, " / ")

    # Common option names based on position
    option_names = ["Size", "Color", "Style"]

    parts
    |> Enum.with_index()
    |> Enum.reduce(%{}, fn {value, index}, acc ->
      key = Enum.at(option_names, index) || "Option #{index + 1}"
      Map.put(acc, key, value)
    end)
  end

  defp extract_category(raw) do
    # Try to extract category from tags
    tags = raw["tags"] || []

    cond do
      "apparel" in tags or "clothing" in tags -> "Apparel"
      "homeware" in tags or "home" in tags -> "Homewares"
      "accessories" in tags -> "Accessories"
      "art" in tags or "print" in tags -> "Art Prints"
      true -> nil
    end
  end

  defp normalize_order_status(raw) do
    %{
      status: map_order_status(raw["status"]),
      provider_status: raw["status"],
      tracking_number: extract_tracking(raw),
      tracking_url: extract_tracking_url(raw),
      shipments: raw["shipments"] || []
    }
  end

  defp map_order_status("pending"), do: "pending"
  defp map_order_status("on-hold"), do: "pending"
  defp map_order_status("payment-not-received"), do: "pending"
  defp map_order_status("in-production"), do: "processing"
  defp map_order_status("partially-shipped"), do: "processing"
  defp map_order_status("shipped"), do: "shipped"
  defp map_order_status("delivered"), do: "delivered"
  defp map_order_status("canceled"), do: "cancelled"
  defp map_order_status(_), do: "pending"

  defp extract_tracking(raw) do
    case raw["shipments"] do
      [shipment | _] -> shipment["tracking_number"]
      _ -> nil
    end
  end

  defp extract_tracking_url(raw) do
    case raw["shipments"] do
      [shipment | _] -> shipment["tracking_url"]
      _ -> nil
    end
  end

  # =============================================================================
  # Order Building
  # =============================================================================

  defp build_order_payload(order) do
    %{
      external_id: order.order_number,
      label: order.order_number,
      line_items:
        Enum.map(order.line_items, fn item ->
          %{
            product_id: item.product_variant.product.provider_product_id,
            variant_id: String.to_integer(item.product_variant.provider_variant_id),
            quantity: item.quantity
          }
        end),
      shipping_method: 1,
      address_to: %{
        first_name: order.shipping_address["first_name"],
        last_name: order.shipping_address["last_name"],
        email: order.customer_email,
        phone: order.shipping_address["phone"],
        country: order.shipping_address["country"],
        region: order.shipping_address["state"] || order.shipping_address["region"],
        address1: order.shipping_address["address1"],
        address2: order.shipping_address["address2"],
        city: order.shipping_address["city"],
        zip: order.shipping_address["zip"] || order.shipping_address["postal_code"]
      }
    }
  end

  # =============================================================================
  # API Key Management
  # =============================================================================

  # Temporarily sets the API key for the request
  # In a production system, this would use a connection pool or request context
  defp set_api_key(api_key) do
    Process.put(:printify_api_key, api_key)
    :ok
  end
end
