package serviceaccount

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	serviceaccountservice "github.com/tidbcloud/AgentTeam/internal/service/serviceaccount"
	serviceaccountstore "github.com/tidbcloud/AgentTeam/internal/store/serviceaccount"
)

type Handler struct {
	service   Service
	projector PendingRequestProjector
}

type createRequestResultView struct {
	Request createRequestView `json:"request"`
	Account *accountView      `json:"account,omitempty"`
}

type createRequestView struct {
	ID                      string                                       `json:"id"`
	WorkspaceID             string                                       `json:"workspaceId"`
	RequestedByMemberID     string                                       `json:"requestedByMemberId"`
	RequestVersion          int64                                        `json:"requestVersion"`
	ClientRequestID         string                                       `json:"clientRequestId"`
	Payload                 serviceaccountservice.WorkspaceCreatePayload `json:"payload"`
	AuthorizationPath       string                                       `json:"authorizationPath"`
	BusinessState           string                                       `json:"businessState"`
	ApprovalInstanceID      string                                       `json:"approvalInstanceId,omitempty"`
	ConsumedDecisionVersion int64                                        `json:"consumedDecisionVersion,omitempty"`
	CreatedServiceAccountID string                                       `json:"createdServiceAccountId,omitempty"`
	Version                 int64                                        `json:"version"`
	CreatedAt               time.Time                                    `json:"createdAt"`
	UpdatedAt               time.Time                                    `json:"updatedAt"`
	CancelledAt             *time.Time                                   `json:"cancelledAt,omitempty"`
	FulfilledAt             *time.Time                                   `json:"fulfilledAt,omitempty"`
	CurrentProcessorNames   []string                                     `json:"currentProcessorNames,omitempty"`
	ProcessorName           string                                       `json:"processorName,omitempty"`
}

type accountView struct {
	ID                       string `json:"id"`
	PrincipalID              string `json:"principalId"`
	ManagementKind           string `json:"managementKind"`
	PlatformServiceAccountID string `json:"platformServiceAccountId,omitempty"`
	MessageScopeID           string `json:"messageScopeId"`
	Name                     string `json:"name"`
	Description              string `json:"description"`
	AvatarURL                string `json:"avatarUrl,omitempty"`
	CanManage                bool   `json:"canManage"`
	CanPublish               bool   `json:"canPublish"`
	CanModerate              bool   `json:"canModerate"`
	CanManagePlatform        bool   `json:"canManagePlatform"`
	Version                  int64  `json:"version,omitempty"`
	CreatedByMemberID        string `json:"createdByMemberId,omitempty"`
}

type managerView struct {
	ID       string `json:"id"`
	MemberID string `json:"memberId"`
	Status   string `json:"status"`
	Version  int64  `json:"version"`
}

func (handler *Handler) CreateRequest(request api.WorkspaceScopedJSON[dto.WorkspaceServiceAccountCreateRequest]) (api.Response, error) {
	if handler == nil || handler.service == nil {
		return api.Response{}, fmt.Errorf("service account management service is not configured")
	}
	body := request.Body
	result, err := handler.service.SubmitWorkspaceCreateRequestAndReconcile(request.Context(), serviceaccountservice.SubmitWorkspaceCreateRequest{
		WorkspaceID:               request.WorkspaceID(),
		RequesterUserID:           request.UserID(),
		ClientRequestID:           body.ClientRequestID,
		CorrelationID:             body.CorrelationID,
		ExpectedAuthorizationPath: body.ExpectedAuthorizationPath,
		Payload: serviceaccountservice.WorkspaceCreatePayload{
			Name:        body.Name,
			Description: body.Description, AvatarAssetID: body.AvatarAssetID, AvatarURL: body.AvatarURL,
		},
	})
	if err != nil {
		return api.Response{}, err
	}
	if result.Approval != nil {
		if handler.projector == nil {
			return api.Response{}, fmt.Errorf("service account Approval projector is not configured")
		}
		_, err := handler.projector.ProjectPendingRequest(request.Context(), result)
		if err != nil {
			return api.Response{}, err
		}
		_, settled, err := handler.service.SettlePrivilegedWorkspaceCreateRequest(
			request.Context(), result.Request.WorkspaceID, result.Request.ID, time.Now().UTC(),
		)
		if err != nil {
			return api.Response{}, err
		}
		if settled {
			return api.Response{}, serviceaccountstore.ErrConflict
		}
	}
	var accountAccess *serviceaccountservice.AccountAccess
	if result.Account != nil {
		access, err := handler.service.GetAccountForUser(
			request.Context(), request.UserID(), request.WorkspaceID(), result.Account.ID,
		)
		if err != nil {
			return api.Response{}, err
		}
		accountAccess = &access
	}
	view, err := createRequestResultToView(result, accountAccess)
	if err != nil {
		return api.Response{}, err
	}
	status := http.StatusOK
	if result.Created {
		status = http.StatusCreated
	}
	if result.Approval != nil {
		status = http.StatusAccepted
	}
	return api.Response{Status: status, Body: view}, nil
}

func (handler *Handler) ListCreateRequests(request api.WorkspaceScopedRequest) (api.Response, error) {
	if handler == nil || handler.service == nil {
		return api.Response{}, fmt.Errorf("service account management service is not configured")
	}
	limit, err := parseLimit(request.Gin.Query("limit"), 50)
	if err != nil {
		return api.Response{}, err
	}
	rows, err := handler.service.ListCreateRequestsForUser(request.Context(), request.UserID(), request.WorkspaceID(), request.Gin.Query("state"), limit)
	if err != nil {
		return api.Response{}, err
	}
	views := make([]createRequestView, 0, len(rows))
	for _, row := range rows {
		view, err := createRequestToView(row)
		if err != nil {
			return api.Response{}, err
		}
		views = append(views, view)
	}
	return api.OK(map[string]any{"requests": views}), nil
}

func (handler *Handler) GetCreateRequest(request api.WorkspaceScopedRequest) (api.Response, error) {
	if handler == nil || handler.service == nil {
		return api.Response{}, fmt.Errorf("service account management service is not configured")
	}
	detail, err := handler.service.GetCreateRequestForUser(request.Context(), request.UserID(), request.WorkspaceID(), request.Gin.Param("requestId"))
	if err != nil {
		return api.Response{}, err
	}
	view, err := createRequestToView(detail.Request)
	if err != nil {
		return api.Response{}, err
	}
	view.CurrentProcessorNames = detail.CurrentProcessorNames
	view.ProcessorName = detail.ProcessorName
	return api.OK(view), nil
}

func (handler *Handler) ListAccounts(request api.WorkspaceScopedRequest) (api.Response, error) {
	if handler == nil || handler.service == nil {
		return api.Response{}, fmt.Errorf("service account management service is not configured")
	}
	limit, err := parseLimit(request.Gin.Query("limit"), 50)
	if err != nil {
		return api.Response{}, err
	}
	rows, err := handler.service.ListAccountsForUser(
		request.Context(), request.UserID(), request.WorkspaceID(),
		request.Gin.Query("after"), request.Gin.Query("q"), limit,
	)
	if err != nil {
		return api.Response{}, err
	}
	views := make([]accountView, 0, len(rows))
	for _, row := range rows {
		views = append(views, accountAccessToView(row))
	}
	nextCursor := ""
	if len(rows) == limit && len(rows) > 0 {
		nextCursor = rows[len(rows)-1].Account.ID
	}
	return api.OK(map[string]any{"serviceAccounts": views, "nextCursor": nextCursor}), nil
}

func (handler *Handler) GetAccount(request api.WorkspaceScopedRequest) (api.Response, error) {
	if handler == nil || handler.service == nil {
		return api.Response{}, fmt.Errorf("service account management service is not configured")
	}
	account, err := handler.service.GetAccountForUser(request.Context(), request.UserID(), request.WorkspaceID(), request.Gin.Param("serviceAccountId"))
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(accountAccessToView(account)), nil
}

func (handler *Handler) DeleteAccount(request api.WorkspaceScopedRequest) (api.Response, error) {
	if handler == nil || handler.service == nil {
		return api.Response{}, fmt.Errorf("service account management service is not configured")
	}
	expectedVersion, err := strconv.ParseInt(strings.TrimSpace(request.Gin.Query("expectedVersion")), 10, 64)
	if err != nil || expectedVersion <= 0 {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "expectedVersion must be a positive integer")
	}
	account, err := handler.service.DeleteWorkspaceAccountForUser(
		request.Context(), request.UserID(), request.WorkspaceID(),
		request.Gin.Param("serviceAccountId"), expectedVersion, time.Now().UTC(),
	)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(accountAccessToView(serviceaccountservice.AccountAccess{
		Account: account, CanManage: true,
	})), nil
}

func (handler *Handler) UpdateAccountAvatar(request api.WorkspaceScopedJSON[dto.WorkspaceServiceAccountAvatarUpdateRequest]) (api.Response, error) {
	if handler == nil || handler.service == nil {
		return api.Response{}, fmt.Errorf("service account management service is not configured")
	}
	account, err := handler.service.UpdateWorkspaceAccountAvatarForUser(
		request.Context(), request.UserID(), request.WorkspaceID(),
		request.Gin.Param("serviceAccountId"), request.Body.AvatarAssetID,
		time.Now().UTC(),
	)
	if err != nil {
		return api.Response{}, err
	}
	access, err := handler.service.GetAccountForUser(
		request.Context(), request.UserID(), request.WorkspaceID(), account.ID,
	)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(accountAccessToView(access)), nil
}

func (handler *Handler) ListManagers(request api.WorkspaceScopedRequest) (api.Response, error) {
	if handler == nil || handler.service == nil {
		return api.Response{}, fmt.Errorf("service account management service is not configured")
	}
	rows, err := handler.service.ListManagersForUser(
		request.Context(), request.UserID(), request.WorkspaceID(), request.Gin.Param("serviceAccountId"),
	)
	if err != nil {
		return api.Response{}, err
	}
	managers := make([]managerView, 0, len(rows))
	for _, row := range rows {
		managers = append(managers, managerToView(row))
	}
	return api.OK(map[string]any{"managers": managers}), nil
}

func (handler *Handler) AddManager(request api.WorkspaceScopedJSON[dto.WorkspaceServiceAccountManagerAddRequest]) (api.Response, error) {
	if handler == nil || handler.service == nil {
		return api.Response{}, fmt.Errorf("service account management service is not configured")
	}
	manager, err := handler.service.AddManagerForUser(
		request.Context(), request.UserID(), request.WorkspaceID(),
		request.Gin.Param("serviceAccountId"), request.Body.MemberID, time.Now().UTC(),
	)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(managerToView(manager)), nil
}

func (handler *Handler) RemoveManager(request api.WorkspaceScopedRequest) (api.Response, error) {
	if handler == nil || handler.service == nil {
		return api.Response{}, fmt.Errorf("service account management service is not configured")
	}
	expectedVersion, err := strconv.ParseInt(strings.TrimSpace(request.Gin.Query("expectedVersion")), 10, 64)
	if err != nil || expectedVersion <= 0 {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "expectedVersion must be a positive integer")
	}
	manager, err := handler.service.RemoveManagerForUser(
		request.Context(), request.UserID(), request.WorkspaceID(),
		request.Gin.Param("serviceAccountId"), request.Gin.Param("managerId"), expectedVersion, time.Now().UTC(),
	)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(managerToView(manager)), nil
}

func createRequestResultToView(result serviceaccountservice.WorkspaceCreateRequestResult, accountAccess *serviceaccountservice.AccountAccess) (createRequestResultView, error) {
	request, err := createRequestToView(result.Request)
	if err != nil {
		return createRequestResultView{}, err
	}
	view := createRequestResultView{Request: request}
	if result.Account != nil && accountAccess != nil {
		account := accountAccessToView(*accountAccess)
		view.Account = &account
	}
	return view, nil
}

func createRequestToView(request serviceaccountstore.CreateRequest) (createRequestView, error) {
	var payload serviceaccountservice.WorkspaceCreatePayload
	if err := json.Unmarshal(request.PayloadSnapshot, &payload); err != nil {
		return createRequestView{}, fmt.Errorf("decode service account create request payload: %w", err)
	}
	return createRequestView{
		ID: request.ID, WorkspaceID: request.WorkspaceID,
		RequestedByMemberID: request.RequestedByMemberID, RequestVersion: request.RequestVersion,
		ClientRequestID: request.ClientRequestID, Payload: payload,
		AuthorizationPath: request.AuthorizationPath, BusinessState: request.BusinessState,
		ApprovalInstanceID: request.ApprovalInstanceID, ConsumedDecisionVersion: request.ConsumedDecisionVersion,
		CreatedServiceAccountID: request.CreatedServiceAccountID, Version: request.Version,
		CreatedAt: request.CreatedAt, UpdatedAt: request.UpdatedAt,
		CancelledAt: request.CancelledAt, FulfilledAt: request.FulfilledAt,
	}, nil
}

func accountAccessToView(access serviceaccountservice.AccountAccess) accountView {
	account := access.Account
	view := accountView{
		ID: account.ID, PrincipalID: account.PrincipalID,
		ManagementKind: account.ManagementKind, MessageScopeID: account.MessageScopeID,
		Name: account.Name, Description: account.Description, AvatarURL: account.AvatarURL,
		CanManage: access.CanManage, CanPublish: access.CanPublish,
		CanModerate: access.CanModerate, CanManagePlatform: access.CanManagePlatform,
	}
	if access.CanManage {
		view.Version = account.Version
	}
	if access.CanManage {
		view.CreatedByMemberID = account.CreatedByMemberID
	}
	if access.CanManagePlatform {
		view.PlatformServiceAccountID = account.PlatformServiceAccountID
	}
	return view
}

func managerToView(manager serviceaccountstore.ManagerAssignment) managerView {
	return managerView{
		ID: manager.ID, MemberID: manager.MemberID,
		Status: manager.Status, Version: manager.Version,
	}
}

func parseLimit(raw string, fallback int) (int, error) {
	if strings.TrimSpace(raw) == "" {
		return fallback, nil
	}
	limit, err := strconv.Atoi(strings.TrimSpace(raw))
	if err != nil || limit < 1 || limit > 100 {
		return 0, api.NewHTTPError(http.StatusBadRequest, "limit must be between 1 and 100")
	}
	return limit, nil
}
