package user

import (
	"testing"

	"system-altrak/internal/domain"
	actModule "system-altrak/internal/modules/activity"
	"system-altrak/pkg/utils"
)

type userRepoMock struct {
	createUser *domain.User
	updateUser *domain.User
	getUser    *domain.User
	createErr  error
	updateErr  error
	getErr     error
	deleteErr  error
	countErr   error
	countValue int64
}

func (m *userRepoMock) List() ([]domain.User, error) { return nil, nil }

func (m *userRepoMock) GetByID(id uint) (*domain.User, error) {
	if m.getErr != nil {
		return nil, m.getErr
	}
	if m.getUser != nil {
		copyUser := *m.getUser
		copyUser.ID = id
		return &copyUser, nil
	}
	return &domain.User{BaseModel: domain.BaseModel{ID: id}, FullName: "Existing User", Username: "existing", Role: "admin", BranchID: 7, PasswordHash: "old-hash"}, nil
}

func (m *userRepoMock) Create(user *domain.User) error {
	if m.createErr != nil {
		return m.createErr
	}
	copyUser := *user
	m.createUser = &copyUser
	return nil
}

func (m *userRepoMock) Update(user *domain.User) error {
	if m.updateErr != nil {
		return m.updateErr
	}
	copyUser := *user
	m.updateUser = &copyUser
	return nil
}

func (m *userRepoMock) Delete(id uint) error { return m.deleteErr }

func (m *userRepoMock) CountByRole(role string) (int64, error) { return m.countValue, m.countErr }

type activityStub struct{}

func (activityStub) List(branchID uint, role string) ([]domain.ActivityLog, error) { return nil, nil }
func (activityStub) Dashboard(branchID uint, role string) (*actModule.DashboardResponse, error) {
	return &actModule.DashboardResponse{}, nil
}
func (activityStub) Restore(resource string, id uint, branchID uint, role string) error { return nil }
func (activityStub) Log(userID uint, username, role, module, action, details, ip, userAgent string, branchID uint) error {
	return nil
}

func TestCreateDefaultsBranchAndTrimsFields(t *testing.T) {
	repo := &userRepoMock{}
	svc := NewService(repo, activityStub{})

	user := &domain.User{FullName: "  Alice  ", Username: "  alice01  ", PasswordHash: "secret123", Role: "manager"}
	if err := svc.Create(user, "127.0.0.1", "unit-test"); err != nil {
		t.Fatalf("Create returned error: %v", err)
	}

	if repo.createUser == nil {
		t.Fatal("expected create to reach repository")
	}
	if repo.createUser.FullName != "Alice" || repo.createUser.Username != "alice01" {
		t.Fatalf("unexpected trimmed create payload: full_name=%q username=%q", repo.createUser.FullName, repo.createUser.Username)
	}
	if repo.createUser.BranchID != 1 {
		t.Fatalf("expected default branch 1, got=%d", repo.createUser.BranchID)
	}
	if repo.createUser.PasswordHash == "secret123" || repo.createUser.PasswordHash == "" {
		t.Fatal("password was not hashed during create")
	}
	if !utils.CheckPasswordHash("secret123", repo.createUser.PasswordHash) {
		t.Fatal("hashed password does not validate against original secret")
	}
}

func TestUpdatePreservesBranchWhenMissingAndHashesPassword(t *testing.T) {
	repo := &userRepoMock{getUser: &domain.User{BaseModel: domain.BaseModel{ID: 9}, FullName: "Old", Username: "old_user", Role: "admin", BranchID: 7, PasswordHash: "old-hash"}}
	svc := NewService(repo, activityStub{})

	user := &domain.User{BaseModel: domain.BaseModel{ID: 9}, FullName: "  Bob  ", Username: "  bob_user  ", PasswordHash: "newsecret", Role: "admin"}
	if err := svc.Update(user, "127.0.0.1", "unit-test"); err != nil {
		t.Fatalf("Update returned error: %v", err)
	}

	if repo.updateUser == nil {
		t.Fatal("expected update to reach repository")
	}
	if repo.updateUser.BranchID != 7 {
		t.Fatalf("expected branch to be preserved, got=%d", repo.updateUser.BranchID)
	}
	if repo.updateUser.FullName != "Bob" || repo.updateUser.Username != "bob_user" {
		t.Fatalf("unexpected trimmed update payload: full_name=%q username=%q", repo.updateUser.FullName, repo.updateUser.Username)
	}
	if repo.updateUser.PasswordHash == "newsecret" || repo.updateUser.PasswordHash == "" {
		t.Fatal("password was not hashed during update")
	}
	if !utils.CheckPasswordHash("newsecret", repo.updateUser.PasswordHash) {
		t.Fatal("hashed update password does not validate against original secret")
	}
}

func TestUpdateReturnsErrorForInvalidRole(t *testing.T) {
	repo := &userRepoMock{getUser: &domain.User{BaseModel: domain.BaseModel{ID: 2}, BranchID: 4, PasswordHash: "old-hash"}}
	svc := NewService(repo, activityStub{})

	err := svc.Update(&domain.User{BaseModel: domain.BaseModel{ID: 2}, FullName: "User", Username: "user01", Role: "invalid"}, "127.0.0.1", "unit-test")
	if err == nil {
		t.Fatal("expected invalid role error")
	}
	if err.Error() != "invalid role value" {
		t.Fatalf("unexpected error: %v", err)
	}
}
