defmodule SimpleshopTheme.Products.ProviderConnectionTest do
  use SimpleshopTheme.DataCase, async: false

  alias SimpleshopTheme.Products.ProviderConnection
  alias SimpleshopTheme.Vault

  import SimpleshopTheme.ProductsFixtures

  describe "changeset/2" do
    test "valid attributes create a valid changeset" do
      attrs = valid_provider_connection_attrs()
      changeset = ProviderConnection.changeset(%ProviderConnection{}, attrs)

      assert changeset.valid?
    end

    test "requires provider_type" do
      attrs = valid_provider_connection_attrs() |> Map.delete(:provider_type)
      changeset = ProviderConnection.changeset(%ProviderConnection{}, attrs)

      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).provider_type
    end

    test "requires name" do
      attrs = valid_provider_connection_attrs() |> Map.delete(:name)
      changeset = ProviderConnection.changeset(%ProviderConnection{}, attrs)

      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).name
    end

    test "validates provider_type is in allowed list" do
      attrs = valid_provider_connection_attrs(%{provider_type: "invalid_provider"})
      changeset = ProviderConnection.changeset(%ProviderConnection{}, attrs)

      refute changeset.valid?
      assert "is invalid" in errors_on(changeset).provider_type
    end

    test "accepts all valid provider types" do
      for type <- ProviderConnection.provider_types() do
        attrs = valid_provider_connection_attrs(%{provider_type: type})
        changeset = ProviderConnection.changeset(%ProviderConnection{}, attrs)
        assert changeset.valid?, "Expected #{type} to be valid"
      end
    end

    test "encrypts api_key when provided" do
      attrs = valid_provider_connection_attrs(%{api_key: "my_secret_key"})
      changeset = ProviderConnection.changeset(%ProviderConnection{}, attrs)

      assert changeset.valid?

      # api_key should be removed from changes
      refute Map.has_key?(changeset.changes, :api_key)

      # api_key_encrypted should be set
      assert encrypted = changeset.changes.api_key_encrypted
      assert is_binary(encrypted)

      # Should decrypt to original
      assert {:ok, "my_secret_key"} = Vault.decrypt(encrypted)
    end

    test "does not set api_key_encrypted when api_key is nil" do
      attrs = valid_provider_connection_attrs() |> Map.delete(:api_key)
      changeset = ProviderConnection.changeset(%ProviderConnection{}, attrs)

      assert changeset.valid?
      refute Map.has_key?(changeset.changes, :api_key_encrypted)
    end

    test "defaults enabled to true" do
      attrs = valid_provider_connection_attrs() |> Map.delete(:enabled)
      changeset = ProviderConnection.changeset(%ProviderConnection{}, attrs)

      # enabled should use schema default
      assert changeset.valid?
    end

    test "stores config as map" do
      config = %{"shop_id" => "123", "extra" => "value"}
      attrs = valid_provider_connection_attrs(%{config: config})
      changeset = ProviderConnection.changeset(%ProviderConnection{}, attrs)

      assert changeset.valid?
      assert changeset.changes.config == config
    end
  end

  describe "sync_changeset/2" do
    test "updates sync_status" do
      conn = provider_connection_fixture()
      changeset = ProviderConnection.sync_changeset(conn, %{sync_status: "syncing"})

      assert changeset.valid?
      assert changeset.changes.sync_status == "syncing"
    end

    test "validates sync_status is in allowed list" do
      conn = provider_connection_fixture()
      changeset = ProviderConnection.sync_changeset(conn, %{sync_status: "invalid"})

      refute changeset.valid?
      assert "is invalid" in errors_on(changeset).sync_status
    end

    test "accepts all valid sync statuses" do
      conn = provider_connection_fixture()

      for status <- ProviderConnection.sync_statuses() do
        changeset = ProviderConnection.sync_changeset(conn, %{sync_status: status})
        assert changeset.valid?, "Expected #{status} to be valid"
      end
    end

    test "updates last_synced_at" do
      conn = provider_connection_fixture()
      now = DateTime.utc_now() |> DateTime.truncate(:second)
      changeset = ProviderConnection.sync_changeset(conn, %{last_synced_at: now})

      assert changeset.valid?
      assert changeset.changes.last_synced_at == now
    end
  end

  describe "get_api_key/1" do
    test "returns decrypted api_key" do
      conn = provider_connection_fixture(%{api_key: "secret_key_123"})
      assert ProviderConnection.get_api_key(conn) == "secret_key_123"
    end

    test "returns nil when api_key_encrypted is nil" do
      conn = %ProviderConnection{api_key_encrypted: nil}
      assert ProviderConnection.get_api_key(conn) == nil
    end

    test "returns nil on decryption failure" do
      conn = %ProviderConnection{api_key_encrypted: "invalid_encrypted_data"}
      assert ProviderConnection.get_api_key(conn) == nil
    end
  end

  describe "provider_types/0" do
    test "returns list of supported providers" do
      types = ProviderConnection.provider_types()

      assert is_list(types)
      assert "printify" in types
      assert "gelato" in types
      assert "prodigi" in types
      assert "printful" in types
    end
  end

  describe "unique constraint" do
    test "enforces unique provider_type" do
      _first = provider_connection_fixture(%{provider_type: "printify"})

      assert {:error, changeset} =
               SimpleshopTheme.Products.create_provider_connection(
                 valid_provider_connection_attrs(%{provider_type: "printify"})
               )

      assert "has already been taken" in errors_on(changeset).provider_type
    end
  end
end
