package service

import (
	"fmt"
	"strings"
	"testing"

	"github.com/tidbcloud/aisupport/services/oncall/db"
	"github.com/tidbcloud/aisupport/services/oncall/models"
)

// setupOverridePermissionFixture creates a team with one manager and one
// plain member, returning the team id and their emails. Names/emails are
// suffixed with the test name since setupTeamPermissionDB uses a shared
// (not per-test-isolated) sqlite database.
func setupOverridePermissionFixture(t *testing.T) (teamID uint, managerEmail, memberEmail, outsiderEmail string) {
	t.Helper()
	setupTeamPermissionDB(t)

	suffix := strings.ToLower(strings.NewReplacer("/", "-", " ", "-").Replace(t.Name()))

	team := models.Team{Name: "rot-" + suffix, DisplayName: "Team Rot"}
	if err := db.DB.Create(&team).Error; err != nil {
		t.Fatalf("create team: %v", err)
	}

	manager := models.Engineer{Name: "Manager Mike", Email: fmt.Sprintf("manager-%s@pingcap.com", suffix)}
	member := models.Engineer{Name: "Member Mary", Email: fmt.Sprintf("member-%s@pingcap.com", suffix)}
	if err := db.DB.Create(&manager).Error; err != nil {
		t.Fatalf("create manager engineer: %v", err)
	}
	if err := db.DB.Create(&member).Error; err != nil {
		t.Fatalf("create member engineer: %v", err)
	}
	if err := db.DB.Create(&models.EngineerTeam{EngineerID: manager.ID, TeamID: team.ID, Role: models.TeamRoleManager}).Error; err != nil {
		t.Fatalf("link manager: %v", err)
	}
	if err := db.DB.Create(&models.EngineerTeam{EngineerID: member.ID, TeamID: team.ID, Role: models.TeamRoleMember}).Error; err != nil {
		t.Fatalf("link member: %v", err)
	}

	return team.ID, manager.Email, member.Email, fmt.Sprintf("outsider-%s@pingcap.com", suffix)
}

func TestIsTeamMember(t *testing.T) {
	teamID, managerEmail, memberEmail, outsiderEmail := setupOverridePermissionFixture(t)
	s := &ScheduleService{}

	if ok, err := s.IsTeamMember(teamID, managerEmail); err != nil || !ok {
		t.Fatalf("expected manager to be a team member, ok=%v err=%v", ok, err)
	}
	if ok, err := s.IsTeamMember(teamID, memberEmail); err != nil || !ok {
		t.Fatalf("expected plain member to be a team member, ok=%v err=%v", ok, err)
	}
	if ok, err := s.IsTeamMember(teamID, outsiderEmail); err != nil || ok {
		t.Fatalf("expected outsider to not be a team member, ok=%v err=%v", ok, err)
	}

	if err := db.DB.Create(&models.SuperAdmin{Email: "root@pingcap.com", CreatedBy: "test"}).Error; err != nil {
		t.Fatalf("create super admin: %v", err)
	}
	if ok, err := s.IsTeamMember(teamID, "root@pingcap.com"); err != nil || !ok {
		t.Fatalf("expected super admin to count as a team member, ok=%v err=%v", ok, err)
	}
}

func TestEnsureTeamOverridePermission(t *testing.T) {
	teamID, managerEmail, memberEmail, outsiderEmail := setupOverridePermissionFixture(t)
	s := &ScheduleService{}

	cases := []struct {
		name    string
		email   string
		targets []string
		wantErr bool
	}{
		{"manager can assign to anyone", managerEmail, []string{memberEmail}, false},
		{"manager can clear to no one", managerEmail, []string{}, false},
		{"member can assign to self", memberEmail, []string{memberEmail}, false},
		{"member self-assign is case/whitespace insensitive", memberEmail, []string{"  " + strings.ToUpper(memberEmail) + "  "}, false},
		{"member cannot assign to someone else", memberEmail, []string{managerEmail}, true},
		{"member cannot assign to self plus someone else", memberEmail, []string{memberEmail, managerEmail}, true},
		{"member cannot clear to no one", memberEmail, []string{}, true},
		{"outsider cannot assign to self", outsiderEmail, []string{outsiderEmail}, true},
		{"outsider cannot assign to a team member", outsiderEmail, []string{memberEmail}, true},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			err := s.EnsureTeamOverridePermission(teamID, c.email, c.targets)
			if c.wantErr && err == nil {
				t.Fatalf("expected error, got nil")
			}
			if !c.wantErr && err != nil {
				t.Fatalf("expected no error, got: %v", err)
			}
		})
	}
}
