package task

import (
	"context"
	"net/http"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/internal/service/collaborator"
	"github.com/tidbcloud/AgentTeam/internal/store/taskboard"
)

// CollaboratorRequestService is the API port for the Human collaborator-request
// lifecycle. The requester identity is always the authenticated Human (user id);
// the execution owner is derived server-side.
type CollaboratorRequestService interface {
	Submit(context.Context, collaborator.SubmitCommand) (collaborator.SubmitResult, error)
	Cancel(context.Context, collaborator.CancelCommand) (collaborator.DecideResult, error)
	ListRequesterRequests(context.Context, string, string) ([]taskboard.CollaboratorRequest, error)
}

// CollaboratorRequestHandler owns the collaborator-request submit/status/cancel
// routes.
type CollaboratorRequestHandler struct {
	service CollaboratorRequestService
}

// Submit handles POST /api/v1/workspaces/:workspaceId/collaborator-requests.
// It creates (or replays) a request to become a collaborator.
func (handler *CollaboratorRequestHandler) Submit(r api.WorkspaceScopedJSON[dto.CollaboratorRequestSubmitRequest]) (api.Response, error) {
	if handler == nil || handler.service == nil {
		return api.Response{}, api.NewHTTPError(http.StatusNotImplemented, "Collaborator requests are unavailable")
	}
	body := r.Body
	body.ScopeType = strings.TrimSpace(body.ScopeType)
	body.ScopeID = strings.TrimSpace(body.ScopeID)
	body.Role = strings.TrimSpace(body.Role)
	body.ClientRequestID = strings.TrimSpace(body.ClientRequestID)
	if body.ScopeType == "" || body.ScopeID == "" || body.ClientRequestID == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "scopeType, scopeId, and clientRequestId are required")
	}
	result, err := handler.service.Submit(r.Context(), collaborator.SubmitCommand{
		WorkspaceID:           r.WorkspaceID(),
		RequesterUserID:       r.User.User.ID,
		ScopeType:             body.ScopeType,
		ScopeID:               body.ScopeID,
		Role:                  body.Role,
		CollaboratorExpiresAt: body.CollaboratorExpiresAt,
		ClientRequestID:       body.ClientRequestID,
		Now:                   time.Now().UTC(),
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(dto.CollaboratorRequestSubmitResponse{
		ApprovalID: result.ApprovalID,
		RequestID:  result.RequestID,
		Status:     result.Status,
		Created:    result.Created,
	}), nil
}

// List handles GET /api/v1/workspaces/:workspaceId/collaborator-requests.
// It returns the authenticated requester's collaborator request statuses.
func (handler *CollaboratorRequestHandler) List(r api.WorkspaceScopedRequest) (api.Response, error) {
	if handler == nil || handler.service == nil {
		return api.Response{}, api.NewHTTPError(http.StatusNotImplemented, "Collaborator requests are unavailable")
	}
	requests, err := handler.service.ListRequesterRequests(r.Context(), r.WorkspaceID(), r.User.User.ID)
	if err != nil {
		return api.Response{}, err
	}
	response := dto.CollaboratorRequestListResponse{
		Requests: make([]dto.CollaboratorRequestStatus, 0, len(requests)),
	}
	for _, request := range requests {
		response.Requests = append(response.Requests, dto.CollaboratorRequestStatus{
			RequestID:             request.ID,
			ApprovalID:            request.ApprovalID,
			RequesterMemberID:     request.RequesterMemberID,
			OwnerMemberID:         request.OwnerMemberID,
			ScopeType:             request.ScopeType,
			ScopeID:               request.ScopeID,
			Role:                  request.Role,
			CollaboratorExpiresAt: request.CollaboratorExpiresAt,
			ApprovalExpiresAt:     request.ApprovalExpiresAt,
			Status:                request.Status,
			GrantedAt:             request.GrantedAt,
			CreatedAt:             request.CreatedAt,
		})
	}
	return api.OK(response), nil
}

// Cancel handles POST /api/v1/workspaces/:workspaceId/collaborator-requests/:approvalId/cancel.
// It cancels a pending request by its applicant.
func (handler *CollaboratorRequestHandler) Cancel(r api.WorkspaceScopedJSON[dto.CollaboratorRequestCancelRequest]) (api.Response, error) {
	if handler == nil || handler.service == nil {
		return api.Response{}, api.NewHTTPError(http.StatusNotImplemented, "Collaborator requests are unavailable")
	}
	approvalID := strings.TrimSpace(r.Gin.Param("approvalId"))
	clientCancellationID := strings.TrimSpace(r.Body.ClientCancellationID)
	if approvalID == "" || clientCancellationID == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "approvalId and clientCancellationId are required")
	}
	// The expected version is resolved server-side; we use the caller's
	// authoritative expected version fetched from the list surface. A stale
	// version conflicts and surfaces to the caller as a retryable error.
	result, err := handler.service.Cancel(r.Context(), collaborator.CancelCommand{
		WorkspaceID:          r.WorkspaceID(),
		RequesterUserID:      r.User.User.ID,
		ApprovalID:           approvalID,
		ExpectedVersion:      0,
		ClientCancellationID: clientCancellationID,
		Reason:               "applicant cancelled",
		Now:                  time.Now().UTC(),
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(dto.CollaboratorRequestSubmitResponse{
		ApprovalID: result.ApprovalID,
		RequestID:  result.RequestID,
		Status:     result.Status,
	}), nil
}
