defmodule SimpleshopTheme.SettingsTest do
  use SimpleshopTheme.DataCase, async: false

  alias SimpleshopTheme.Settings
  alias SimpleshopTheme.Settings.ThemeSettings

  describe "get_setting/2 and put_setting/3" do
    test "stores and retrieves string settings" do
      assert {:ok, _} = Settings.put_setting("site_name", "My Shop")
      assert Settings.get_setting("site_name") == "My Shop"
    end

    test "stores and retrieves json settings" do
      data = %{"foo" => "bar", "nested" => %{"key" => "value"}}
      assert {:ok, _} = Settings.put_setting("custom_data", data, "json")
      assert Settings.get_setting("custom_data") == data
    end

    test "stores and retrieves integer settings" do
      assert {:ok, _} = Settings.put_setting("max_products", 100, "integer")
      assert Settings.get_setting("max_products") == 100
    end

    test "stores and retrieves boolean settings" do
      assert {:ok, _} = Settings.put_setting("feature_enabled", true, "boolean")
      assert Settings.get_setting("feature_enabled") == true
    end

    test "returns default when setting doesn't exist" do
      assert Settings.get_setting("nonexistent", "default") == "default"
      assert Settings.get_setting("nonexistent") == nil
    end

    test "updates existing setting" do
      assert {:ok, _} = Settings.put_setting("site_name", "Old Name")
      assert {:ok, _} = Settings.put_setting("site_name", "New Name")
      assert Settings.get_setting("site_name") == "New Name"
    end
  end

  describe "get_theme_settings/0" do
    test "returns default theme settings when none exist" do
      settings = Settings.get_theme_settings()
      assert %ThemeSettings{} = settings
      assert settings.mood == "neutral"
      assert settings.typography == "clean"
      assert settings.shape == "soft"
      assert settings.density == "balanced"
    end

    test "returns stored theme settings" do
      {:ok, _} = Settings.update_theme_settings(%{mood: "dark", typography: "modern"})
      settings = Settings.get_theme_settings()
      assert settings.mood == "dark"
      assert settings.typography == "modern"
    end
  end

  describe "update_theme_settings/1" do
    test "updates theme settings successfully" do
      {:ok, settings} = Settings.update_theme_settings(%{mood: "warm", typography: "editorial"})
      assert settings.mood == "warm"
      assert settings.typography == "editorial"
    end

    test "regenerates CSS cache when settings change" do
      alias SimpleshopTheme.Theme.CSSCache

      # Get initial cached CSS
      {:ok, initial_css} = CSSCache.get()
      assert initial_css =~ ".themed {"

      # Update to a different accent color
      {:ok, _settings} = Settings.update_theme_settings(%{accent_color: "#ff0000"})

      # Cache should now contain new CSS with the red accent color
      {:ok, updated_css} = CSSCache.get()
      assert updated_css =~ ".themed {"
      # Red = hue 0
      assert updated_css =~ "--t-accent-h: 0"

      # Change to blue
      {:ok, _settings} = Settings.update_theme_settings(%{accent_color: "#0000ff"})

      {:ok, blue_css} = CSSCache.get()
      # Blue = hue 240
      assert blue_css =~ "--t-accent-h: 240"

      # Restore default
      CSSCache.warm()
    end

    test "validates mood values" do
      {:error, changeset} = Settings.update_theme_settings(%{mood: "invalid"})
      assert "is invalid" in errors_on(changeset).mood
    end

    test "validates typography values" do
      {:error, changeset} = Settings.update_theme_settings(%{typography: "invalid"})
      assert "is invalid" in errors_on(changeset).typography
    end

    test "validates logo size range" do
      {:error, changeset} = Settings.update_theme_settings(%{logo_size: 10})
      assert "must be greater than or equal to 24" in errors_on(changeset).logo_size
    end

    test "preserves existing settings when updating subset" do
      {:ok, _} = Settings.update_theme_settings(%{mood: "warm"})
      {:ok, settings} = Settings.update_theme_settings(%{typography: "modern"})
      assert settings.mood == "warm"
      assert settings.typography == "modern"
    end
  end

  describe "apply_preset/1" do
    test "applies gallery preset successfully" do
      {:ok, settings} = Settings.apply_preset(:gallery)
      assert settings.mood == "warm"
      assert settings.typography == "editorial"
      assert settings.shape == "soft"
      assert settings.accent_color == "#e85d04"
    end

    test "applies studio preset successfully" do
      {:ok, settings} = Settings.apply_preset(:studio)
      assert settings.mood == "neutral"
      assert settings.typography == "clean"
      assert settings.accent_color == "#2563eb"
    end

    test "returns error for invalid preset" do
      assert {:error, :preset_not_found} = Settings.apply_preset(:nonexistent)
    end
  end

  describe "put_secret/2 and get_secret/2" do
    test "encrypts, stores, and retrieves a secret" do
      assert {:ok, _} = Settings.put_secret("test_key", "super_secret_value")
      assert Settings.get_secret("test_key") == "super_secret_value"
    end

    test "returns default when secret doesn't exist" do
      assert Settings.get_secret("nonexistent") == nil
      assert Settings.get_secret("nonexistent", "fallback") == "fallback"
    end

    test "upserts existing secret" do
      assert {:ok, _} = Settings.put_secret("test_key", "first_value")
      assert {:ok, _} = Settings.put_secret("test_key", "second_value")
      assert Settings.get_secret("test_key") == "second_value"
    end

    test "stores encrypted_value as binary, not plaintext" do
      {:ok, _} = Settings.put_secret("test_key", "plaintext_here")

      setting = Repo.get_by(SimpleshopTheme.Settings.Setting, key: "test_key")
      assert setting.value_type == "encrypted"
      assert setting.value == "[encrypted]"
      assert is_binary(setting.encrypted_value)
      refute setting.encrypted_value == "plaintext_here"
    end
  end

  describe "has_secret?/1" do
    test "returns false when secret doesn't exist" do
      refute Settings.has_secret?("nonexistent")
    end

    test "returns true when secret exists" do
      {:ok, _} = Settings.put_secret("test_key", "value")
      assert Settings.has_secret?("test_key")
    end
  end

  describe "secret_hint/1" do
    test "returns nil when secret doesn't exist" do
      assert Settings.secret_hint("nonexistent") == nil
    end

    test "returns masked hint for long secrets" do
      {:ok, _} = Settings.put_secret("test_key", "sk_test_abc123xyz789")
      hint = Settings.secret_hint("test_key")
      assert hint =~ "sk_test_"
      assert hint =~ "•••"
      assert hint =~ "789"
    end

    test "returns masked hint for short secrets" do
      {:ok, _} = Settings.put_secret("test_key", "short")
      assert Settings.secret_hint("test_key") == "•••"
    end
  end

  describe "site_live?/0 and set_site_live/1" do
    test "defaults to false when no setting exists" do
      refute Settings.site_live?()
    end

    test "returns true after setting site live" do
      assert {:ok, _} = Settings.set_site_live(true)
      assert Settings.site_live?()
    end

    test "returns false after setting site offline" do
      assert {:ok, _} = Settings.set_site_live(true)
      assert Settings.site_live?()

      assert {:ok, _} = Settings.set_site_live(false)
      refute Settings.site_live?()
    end
  end

  describe "delete_setting/1" do
    test "deletes an existing setting" do
      {:ok, _} = Settings.put_setting("to_delete", "value")
      assert Settings.get_setting("to_delete") == "value"

      assert {:ok, _} = Settings.delete_setting("to_delete")
      assert Settings.get_setting("to_delete") == nil
    end

    test "returns :ok when setting doesn't exist" do
      assert :ok = Settings.delete_setting("nonexistent")
    end
  end
end
