package service

import (
	"errors"
	"github.com/tidbcloud/aisupport/services/oncall/db"
	"github.com/tidbcloud/aisupport/services/oncall/models"
	"strings"

	"gorm.io/gorm"
)

// FindTeamIDByName resolves a team's unique immutable slug (name) to its numeric id.
func (s *ScheduleService) FindTeamIDByName(name string) (uint, error) {
	n := strings.TrimSpace(name)
	if n == "" {
		return 0, errors.New("missing name")
	}
	var t models.Team
	if err := db.DB.Where("name = ?", n).First(&t).Error; err != nil {
		return 0, err
	}
	return t.ID, nil
}

func (s *ScheduleService) GetTeamDetail(teamID uint) (map[string]interface{}, error) {
	var cat models.Team
	if err := db.DB.First(&cat, teamID).Error; err != nil {
		return nil, err
	}

	availables, err := s.getTeamAvailableEngineers(teamID)
	if err != nil {
		return nil, err
	}

	return map[string]interface{}{
		"team":       cat,
		"availables": availables,
		"is_manager": false,
	}, nil
}

type UpdateTeamRequest struct {
	Name           string
	DisplayName    *string
	GroupNames     *[]string
	SortPriority   *int
	Managers       []string
	Description    *string
	FeishuChatID   *string
	ValidateFeishu bool
	ValidateDomain bool
}

func (s *ScheduleService) UpdateTeam(teamID uint, req UpdateTeamRequest) error {
	var current models.Team
	if err := db.DB.First(&current, teamID).Error; err != nil {
		return err
	}

	updates := map[string]interface{}{}
	if strings.TrimSpace(req.Name) != "" {
		keyName := normalizeTeamKeyName(req.Name)
		if err := validateTeamKeyName(keyName); err != nil {
			return err
		}
		if keyName != strings.TrimSpace(current.Name) {
			return errors.New("name cannot be modified after team creation")
		}
	}
	if req.DisplayName != nil {
		display := strings.TrimSpace(*req.DisplayName)
		if display == "" {
			return errors.New("display_name cannot be empty")
		}
		updates["display_name"] = display
	}
	if req.GroupNames != nil {
		groups := normalizeGroups(*req.GroupNames)
		updates["group_names"] = groups
		updates["group_name"] = groups[0] // keep legacy column populated
	}
	if req.SortPriority != nil {
		updates["sort_priority"] = *req.SortPriority
	}

	if req.Description != nil {
		updates["description"] = strings.TrimSpace(*req.Description)
	}
	if req.FeishuChatID != nil {
		updates["feishu_chat_id"] = strings.TrimSpace(*req.FeishuChatID)
	}

	managerEmails := uniqEmails(req.Managers)
	if len(updates) == 0 && len(managerEmails) == 0 {
		return errors.New("no updatable fields provided")
	}

	return db.DB.Transaction(func(tx *gorm.DB) error {
		if len(updates) > 0 {
			if err := tx.Model(&models.Team{}).Where("id = ?", teamID).Updates(updates).Error; err != nil {
				return err
			}
		}

		if len(managerEmails) == 0 {
			return nil
		}

		var links []models.EngineerTeam
		if err := tx.Where("team_id = ?", teamID).Find(&links).Error; err != nil {
			return err
		}
		memberSet := map[uint]struct{}{}
		for _, link := range links {
			memberSet[link.EngineerID] = struct{}{}
		}
		managerSet := map[uint]struct{}{}
		for _, email := range managerEmails {
			if req.ValidateDomain && !IsEmailAllowed(email) {
				return errors.New("email domain not allowed: " + email)
			}
			if req.ValidateFeishu {
				exists, err := CheckFeishuUserExists(email)
				if err != nil {
					return err
				}
				if !exists {
					return errors.New("manager not found in Feishu: " + email)
				}
			}
			engID, err := s.resolveEngineerID(0, email, true)
			if err != nil {
				return err
			}
			managerSet[engID] = struct{}{}
		}

		// Managers is a full replacement of the manager set. Engineers dropped
		// from it fall back to plain members, but anyone who was never a manager
		// keeps their role — this must not quietly strip the team's editors.
		for _, link := range links {
			_, wantManager := managerSet[link.EngineerID]
			isManager := link.HasRole(models.TeamRoleManager)
			if wantManager == isManager {
				continue
			}
			role := models.TeamRoleMember
			if wantManager {
				role = models.TeamRoleManager
			}
			if err := setMemberRole(tx, link.EngineerID, teamID, role); err != nil {
				return err
			}
		}
		for engID := range managerSet {
			if _, ok := memberSet[engID]; ok {
				continue
			}
			if err := setMemberRole(tx, engID, teamID, models.TeamRoleManager); err != nil {
				return err
			}
		}

		return nil
	})
}

func (s *ScheduleService) GetTeamWithMembers(teamID uint) (*models.Team, []models.Engineer, error) {
	var team models.Team
	if err := db.DB.First(&team, teamID).Error; err != nil {
		return nil, nil, err
	}
	var links []models.EngineerTeam
	if err := db.DB.Preload("Engineer").Where("team_id = ?", teamID).Find(&links).Error; err != nil {
		return nil, nil, err
	}
	engineers := make([]models.Engineer, 0, len(links))
	for _, link := range links {
		engineers = append(engineers, link.Engineer)
	}
	return &team, engineers, nil
}
