package repository

import (
	"testing"

	"hyapp-admin-server/internal/model"

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

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

	mock.ExpectQuery("SELECT `id`,`app_scope_mode` FROM `admin_users` WHERE `admin_users`.`id` = \\? ORDER BY `admin_users`.`id` LIMIT \\?").
		WillReturnRows(sqlmock.NewRows([]string{"id", "app_scope_mode"}).AddRow(7, model.UserAppScopeModeSelected))
	mock.ExpectQuery("SELECT \\* FROM `admin_user_app_scopes` WHERE user_id = \\? ORDER BY app_code ASC").
		WillReturnRows(sqlmock.NewRows([]string{"id", "user_id", "app_code", "created_at_ms", "updated_at_ms"}).
			AddRow(1, 7, "huwaa", int64(1), int64(1)).
			AddRow(2, 7, "lalu", int64(1), int64(1)))

	access, err := store.AppAccessForUser(7)
	if err != nil {
		t.Fatalf("get app access: %v", err)
	}
	if !access.Allows("LALU") || !access.Allows("huwaa") || access.Allows("fami") {
		t.Fatalf("unexpected selected access: %+v", access)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	mock.ExpectBegin()
	mock.ExpectQuery("SELECT \\* FROM `admin_users` WHERE `admin_users`.`id` = \\? ORDER BY `admin_users`.`id` LIMIT \\?").
		WillReturnRows(sqlmock.NewRows([]string{"id", "app_scope_mode"}).AddRow(9, model.UserAppScopeModeNone))
	mock.ExpectExec("DELETE FROM `admin_user_app_scopes` WHERE user_id = \\?").WillReturnResult(sqlmock.NewResult(0, 0))
	mock.ExpectExec("INSERT INTO `admin_user_app_scopes`").WillReturnResult(sqlmock.NewResult(1, 2))
	mock.ExpectExec("UPDATE `admin_users` SET `app_scope_mode`=\\?,`updated_at_ms`=\\? WHERE `id` = \\?").WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectCommit()

	access, err := store.ReplaceUserAppScopes(9, model.UserAppScopeModeSelected, []string{" LALU ", "huwaa", "lalu", ""})
	if err != nil {
		t.Fatalf("replace app scopes: %v", err)
	}
	if len(access.AppCodes) != 2 || access.AppCodes[0] != "huwaa" || access.AppCodes[1] != "lalu" {
		t.Fatalf("unexpected normalized access: %+v", access)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	if _, err := store.ReplaceUserAppScopes(9, model.UserAppScopeModeSelected, nil); err == nil {
		t.Fatal("expected selected mode without app codes to fail")
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	mock.ExpectQuery("(?s)SELECT admin_users\\.username,.*FROM admin_users.*WHERE admin_users\\.id = \\?.*ORDER BY admin_permissions\\.code ASC").
		WithArgs(uint(7)).
		WillReturnRows(sqlmock.NewRows([]string{"username", "status", "permission_code"}).
			AddRow("ops", model.UserStatusActive, "operations-withdrawal:audit").
			AddRow("ops", model.UserStatusActive, "operations-withdrawal:audit").
			AddRow("ops", model.UserStatusActive, "operations-withdrawal:view"))

	authorization, err := store.CurrentAuthorizationForUser(7)
	if err != nil {
		t.Fatalf("resolve current authorization: %v", err)
	}
	if authorization.Username != "ops" || authorization.Status != model.UserStatusActive {
		t.Fatalf("unexpected current user state: %+v", authorization)
	}
	if len(authorization.Permissions) != 2 ||
		authorization.Permissions[0] != "operations-withdrawal:audit" ||
		authorization.Permissions[1] != "operations-withdrawal:view" {
		t.Fatalf("unexpected live permissions: %+v", authorization.Permissions)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}
