package team

import (
	"net/http"
	"strconv"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	storeteam "github.com/tidbcloud/AgentTeam/internal/store/team"
)

// Handler owns Team management and picker routes.
type Handler struct {
	store Store
}

func (h *Handler) List(r api.WorkspaceScopedRequest) (api.Response, error) {
	limit, err := parseTeamLimit(r.Gin.Query("limit"), storeteam.DefaultTeamDirectoryLimit, storeteam.MaxTeamDirectoryLimit)
	if err != nil {
		return api.Response{}, err
	}
	teams, err := h.store.ListTeamsForUser(r.Context(), r.UserID(), r.WorkspaceID(), storeteam.ListTeamsFilter{
		Query:  firstNonEmpty(r.Gin.Query("q"), r.Gin.Query("query")),
		Status: r.Gin.Query("status"),
		Limit:  limit,
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(dto.TeamListResponse{Teams: teamResponses(teams)}), nil
}

func (h *Handler) Get(r api.WorkspaceScopedRequest) (api.Response, error) {
	teamID, err := requireParam(r, "teamId")
	if err != nil {
		return api.Response{}, err
	}
	team, err := h.store.GetTeamForUser(r.Context(), r.UserID(), r.WorkspaceID(), teamID)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(teamResponse(team)), nil
}

func (h *Handler) Create(r api.WorkspaceScopedJSON[dto.CreateTeamRequest]) (api.Response, error) {
	request := r.Body
	name := requestTeamName(request.Handle, request.DisplayName)
	team, err := h.store.CreateTeamForUser(r.Context(), r.UserID(), r.WorkspaceID(), storeteam.CreateTeamParams{
		Handle:         name,
		DisplayName:    name,
		Description:    request.Description,
		AvatarURL:      request.AvatarURL,
		Visibility:     request.Visibility,
		InitialMembers: initialTeamMemberParams(request.InitialMembers),
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.Created(teamResponse(team)), nil
}

func (h *Handler) Update(r api.WorkspaceScopedJSON[dto.UpdateTeamRequest]) (api.Response, error) {
	teamID, err := requireParam(r.WorkspaceScopedRequest, "teamId")
	if err != nil {
		return api.Response{}, err
	}
	request := r.Body
	handle, displayName := requestTeamNamePatch(request.Handle, request.DisplayName)
	team, err := h.store.UpdateTeamForUser(r.Context(), r.UserID(), r.WorkspaceID(), teamID, storeteam.UpdateTeamParams{
		Handle:      handle,
		DisplayName: displayName,
		Description: request.Description,
		AvatarURL:   request.AvatarURL,
		Visibility:  request.Visibility,
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(teamResponse(team)), nil
}

func (h *Handler) Delete(r api.WorkspaceScopedRequest) (api.Response, error) {
	teamID, err := requireParam(r, "teamId")
	if err != nil {
		return api.Response{}, err
	}
	if err := h.store.DeleteTeamForUser(r.Context(), r.UserID(), r.WorkspaceID(), teamID); err != nil {
		return api.Response{}, err
	}
	return api.NoContent(), nil
}

func (h *Handler) ListTemplates(r api.WorkspaceScopedRequest) (api.Response, error) {
	limit, err := parseTeamLimit(r.Gin.Query("limit"), storeteam.DefaultTeamTemplateLimit, storeteam.MaxTeamTemplateLimit)
	if err != nil {
		return api.Response{}, err
	}
	templates, err := h.store.ListTeamTemplatesForUser(r.Context(), r.UserID(), r.WorkspaceID(), storeteam.ListTeamTemplateFilter{
		Query:  firstNonEmpty(r.Gin.Query("q"), r.Gin.Query("query")),
		Limit:  limit,
		Locale: r.Gin.Query("template_locale"),
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(dto.TeamTemplateListResponse{Templates: teamTemplateResponses(templates)}), nil
}

func (h *Handler) CreateTemplate(r api.WorkspaceScopedJSON[dto.CreateTeamTemplateRequest]) (api.Response, error) {
	request := r.Body
	template, err := h.store.CreateTeamTemplateForUser(r.Context(), r.UserID(), r.WorkspaceID(), storeteam.CreateTeamTemplateParams{
		Name:        request.Name,
		Description: request.Description,
		Visibility:  request.Visibility,
		Scope:       request.Scope,
		Members:     createTemplateMemberParams(request.Members),
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.Created(teamTemplateResponse(template)), nil
}

func (h *Handler) SaveTemplateFromTeam(r api.WorkspaceScopedJSON[dto.SaveTeamTemplateFromTeamRequest]) (api.Response, error) {
	teamID, err := requireParam(r.WorkspaceScopedRequest, "teamId")
	if err != nil {
		return api.Response{}, err
	}
	template, err := h.store.SaveTeamTemplateFromTeamForUser(r.Context(), r.UserID(), r.WorkspaceID(), teamID, r.Body.Name, r.Body.Description, r.Body.Scope)
	if err != nil {
		return api.Response{}, err
	}
	return api.Created(teamTemplateResponse(template)), nil
}

func (h *Handler) UpdateTemplate(r api.WorkspaceScopedJSON[dto.UpdateTeamTemplateRequest]) (api.Response, error) {
	templateID, err := requireParam(r.WorkspaceScopedRequest, "templateId")
	if err != nil {
		return api.Response{}, err
	}
	request := r.Body
	template, err := h.store.UpdateTeamTemplateForUser(r.Context(), r.UserID(), r.WorkspaceID(), templateID, storeteam.UpdateTeamTemplateParams{
		Name:        request.Name,
		Description: request.Description,
		Scope:       request.Scope,
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(teamTemplateResponse(template)), nil
}

func (h *Handler) DeleteTemplate(r api.WorkspaceScopedRequest) (api.Response, error) {
	templateID, err := requireParam(r, "templateId")
	if err != nil {
		return api.Response{}, err
	}
	if err := h.store.DeleteTeamTemplateForUser(r.Context(), r.UserID(), r.WorkspaceID(), templateID); err != nil {
		return api.Response{}, err
	}
	return api.NoContent(), nil
}

func (h *Handler) InstantiateTemplate(r api.WorkspaceScopedJSON[dto.InstantiateTeamTemplateRequest]) (api.Response, error) {
	templateID, err := requireParam(r.WorkspaceScopedRequest, "templateId")
	if err != nil {
		return api.Response{}, err
	}
	request := r.Body
	templateLocale := strings.TrimSpace(r.Gin.Query("template_locale"))
	name := requestTeamName(request.Handle, request.DisplayName)
	bindings := templateMemberBindings(request.MemberBindings)

	instantiated, err := h.store.InstantiateTeamTemplate(r.Context(), r.UserID(), r.WorkspaceID(), storeteam.InstantiateTeamTemplateParams{
		TemplateID:     templateID,
		Handle:         name,
		DisplayName:    name,
		Description:    request.Description,
		Visibility:     request.Visibility,
		MemberBindings: bindings,
		InitialMembers: initialTeamMemberParams(request.InitialMembers),
		Locale:         templateLocale,
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.Created(instantiatedTeamResponse(instantiated)), nil
}

func (h *Handler) ListMembers(r api.WorkspaceScopedRequest) (api.Response, error) {
	teamID, err := requireParam(r, "teamId")
	if err != nil {
		return api.Response{}, err
	}
	members, err := h.store.ListTeamMembersForUser(r.Context(), r.UserID(), r.WorkspaceID(), teamID)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(dto.TeamMemberListResponse{Members: teamMemberResponses(members)}), nil
}

func (h *Handler) AddMember(r api.WorkspaceScopedJSON[dto.AddTeamMemberRequest]) (api.Response, error) {
	teamID, err := requireParam(r.WorkspaceScopedRequest, "teamId")
	if err != nil {
		return api.Response{}, err
	}
	request := r.Body
	member, err := h.store.AddTeamMemberForUser(r.Context(), r.UserID(), r.WorkspaceID(), teamID, storeteam.AddTeamMemberParams{
		ActorKind:         request.ActorKind,
		ActorID:           request.ActorID,
		Role:              request.Role,
		RoleLabel:         request.RoleLabel,
		RoleOverlayPrompt: request.RoleOverlayPrompt,
		WakeOnTeamMention: request.WakeOnTeamMention,
		SortOrder:         request.SortOrder,
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.Created(teamMemberResponse(member)), nil
}

func (h *Handler) UpdateMember(r api.WorkspaceScopedJSON[dto.UpdateTeamMemberRequest]) (api.Response, error) {
	teamID, err := requireParam(r.WorkspaceScopedRequest, "teamId")
	if err != nil {
		return api.Response{}, err
	}
	teamMemberID, err := requireParam(r.WorkspaceScopedRequest, "teamMemberId")
	if err != nil {
		return api.Response{}, err
	}
	request := r.Body
	member, err := h.store.UpdateTeamMemberForUser(r.Context(), r.UserID(), r.WorkspaceID(), teamID, teamMemberID, storeteam.UpdateTeamMemberParams{
		Role:              request.Role,
		RoleLabel:         request.RoleLabel,
		RoleOverlayPrompt: request.RoleOverlayPrompt,
		WakeOnTeamMention: request.WakeOnTeamMention,
		SortOrder:         request.SortOrder,
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(teamMemberResponse(member)), nil
}

func (h *Handler) RemoveMember(r api.WorkspaceScopedRequest) (api.Response, error) {
	teamID, err := requireParam(r, "teamId")
	if err != nil {
		return api.Response{}, err
	}
	teamMemberID, err := requireParam(r, "teamMemberId")
	if err != nil {
		return api.Response{}, err
	}
	if err := h.store.RemoveTeamMemberForUser(r.Context(), r.UserID(), r.WorkspaceID(), teamID, teamMemberID); err != nil {
		return api.Response{}, err
	}
	return api.NoContent(), nil
}

func (h *Handler) MentionCandidates(r api.WorkspaceScopedRequest) (api.Response, error) {
	limit, err := parseTeamLimit(r.Gin.Query("limit"), storeteam.DefaultTeamMentionCandidateLimit, storeteam.MaxTeamMentionCandidateLimit)
	if err != nil {
		return api.Response{}, err
	}
	candidates, err := h.store.ListTeamMentionCandidatesForUser(
		r.Context(),
		r.UserID(),
		r.WorkspaceID(),
		firstNonEmpty(r.Gin.Query("q"), r.Gin.Query("query")),
		limit,
		firstNonEmpty(r.Gin.Query("scopeId"), r.Gin.Query("scopeID")),
	)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(dto.TeamMentionCandidateListResponse{Teams: mentionCandidateResponses(candidates)}), nil
}

func requestTeamName(handle string, displayName string) string {
	if strings.TrimSpace(handle) != "" {
		return handle
	}
	return displayName
}

func requestTeamNamePatch(handle *string, displayName *string) (*string, *string) {
	if handle == nil && displayName == nil {
		return nil, nil
	}
	name := ""
	if handle != nil {
		name = *handle
	} else {
		name = *displayName
	}
	return &name, &name
}

func requireParam(r api.WorkspaceScopedRequest, name string) (string, error) {
	value := strings.TrimSpace(r.Gin.Param(name))
	if value == "" {
		return "", api.NewHTTPError(http.StatusBadRequest, name+" is required")
	}
	return value, nil
}

func parseTeamLimit(raw string, defaultValue int, maxValue int) (int, error) {
	trimmed := strings.TrimSpace(raw)
	if trimmed == "" {
		return defaultValue, nil
	}
	limit, err := strconv.Atoi(trimmed)
	if err != nil || limit <= 0 {
		return 0, api.NewHTTPError(http.StatusBadRequest, "limit must be a positive integer")
	}
	if limit > maxValue {
		return maxValue, nil
	}
	return limit, nil
}

func firstNonEmpty(values ...string) string {
	for _, value := range values {
		if strings.TrimSpace(value) != "" {
			return value
		}
	}
	return ""
}

func createTemplateMemberParams(requests []dto.CreateTeamTemplateMemberRequest) []storeteam.CreateTeamTemplateMemberParams {
	if len(requests) == 0 {
		return nil
	}
	params := make([]storeteam.CreateTeamTemplateMemberParams, 0, len(requests))
	for _, request := range requests {
		params = append(params, storeteam.CreateTeamTemplateMemberParams{
			ActorKind:         request.ActorKind,
			ActorID:           request.ActorID,
			Role:              request.Role,
			RoleLabel:         request.RoleLabel,
			RoleOverlayPrompt: request.RoleOverlayPrompt,
			WakeOnTeamMention: request.WakeOnTeamMention,
			SortOrder:         request.SortOrder,
		})
	}
	return params
}

func initialTeamMemberParams(requests []dto.AddTeamMemberRequest) []storeteam.AddTeamMemberParams {
	if len(requests) == 0 {
		return nil
	}
	params := make([]storeteam.AddTeamMemberParams, 0, len(requests))
	for _, request := range requests {
		params = append(params, storeteam.AddTeamMemberParams{
			ActorKind:         request.ActorKind,
			ActorID:           request.ActorID,
			Role:              request.Role,
			RoleLabel:         request.RoleLabel,
			RoleOverlayPrompt: request.RoleOverlayPrompt,
			WakeOnTeamMention: request.WakeOnTeamMention,
			SortOrder:         request.SortOrder,
		})
	}
	return params
}

func templateMemberBindings(requests []dto.TeamTemplateMemberBindingRequest) []storeteam.TeamTemplateMemberBinding {
	if len(requests) == 0 {
		return nil
	}
	bindings := make([]storeteam.TeamTemplateMemberBinding, 0, len(requests))
	for _, request := range requests {
		binding := storeteam.TeamTemplateMemberBinding{
			TemplateMemberID:     request.TemplateMemberID,
			ActorID:              request.ActorID,
			RoleSet:              request.Role != nil,
			RoleLabelSet:         request.RoleLabel != nil,
			RoleOverlayPromptSet: request.RoleOverlayPrompt != nil,
			WakeOnTeamMention:    request.WakeOnTeamMention,
		}
		if request.Role != nil {
			binding.Role = *request.Role
		}
		if request.RoleLabel != nil {
			binding.RoleLabel = *request.RoleLabel
		}
		if request.RoleOverlayPrompt != nil {
			binding.RoleOverlayPrompt = *request.RoleOverlayPrompt
		}
		bindings = append(bindings, binding)
	}
	return bindings
}
