package store

import (
	"encoding/json"
	"testing"
)

func boolPtr(b bool) *bool { return &b }

func TestResolveSettings_GlobalOnly(t *testing.T) {
	global := json.RawMessage(`{"font_size": 14, "theme": "dark"}`)
	schema := []SettingSchema{
		{Key: "font_size", UserOverridable: boolPtr(true)},
		{Key: "theme", UserOverridable: boolPtr(true)},
	}

	merged := ResolveSettings(global, nil, nil, schema)

	if merged["font_size"] != float64(14) {
		t.Errorf("font_size: got %v, want 14", merged["font_size"])
	}
	if merged["theme"] != "dark" {
		t.Errorf("theme: got %v, want dark", merged["theme"])
	}
}

func TestResolveSettings_UserOverridesGlobal(t *testing.T) {
	global := json.RawMessage(`{"font_size": 14, "theme": "dark"}`)
	user := json.RawMessage(`{"font_size": 18}`)
	schema := []SettingSchema{
		{Key: "font_size", UserOverridable: boolPtr(true)},
		{Key: "theme", UserOverridable: boolPtr(true)},
	}

	merged := ResolveSettings(global, nil, user, schema)

	if merged["font_size"] != float64(18) {
		t.Errorf("font_size: got %v, want 18", merged["font_size"])
	}
	if merged["theme"] != "dark" {
		t.Errorf("theme: got %v, want dark", merged["theme"])
	}
}

func TestResolveSettings_LockedKeyBlocksUserAndTeam(t *testing.T) {
	global := json.RawMessage(`{"api_url": "https://prod.example.com", "font_size": 14}`)
	team := json.RawMessage(`{"api_url": "https://team.example.com", "font_size": 16}`)
	user := json.RawMessage(`{"api_url": "https://user.example.com", "font_size": 20}`)
	schema := []SettingSchema{
		{Key: "api_url", UserOverridable: boolPtr(false)},
		{Key: "font_size", UserOverridable: boolPtr(true)},
	}

	merged := ResolveSettings(global, team, user, schema)

	if merged["api_url"] != "https://prod.example.com" {
		t.Errorf("api_url: got %v, want https://prod.example.com (locked)", merged["api_url"])
	}
	if merged["font_size"] != float64(20) {
		t.Errorf("font_size: got %v, want 20 (user wins)", merged["font_size"])
	}
}

func TestResolveSettings_TeamOverridesGlobal(t *testing.T) {
	global := json.RawMessage(`{"font_size": 14}`)
	team := json.RawMessage(`{"font_size": 16}`)
	schema := []SettingSchema{
		{Key: "font_size", UserOverridable: boolPtr(true)},
	}

	merged := ResolveSettings(global, team, nil, schema)

	if merged["font_size"] != float64(16) {
		t.Errorf("font_size: got %v, want 16", merged["font_size"])
	}
}

func TestResolveSettings_UserOverridesTeam(t *testing.T) {
	global := json.RawMessage(`{"font_size": 14}`)
	team := json.RawMessage(`{"font_size": 16}`)
	user := json.RawMessage(`{"font_size": 20}`)
	schema := []SettingSchema{
		{Key: "font_size", UserOverridable: boolPtr(true)},
	}

	merged := ResolveSettings(global, team, user, schema)

	if merged["font_size"] != float64(20) {
		t.Errorf("font_size: got %v, want 20", merged["font_size"])
	}
}

func TestResolveSettings_AbsentFlagDefaultsTrue(t *testing.T) {
	global := json.RawMessage(`{"font_size": 14}`)
	user := json.RawMessage(`{"font_size": 20}`)
	// UserOverridable is nil — should default to true
	schema := []SettingSchema{
		{Key: "font_size"},
	}

	merged := ResolveSettings(global, nil, user, schema)

	if merged["font_size"] != float64(20) {
		t.Errorf("font_size: got %v, want 20 (nil flag = overridable)", merged["font_size"])
	}
}

func TestResolveSettings_UnknownKeyDefaultsOverridable(t *testing.T) {
	global := json.RawMessage(`{"known": "a", "unknown": "b"}`)
	user := json.RawMessage(`{"unknown": "c"}`)
	schema := []SettingSchema{
		{Key: "known", UserOverridable: boolPtr(false)},
		// "unknown" is not in schema
	}

	merged := ResolveSettings(global, nil, user, schema)

	if merged["unknown"] != "c" {
		t.Errorf("unknown: got %v, want c (undeclared keys are overridable)", merged["unknown"])
	}
}

func TestResolveSettings_NilLayers(t *testing.T) {
	merged := ResolveSettings(nil, nil, nil, nil)
	if merged == nil {
		t.Error("expected non-nil empty map")
	}
	if len(merged) != 0 {
		t.Errorf("expected empty map, got %v", merged)
	}
}

func TestResolveSettings_EmptyJSON(t *testing.T) {
	merged := ResolveSettings(json.RawMessage(`{}`), json.RawMessage(`{}`), json.RawMessage(`{}`), nil)
	if len(merged) != 0 {
		t.Errorf("expected empty map, got %v", merged)
	}
}

func TestParseSettingsSchema(t *testing.T) {
	manifest := map[string]any{
		"settings": []any{
			map[string]any{"key": "font_size", "type": "number", "default": 14, "user_overridable": true},
			map[string]any{"key": "api_url", "type": "string", "user_overridable": false},
			map[string]any{"key": "theme", "type": "string"},
			map[string]any{"no_key": true}, // should be skipped
		},
	}

	schema := ParseSettingsSchema(manifest)

	if len(schema) != 3 {
		t.Fatalf("expected 3 entries, got %d", len(schema))
	}

	if schema[0].Key != "font_size" || !schema[0].IsUserOverridable() {
		t.Errorf("font_size: expected overridable=true, got %v", schema[0].UserOverridable)
	}
	if schema[1].Key != "api_url" || schema[1].IsUserOverridable() {
		t.Errorf("api_url: expected overridable=false, got %v", schema[1].UserOverridable)
	}
	if schema[2].Key != "theme" || !schema[2].IsUserOverridable() {
		t.Errorf("theme: nil flag should default to overridable=true")
	}
}

func TestParseSettingsSchema_NoSettings(t *testing.T) {
	schema := ParseSettingsSchema(map[string]any{})
	if schema != nil {
		t.Errorf("expected nil, got %v", schema)
	}
}

func TestFilterOverridableKeys(t *testing.T) {
	settings := map[string]any{
		"font_size": 20,
		"api_url":   "https://hack.example.com",
		"theme":     "light",
	}
	schema := []SettingSchema{
		{Key: "font_size", UserOverridable: boolPtr(true)},
		{Key: "api_url", UserOverridable: boolPtr(false)},
		{Key: "theme"}, // nil = overridable
	}

	filtered, rejected := FilterOverridableKeys(settings, schema)

	if _, ok := filtered["api_url"]; ok {
		t.Error("api_url should have been stripped")
	}
	if filtered["font_size"] != 20 {
		t.Errorf("font_size: got %v, want 20", filtered["font_size"])
	}
	if filtered["theme"] != "light" {
		t.Errorf("theme: got %v, want light", filtered["theme"])
	}
	if len(rejected) != 1 || rejected[0] != "api_url" {
		t.Errorf("rejected: got %v, want [api_url]", rejected)
	}
}
