package services

import (
	"context"
	"database/sql"
	"encoding/json"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
)

func TestNewSettingsService(t *testing.T) {
	db, _, err := sqlmock.New()
	if err != nil {
		t.Fatalf("Failed to create mock db: %v", err)
	}
	defer db.Close()

	service := NewSettingsService(db)
	if service == nil {
		t.Error("Expected service, got nil")
	}
	if service.db != db {
		t.Error("Expected db to be set")
	}
}

func TestSettingsService_GetSettings(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("Failed to create mock db: %v", err)
	}
	defer db.Close()

	service := NewSettingsService(db)
	ctx := context.Background()

	t.Run("returns setting value", func(t *testing.T) {
		expectedValue := map[string]interface{}{"theme": "dark", "lang": "pt-BR"}
		jsonBytes, _ := json.Marshal(expectedValue)

		mock.ExpectQuery("SELECT value FROM system_settings WHERE key").
			WithArgs("ui_config").
			WillReturnRows(sqlmock.NewRows([]string{"value"}).AddRow(jsonBytes))

		result, err := service.GetSettings(ctx, "ui_config")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if result == nil {
			t.Fatal("Expected result, got nil")
		}

		var parsed map[string]interface{}
		if err := json.Unmarshal(result, &parsed); err != nil {
			t.Fatalf("Failed to parse result: %v", err)
		}
		if parsed["theme"] != "dark" {
			t.Errorf("Expected theme='dark', got '%v'", parsed["theme"])
		}

		if err := mock.ExpectationsWereMet(); err != nil {
			t.Errorf("Unmet expectations: %v", err)
		}
	})

	t.Run("returns nil when not found", func(t *testing.T) {
		mock.ExpectQuery("SELECT value FROM system_settings WHERE key").
			WithArgs("non_existent").
			WillReturnError(sql.ErrNoRows)

		result, err := service.GetSettings(ctx, "non_existent")
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}
		if result != nil {
			t.Errorf("Expected nil, got %v", result)
		}

		if err := mock.ExpectationsWereMet(); err != nil {
			t.Errorf("Unmet expectations: %v", err)
		}
	})

	t.Run("returns error on db failure", func(t *testing.T) {
		mock.ExpectQuery("SELECT value FROM system_settings WHERE key").
			WithArgs("test_key").
			WillReturnError(sql.ErrConnDone)

		_, err := service.GetSettings(ctx, "test_key")
		if err == nil {
			t.Error("Expected error, got nil")
		}

		if err := mock.ExpectationsWereMet(); err != nil {
			t.Errorf("Unmet expectations: %v", err)
		}
	})
}

func TestSettingsService_SaveSettings(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("Failed to create mock db: %v", err)
	}
	defer db.Close()

	service := NewSettingsService(db)
	ctx := context.Background()

	t.Run("saves setting successfully", func(t *testing.T) {
		value := map[string]interface{}{"enabled": true, "timeout": 30}

		mock.ExpectExec("INSERT INTO system_settings").
			WithArgs("feature_flags", sqlmock.AnyArg()).
			WillReturnResult(sqlmock.NewResult(1, 1))

		err := service.SaveSettings(ctx, "feature_flags", value)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		if err := mock.ExpectationsWereMet(); err != nil {
			t.Errorf("Unmet expectations: %v", err)
		}
	})

	t.Run("upserts existing setting", func(t *testing.T) {
		value := map[string]string{"version": "2.0"}

		mock.ExpectExec("INSERT INTO system_settings").
			WithArgs("app_config", sqlmock.AnyArg()).
			WillReturnResult(sqlmock.NewResult(0, 1))

		err := service.SaveSettings(ctx, "app_config", value)
		if err != nil {
			t.Fatalf("Unexpected error: %v", err)
		}

		if err := mock.ExpectationsWereMet(); err != nil {
			t.Errorf("Unmet expectations: %v", err)
		}
	})

	t.Run("returns error on db failure", func(t *testing.T) {
		mock.ExpectExec("INSERT INTO system_settings").
			WithArgs("bad_key", sqlmock.AnyArg()).
			WillReturnError(sql.ErrConnDone)

		err := service.SaveSettings(ctx, "bad_key", "value")
		if err == nil {
			t.Error("Expected error, got nil")
		}

		if err := mock.ExpectationsWereMet(); err != nil {
			t.Errorf("Unmet expectations: %v", err)
		}
	})

	t.Run("handles unmarshalable value", func(t *testing.T) {
		// Channels cannot be marshaled to JSON
		badValue := make(chan int)

		err := service.SaveSettings(ctx, "bad_marshal", badValue)
		if err == nil {
			t.Error("Expected marshal error, got nil")
		}
	})
}
