package repository

import (
	"testing"

	"hyapp-admin-server/internal/model"

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

func TestMergeScopedAppConfigsDoesNotLeakOverridesOrTombstonesAcrossApps(t *testing.T) {
	rows := []model.AppConfig{
		{AppCode: "", Group: "runtime-config", Key: "admin", Description: "Legacy Admin", Value: "https://legacy.example.com/admin"},
		{AppCode: "", Group: "runtime-config", Key: "host", Description: "Legacy Host", Value: "https://legacy.example.com/host"},
		{AppCode: "yumi", Group: "runtime-config", Key: "admin", Description: "Yumi Admin", Value: "https://yumi.example.com/admin"},
		{AppCode: "yumi", Group: "runtime-config", Key: "host", IsDeleted: true},
		{AppCode: "fami", Group: "runtime-config", Key: "admin", Description: "Fami Admin", Value: "https://fami.example.com/admin"},
	}

	yumi := mergeScopedAppConfigs(rows, "yumi")
	if len(yumi) != 1 || yumi[0].AppCode != "yumi" || yumi[0].Description != "Yumi Admin" {
		t.Fatalf("yumi effective config mismatch: %+v", yumi)
	}
	fami := mergeScopedAppConfigs(rows, "fami")
	if len(fami) != 2 || fami[0].AppCode != "fami" || fami[0].Description != "Fami Admin" || fami[1].Value != "https://legacy.example.com/host" {
		t.Fatalf("fami must not inherit yumi override/tombstone: %+v", fami)
	}
	future := mergeScopedAppConfigs(rows, "future-app")
	if len(future) != 2 || future[0].Description != "Legacy Admin" || future[1].Description != "Legacy Host" {
		t.Fatalf("future app must inherit only legacy baseline: %+v", future)
	}
}

func TestTombstoneScopedAppConfigWritesOnlyCurrentApp(t *testing.T) {
	store, mock, closeStore := newRepositorySQLMock(t)
	defer closeStore()

	mock.ExpectBegin()
	mock.ExpectExec("INSERT INTO `admin_app_configs`").
		WithArgs("yumi", "runtime-config", "host", "", "", true, sqlmock.AnyArg(), sqlmock.AnyArg(), true, sqlmock.AnyArg()).
		WillReturnResult(sqlmock.NewResult(9, 1))
	mock.ExpectCommit()

	if err := store.TombstoneScopedAppConfig(" YUMI ", "runtime-config", "host"); err != nil {
		t.Fatalf("write scoped tombstone failed: %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations mismatch: %v", err)
	}
}

func TestListScopedAppConfigsOverlaysBaselineAndHonorsTombstone(t *testing.T) {
	store, mock, closeStore := newRepositorySQLMock(t)
	defer closeStore()

	mock.ExpectQuery("SELECT \\* FROM `admin_app_configs` WHERE `group` = \\? AND app_code IN \\(\\?,\\?\\) ORDER BY `key` ASC, app_code ASC").
		WithArgs("runtime-config", "", "yumi").
		WillReturnRows(sqlmock.NewRows([]string{"id", "app_code", "group", "key", "value", "description", "is_deleted", "created_at_ms", "updated_at_ms"}).
			AddRow(1, "", "runtime-config", "admin", "https://legacy.example.com/admin", "Admin", false, int64(100), int64(100)).
			AddRow(2, "yumi", "runtime-config", "admin", "https://yumi.example.com/admin", "Yumi Admin", false, int64(200), int64(200)).
			AddRow(3, "", "runtime-config", "host", "https://legacy.example.com/host", "Host", false, int64(100), int64(100)).
			AddRow(4, "yumi", "runtime-config", "host", "", "", true, int64(300), int64(300)).
			AddRow(5, "", "runtime-config", "public", "https://legacy.example.com/public", "Public", false, int64(100), int64(100)))

	items, err := store.ListScopedAppConfigs(" YUMI ", "runtime-config")
	if err != nil {
		t.Fatalf("list scoped app configs failed: %v", err)
	}
	if len(items) != 2 {
		t.Fatalf("unexpected effective configs: %+v", items)
	}
	if items[0].AppCode != "yumi" || items[0].Key != "admin" || items[0].Description != "Yumi Admin" {
		t.Fatalf("override mismatch: %+v", items[0])
	}
	if items[1].AppCode != "yumi" || items[1].Key != "public" || items[1].Value != "https://legacy.example.com/public" {
		t.Fatalf("baseline projection mismatch: %+v", items[1])
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations mismatch: %v", err)
	}
}

func TestListOwnedAppConfigsDoesNotReadLegacyOrOtherAppRows(t *testing.T) {
	store, mock, closeStore := newRepositorySQLMock(t)
	defer closeStore()

	mock.ExpectQuery("SELECT \\* FROM `admin_app_configs` WHERE app_code = \\? AND `group` = \\? AND is_deleted = FALSE ORDER BY `key` ASC").
		WithArgs("yumi", "h5-links").
		WillReturnRows(sqlmock.NewRows([]string{"id", "app_code", "group", "key", "value", "description", "is_deleted", "created_at_ms", "updated_at_ms"}).
			AddRow(2, "yumi", "h5-links", "admin", "https://yumi.example.com/admin", "Yumi Admin", false, int64(200), int64(200)))

	items, err := store.ListOwnedAppConfigs(" YUMI ", "h5-links")
	if err != nil {
		t.Fatalf("list owned app configs failed: %v", err)
	}
	if len(items) != 1 || items[0].AppCode != "yumi" || items[0].Key != "admin" {
		t.Fatalf("owned config scope mismatch: %+v", items)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations mismatch: %v", err)
	}
}
