package agent

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

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/service/channeladmin"
)

// AgentChannelHandler owns the Agent Runtime channel management routes
// (`/channels...`). It is a thin transport adapter: it decodes the typed
// request, invokes the channelmanagement service with the authenticated Agent
// principal, and maps service results/errors to the wire DTOs the Desktop Exec
// client decodes. Business logic, owner resolution, idempotency, and realtime
// live in the service.
type AgentChannelHandler struct {
	channels *channeladmin.Service
}

// Wire DTOs below must match the Desktop Exec channel client JSON shapes
// exactly (see desktop/internal/api/exec/channel.go).

type agentChannelListItem struct {
	ChannelID   string `json:"channelId"`
	Slug        string `json:"slug"`
	DisplayName string `json:"displayName"`
	Description string `json:"description"`
	Visibility  string `json:"visibility"`
}

type agentChannelDetail struct {
	ChannelID   string `json:"channelId"`
	Slug        string `json:"slug"`
	DisplayName string `json:"displayName"`
	Description string `json:"description"`
	Visibility  string `json:"visibility"`
	Version     int64  `json:"version"`
}

type agentChannelMember struct {
	MemberID    string `json:"memberId"`
	DisplayName string `json:"displayName"`
	Kind        string `json:"kind"`
}

type agentChannelListResponse struct {
	Channels   []agentChannelListItem `json:"channels"`
	HasMore    bool                   `json:"hasMore"`
	NextCursor string                 `json:"nextCursor,omitempty"`
	TotalCount int                    `json:"totalCount"`
}

type agentChannelGetRequest struct {
	ChannelID string `json:"channelId"`
}

type agentChannelGetResponse struct {
	Channel agentChannelDetail `json:"channel"`
}

type agentChannelMembersRequest struct {
	ChannelID string `json:"channelId"`
	Limit     int    `json:"limit,omitempty"`
	Cursor    string `json:"cursor,omitempty"`
}

type agentChannelMembersResponse struct {
	ChannelID  string               `json:"channelId"`
	Members    []agentChannelMember `json:"members"`
	HasMore    bool                 `json:"hasMore"`
	NextCursor string               `json:"nextCursor,omitempty"`
	TotalCount int                  `json:"totalCount"`
}

type agentChannelCreateRequest struct {
	RequestID   string  `json:"requestId"`
	DisplayName string  `json:"displayName"`
	Description *string `json:"description,omitempty"`
	Visibility  string  `json:"visibility,omitempty"`
}

type agentChannelCreateResponse struct {
	Channel agentChannelDetail `json:"channel"`
}

type agentChannelUpdateRequest struct {
	RequestID       string  `json:"requestId"`
	ChannelID       string  `json:"channelId"`
	ExpectedVersion int64   `json:"expectedVersion"`
	DisplayName     *string `json:"displayName,omitempty"`
	Description     *string `json:"description,omitempty"`
	Visibility      *string `json:"visibility,omitempty"`
}

type agentChannelUpdateResponse struct {
	Channel agentChannelDetail `json:"channel"`
}

type agentChannelAgentReference struct {
	AgentID string `json:"agentId"`
}

type agentChannelAgentMutationRequest struct {
	RequestID string                       `json:"requestId"`
	ChannelID string                       `json:"channelId"`
	Agents    []agentChannelAgentReference `json:"agents"`
}

// agentChannelMutationMember is one affected agent in an agent-add/agent-remove
// result. kind is omitted (always "agent") per the agent-only contract.
type agentChannelMutationMember struct {
	MemberID    string `json:"memberId"`
	DisplayName string `json:"displayName"`
}

type agentChannelAgentMutationResponse struct {
	ChannelID string                       `json:"channelId"`
	Agents    []agentChannelMutationMember `json:"agents"`
}

func (h *AgentChannelHandler) List(request api.AgentScopedRequest) (api.Response, error) {
	if h == nil || h.channels == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Channel service is not configured")
	}
	page, err := parseAgentChannelListPage(request.Gin.Query("limit"), request.Gin.Query("cursor"))
	if err != nil {
		return api.Response{}, err
	}
	result, err := h.channels.List(request.Context(), request.Principal, page)
	if err != nil {
		return classifyChannelError(err)
	}
	channels := make([]agentChannelListItem, 0, len(result.Channels))
	for _, channel := range result.Channels {
		channels = append(channels, agentChannelListItem{
			ChannelID: channel.ChannelID, Slug: channel.Slug, DisplayName: channel.DisplayName,
			Description: channel.Description, Visibility: channel.Visibility,
		})
	}
	return api.OK(agentChannelListResponse{
		Channels:   channels,
		HasMore:    result.HasMore,
		NextCursor: result.NextCursor,
		TotalCount: result.TotalCount,
	}), nil
}

func (h *AgentChannelHandler) Get(request api.AgentScopedJSON[agentChannelGetRequest]) (api.Response, error) {
	if h == nil || h.channels == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Channel service is not configured")
	}
	channelID := strings.TrimSpace(request.Body.ChannelID)
	if channelID == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "channelId is required")
	}
	result, err := h.channels.Get(request.Context(), request.Principal, channelID)
	if err != nil {
		return classifyChannelError(err)
	}
	return api.OK(agentChannelGetResponse{Channel: channelDetailFromView(result.Channel)}), nil
}

func (h *AgentChannelHandler) Members(request api.AgentScopedJSON[agentChannelMembersRequest]) (api.Response, error) {
	if h == nil || h.channels == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Channel service is not configured")
	}
	channelID := strings.TrimSpace(request.Body.ChannelID)
	if channelID == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "channelId is required")
	}
	result, err := h.channels.Members(request.Context(), request.Principal, channelID, channeladmin.PageInput{
		Limit:  request.Body.Limit,
		Cursor: strings.TrimSpace(request.Body.Cursor),
	})
	if err != nil {
		return classifyChannelError(err)
	}
	return api.OK(agentChannelMembersResponse{
		ChannelID:  result.ChannelID,
		Members:    channelMembersFromView(result.Members),
		HasMore:    result.HasMore,
		NextCursor: result.NextCursor,
		TotalCount: result.TotalCount,
	}), nil
}

func parseAgentChannelListPage(rawLimit string, rawCursor string) (channeladmin.PageInput, error) {
	rawLimit = strings.TrimSpace(rawLimit)
	page := channeladmin.PageInput{Cursor: strings.TrimSpace(rawCursor)}
	if rawLimit == "" {
		return page, nil
	}
	limit, err := strconv.Atoi(rawLimit)
	if err != nil || limit < 1 || limit > 100 {
		return channeladmin.PageInput{}, api.NewHTTPErrorWithCode(
			http.StatusBadRequest,
			"channel limit must be an integer from 1 to 100",
			"channel_invalid_request",
		)
	}
	page.Limit = limit
	return page, nil
}

func (h *AgentChannelHandler) Create(request api.AgentScopedJSON[agentChannelCreateRequest]) (api.Response, error) {
	if h == nil || h.channels == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Channel service is not configured")
	}
	body := request.Body
	body.RequestID = strings.TrimSpace(body.RequestID)
	if body.RequestID == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "requestId is required")
	}
	result, err := h.channels.Create(request.Context(), request.Principal, channeladmin.CreateInput{
		RequestID: body.RequestID, DisplayName: strings.TrimSpace(body.DisplayName),
		Description: trimChannelPointer(body.Description), Visibility: body.Visibility,
	})
	if err != nil {
		return classifyChannelError(err)
	}
	return api.OK(agentChannelCreateResponse{Channel: channelDetailFromView(result.Channel)}), nil
}

func (h *AgentChannelHandler) Update(request api.AgentScopedJSON[agentChannelUpdateRequest]) (api.Response, error) {
	if h == nil || h.channels == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Channel service is not configured")
	}
	body := request.Body
	body.RequestID = strings.TrimSpace(body.RequestID)
	body.ChannelID = strings.TrimSpace(body.ChannelID)
	if body.RequestID == "" || body.ChannelID == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "channelId and requestId are required")
	}
	result, err := h.channels.Update(request.Context(), request.Principal, channeladmin.UpdateInput{
		RequestID: body.RequestID, ChannelID: body.ChannelID, ExpectedVersion: body.ExpectedVersion,
		DisplayName: trimChannelPointer(body.DisplayName), Description: trimChannelPointer(body.Description),
		Visibility: body.Visibility,
	})
	if err != nil {
		return classifyChannelError(err)
	}
	return api.OK(agentChannelUpdateResponse{Channel: channelDetailFromView(result.Channel)}), nil
}

func (h *AgentChannelHandler) AgentAdd(request api.AgentScopedJSON[agentChannelAgentMutationRequest]) (api.Response, error) {
	if h == nil || h.channels == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Channel service is not configured")
	}
	channelID, requestID, refs, ok := decodeChannelAgentMutation(request.Body)
	if !ok {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "channelId, requestId and agents are required")
	}
	result, err := h.channels.AgentAdd(request.Context(), request.Principal, channelID, requestID, refs)
	if err != nil {
		return classifyChannelError(err)
	}
	return api.OK(agentChannelAgentMutationResponse{ChannelID: result.ChannelID, Agents: channelMutationMembersFromView(result.Members)}), nil
}

func (h *AgentChannelHandler) AgentRemove(request api.AgentScopedJSON[agentChannelAgentMutationRequest]) (api.Response, error) {
	if h == nil || h.channels == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Channel service is not configured")
	}
	channelID, requestID, refs, ok := decodeChannelAgentMutation(request.Body)
	if !ok {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "channelId, requestId and agents are required")
	}
	result, err := h.channels.AgentRemove(request.Context(), request.Principal, channelID, requestID, refs)
	if err != nil {
		return classifyChannelError(err)
	}
	return api.OK(agentChannelAgentMutationResponse{ChannelID: result.ChannelID, Agents: channelMutationMembersFromView(result.Members)}), nil
}

func decodeChannelAgentMutation(body agentChannelAgentMutationRequest) (string, string, []channeladmin.AgentReference, bool) {
	channelID := strings.TrimSpace(body.ChannelID)
	requestID := strings.TrimSpace(body.RequestID)
	if channelID == "" || requestID == "" || len(body.Agents) == 0 {
		return "", "", nil, false
	}
	refs := make([]channeladmin.AgentReference, 0, len(body.Agents))
	for _, agent := range body.Agents {
		agentID := strings.TrimSpace(agent.AgentID)
		if agentID == "" {
			return "", "", nil, false
		}
		refs = append(refs, channeladmin.AgentReference{AgentID: agentID})
	}
	return channelID, requestID, refs, true
}

func channelDetailFromView(view channeladmin.ChannelView) agentChannelDetail {
	return agentChannelDetail{
		ChannelID: view.ChannelID, Slug: view.Slug, DisplayName: view.DisplayName,
		Description: view.Description, Visibility: view.Visibility, Version: view.Version,
	}
}

func channelMembersFromView(views []channeladmin.MemberView) []agentChannelMember {
	members := make([]agentChannelMember, 0, len(views))
	for _, view := range views {
		members = append(members, agentChannelMember{MemberID: view.MemberID, DisplayName: view.DisplayName, Kind: view.Kind})
	}
	return members
}

func channelMutationMembersFromView(views []channeladmin.MemberView) []agentChannelMutationMember {
	members := make([]agentChannelMutationMember, 0, len(views))
	for _, view := range views {
		members = append(members, agentChannelMutationMember{MemberID: view.MemberID, DisplayName: view.DisplayName})
	}
	return members
}

func trimChannelPointer(value *string) *string {
	if value == nil {
		return nil
	}
	trimmed := strings.TrimSpace(*value)
	return &trimmed
}

// classifyChannelError maps a channelmanagement service error to a stable HTTP
// response. Internal cause text never crosses the boundary.
func classifyChannelError(err error) (api.Response, error) {
	switch {
	case err == nil:
		return api.Response{}, nil
	case errors.Is(err, channeladmin.ErrValidation):
		return api.Response{}, api.NewHTTPErrorWithCode(http.StatusBadRequest, "invalid channel request", "channel_invalid_request")
	case errors.Is(err, channeladmin.ErrNotFound):
		return api.Response{}, api.NewHTTPErrorWithCode(http.StatusNotFound, "channel not found", "channel_not_found")
	case errors.Is(err, channeladmin.ErrForbidden):
		return api.Response{}, api.NewHTTPErrorWithCode(http.StatusForbidden, "channel action forbidden", "channel_forbidden")
	case errors.Is(err, channeladmin.ErrQuotaExceeded):
		return api.Response{}, api.NewHTTPErrorWithCode(http.StatusForbidden, "channel quota exceeded", "quota_exceeded")
	case errors.Is(err, channeladmin.ErrSlugConflict):
		return api.Response{}, api.NewHTTPErrorWithCode(http.StatusConflict, "channel link identifier conflict", "channel_slug_conflict")
	case errors.Is(err, channeladmin.ErrConflict):
		return api.Response{}, api.NewHTTPErrorWithCode(http.StatusConflict, "channel action conflict", "channel_conflict")
	case errors.Is(err, channeladmin.ErrVersionConflict):
		return api.Response{}, api.NewHTTPErrorWithCode(http.StatusConflict, "channel version conflict", "channel_version_conflict")
	case errors.Is(err, channeladmin.ErrMemberConflict):
		return api.Response{}, api.NewHTTPErrorWithCode(http.StatusConflict, "channel member mutation conflict", "channel_member_conflict")
	case errors.Is(err, channeladmin.ErrRequestIdConflict):
		return api.Response{}, api.NewHTTPErrorWithCode(http.StatusConflict, "channel request id conflict", "channel_idempotency_conflict")
	case errors.Is(err, channeladmin.ErrAgentOwnerMissing):
		return api.Response{}, api.NewHTTPErrorWithCode(http.StatusForbidden, "agent has no human execution owner", "channel_forbidden")
	default:
		return api.Response{}, api.NewHTTPErrorWithCodeAndCause(http.StatusInternalServerError, "channel internal error", "channel_internal", err)
	}
}

// channelAgentErrorClassifier is the route-level error classifier for the Agent
// channel routes. classifyChannelError wraps every service error in an
// api.HTTPError carrying a fixed, secret-free message (and the original cause
// for logs). Unknown errors are reduced to a fixed internal response so no
// driver/store text reaches the Agent.
func channelAgentErrorClassifier(err error) (api.ErrorResponse, bool) {
	var httpErr api.HTTPError
	if errors.As(err, &httpErr) {
		return api.ErrorResponse{Status: httpErr.Status, Message: httpErr.Message, Code: httpErr.Code}, true
	}
	return api.ErrorResponse{Status: http.StatusInternalServerError, Message: "channel internal error", Code: "channel_internal"}, true
}
