defmodule SimpleshopTheme.Products.ProviderConnection do
  @moduledoc """
  Schema for POD provider connections.

  Stores encrypted API credentials and configuration for each provider.
  Only one connection per provider type is allowed.
  """

  use Ecto.Schema
  import Ecto.Changeset

  alias SimpleshopTheme.Vault

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id

  @provider_types ~w(printify gelato prodigi printful)
  @sync_statuses ~w(pending syncing completed failed)

  schema "provider_connections" do
    field :provider_type, :string
    field :name, :string
    field :enabled, :boolean, default: true
    field :api_key_encrypted, :binary
    field :config, :map, default: %{}
    field :last_synced_at, :utc_datetime
    field :sync_status, :string, default: "pending"

    # Virtual field for setting API key
    field :api_key, :string, virtual: true

    has_many :products, SimpleshopTheme.Products.Product

    timestamps(type: :utc_datetime)
  end

  @doc """
  Returns the list of supported provider types.
  """
  def provider_types, do: @provider_types

  @doc """
  Returns the list of valid sync statuses.
  """
  def sync_statuses, do: @sync_statuses

  @doc """
  Changeset for creating a new provider connection.
  """
  def changeset(provider_connection, attrs) do
    provider_connection
    |> cast(attrs, [:provider_type, :name, :enabled, :api_key, :config])
    |> validate_required([:provider_type, :name])
    |> validate_inclusion(:provider_type, @provider_types)
    |> unique_constraint(:provider_type)
    |> encrypt_api_key()
  end

  @doc """
  Changeset for updating sync status.
  """
  def sync_changeset(provider_connection, attrs) do
    provider_connection
    |> cast(attrs, [:last_synced_at, :sync_status])
    |> validate_inclusion(:sync_status, @sync_statuses)
  end

  @doc """
  Decrypts and returns the API key for a provider connection.
  """
  def get_api_key(%__MODULE__{api_key_encrypted: nil}), do: nil

  def get_api_key(%__MODULE__{api_key_encrypted: encrypted}) do
    case Vault.decrypt(encrypted) do
      {:ok, api_key} -> api_key
      {:error, _} -> nil
    end
  end

  defp encrypt_api_key(changeset) do
    case get_change(changeset, :api_key) do
      nil ->
        changeset

      api_key ->
        case Vault.encrypt(api_key) do
          {:ok, encrypted} ->
            changeset
            |> put_change(:api_key_encrypted, encrypted)
            |> delete_change(:api_key)

          {:error, _} ->
            add_error(changeset, :api_key, "could not be encrypted")
        end
    end
  end
end
