package admin

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

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

func (handler *SystemAdminHandler) requirePlatformServiceAccounts() (PlatformServiceAccountService, error) {
	if handler == nil || handler.platformServiceAccounts == nil {
		return nil, api.NewHTTPError(http.StatusServiceUnavailable, "Platform Service Account service unavailable")
	}
	return handler.platformServiceAccounts, nil
}

func (handler *SystemAdminHandler) CreatePlatformServiceAccount(r api.AuthenticatedJSON[dto.AdminPlatformServiceAccountCreateRequest]) (api.Response, error) {
	service, err := handler.requirePlatformServiceAccounts()
	if err != nil {
		return api.Response{}, err
	}
	account, err := service.CreatePlatformAccount(r.Context(), serviceaccount.PlatformAccountCreate{
		ID: r.Body.ID, Name: r.Body.Name,
		Description: r.Body.Description, AvatarAssetID: r.Body.AvatarAssetID,
		OperatorUserID: r.UserID(), CorrelationID: r.Body.CorrelationID,
		Now: time.Now().UTC(),
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.Created(presentPlatformServiceAccount(account)), nil
}

func (handler *SystemAdminHandler) GetPlatformServiceAccount(r api.AuthenticatedRequest) (api.Response, error) {
	service, err := handler.requirePlatformServiceAccounts()
	if err != nil {
		return api.Response{}, err
	}
	account, err := service.GetPlatformAccount(r.Context(), platformServiceAccountIDParam(r))
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(presentPlatformServiceAccount(account)), nil
}

func (handler *SystemAdminHandler) UpdatePlatformServiceAccountAvatar(r api.AuthenticatedJSON[dto.AdminPlatformServiceAccountAvatarUpdateRequest]) (api.Response, error) {
	service, err := handler.requirePlatformServiceAccounts()
	if err != nil {
		return api.Response{}, err
	}
	account, err := service.UpdatePlatformAccountAvatar(r.Context(), serviceaccount.PlatformAccountAvatarMutation{
		PlatformServiceAccountID: platformServiceAccountIDParam(r.AuthenticatedRequest),
		ExpectedVersion:          r.Body.ExpectedVersion, AvatarAssetID: r.Body.AvatarAssetID,
		OperatorUserID: r.UserID(), CorrelationID: r.Body.CorrelationID,
		Now: time.Now().UTC(),
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(presentPlatformServiceAccount(account)), nil
}

func (handler *SystemAdminHandler) ListPlatformServiceAccounts(r api.AuthenticatedRequest) (api.Response, error) {
	service, err := handler.requirePlatformServiceAccounts()
	if err != nil {
		return api.Response{}, err
	}
	limit := parseAdminLimit(r.Gin.Query("limit"), 50)
	accounts, err := service.ListPlatformAccounts(r.Context(), r.Gin.Query("after"), r.Gin.Query("q"), limit)
	if err != nil {
		return api.Response{}, err
	}
	items := make([]dto.AdminPlatformServiceAccountResponse, 0, len(accounts))
	for _, account := range accounts {
		items = append(items, presentPlatformServiceAccount(account))
	}
	nextCursor := ""
	if len(accounts) == limit && len(accounts) > 0 {
		nextCursor = accounts[len(accounts)-1].ID
	}
	return api.OK(map[string]any{"items": items, "nextCursor": nextCursor}), nil
}

func (handler *SystemAdminHandler) DeletePlatformServiceAccount(r api.AuthenticatedRequest) (api.Response, error) {
	service, err := handler.requirePlatformServiceAccounts()
	if err != nil {
		return api.Response{}, err
	}
	expectedVersion, err := strconv.ParseInt(strings.TrimSpace(r.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 := service.DeletePlatformAccount(r.Context(), serviceaccount.PlatformAccountDelete{
		PlatformServiceAccountID: platformServiceAccountIDParam(r),
		ExpectedVersion:          expectedVersion, OperatorUserID: r.UserID(),
		Now: time.Now().UTC(),
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(presentPlatformServiceAccount(account)), nil
}

func (handler *SystemAdminHandler) ReconcilePlatformServiceAccounts(r api.AuthenticatedRequest) (api.Response, error) {
	service, err := handler.requirePlatformServiceAccounts()
	if err != nil {
		return api.Response{}, err
	}
	if _, err := service.GetPlatformAccount(r.Context(), platformServiceAccountIDParam(r)); err != nil {
		return api.Response{}, err
	}
	result, err := service.ProcessOnce(r.Context())
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(dto.AdminPlatformReconcileResponse{
		DiscoveredInputs: result.DiscoveredInputs, ProvisionedInstances: result.ProvisionedInstances,
		AdvancedAccounts: result.AdvancedAccounts, AllocatedBroadcasts: result.AllocatedBroadcasts,
		EnumeratedDeliveries:   result.EnumeratedDeliveries,
		SettledExcludedTargets: result.SettledExcludedTargets,
		DeliveredMessages:      result.DeliveredMessages, RetriedDeliveries: result.RetriedDeliveries,
		CompletedBroadcasts: result.CompletedBroadcasts,
	}), nil
}

func (handler *SystemAdminHandler) CreatePlatformBroadcast(r api.AuthenticatedJSON[dto.AdminPlatformBroadcastCreateRequest]) (api.Response, error) {
	service, err := handler.requirePlatformServiceAccounts()
	if err != nil {
		return api.Response{}, err
	}
	assets := make([]serviceaccount.PlatformBroadcastAsset, 0, len(r.Body.Assets))
	for _, asset := range r.Body.Assets {
		assets = append(assets, serviceaccount.PlatformBroadcastAsset{
			SourceAssetRef: asset.SourceAssetRef, ContentHash: asset.ContentHash,
			MediaType: asset.MediaType, SizeBytes: asset.SizeBytes, ClientLabel: asset.ClientLabel,
		})
	}
	broadcast, err := service.SubmitPlatformBroadcast(r.Context(), serviceaccount.PlatformBroadcastSubmit{
		PlatformServiceAccountID: platformServiceAccountIDParam(r.AuthenticatedRequest),
		OperatorUserID:           r.UserID(), ClientBroadcastID: r.Body.ClientBroadcastID,
		CorrelationID: r.Body.CorrelationID,
		Plan: serviceaccount.PlatformBroadcastPlan{
			Title: r.Body.Title, Content: r.Body.Content, Assets: assets,
		},
		Now: time.Now().UTC(),
	})
	if err != nil {
		return api.Response{}, err
	}
	return api.Response{Status: http.StatusAccepted, Body: presentPlatformBroadcast(broadcast)}, nil
}

func (handler *SystemAdminHandler) GetPlatformBroadcast(r api.AuthenticatedRequest) (api.Response, error) {
	service, err := handler.requirePlatformServiceAccounts()
	if err != nil {
		return api.Response{}, err
	}
	broadcast, err := service.GetPlatformBroadcast(r.Context(), platformServiceAccountIDParam(r), strings.TrimSpace(r.Gin.Param("broadcastId")))
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(presentPlatformBroadcast(broadcast)), nil
}

func (handler *SystemAdminHandler) ListPlatformBroadcasts(r api.AuthenticatedRequest) (api.Response, error) {
	service, err := handler.requirePlatformServiceAccounts()
	if err != nil {
		return api.Response{}, err
	}
	limit := parseAdminLimit(r.Gin.Query("limit"), 50)
	broadcasts, err := service.ListPlatformBroadcastItems(
		r.Context(), platformServiceAccountIDParam(r), r.Gin.Query("workspaceId"),
		r.Gin.Query("after"), limit,
	)
	if err != nil {
		return api.Response{}, err
	}
	items := make([]dto.AdminPlatformBroadcastResponse, 0, len(broadcasts))
	for _, broadcast := range broadcasts {
		items = append(items, presentPlatformBroadcastListItem(broadcast))
	}
	nextCursor := ""
	if len(broadcasts) == limit && len(broadcasts) > 0 {
		nextCursor = broadcasts[len(broadcasts)-1].Broadcast.ID
	}
	return api.OK(map[string]any{"items": items, "nextCursor": nextCursor}), nil
}

func platformServiceAccountIDParam(r api.AuthenticatedRequest) string {
	return strings.TrimSpace(r.Gin.Param("platformServiceAccountId"))
}

func presentPlatformServiceAccount(account serviceaccountstore.PlatformAccount) dto.AdminPlatformServiceAccountResponse {
	return dto.AdminPlatformServiceAccountResponse{
		ID: account.ID, Name: account.Name,
		Description: account.Description, AvatarAssetID: account.AvatarAssetID,
		AvatarURL:      attachment.AvatarAssetURL(account.AvatarAssetID),
		ProfileVersion: account.ProfileVersion, Version: account.Version,
		ProvisioningMode: account.ProvisioningMode, ProvisioningState: account.ProvisioningState,
		AppliedProvisioningGeneration: account.AppliedProvisioningGeneration,
		AudiencePolicyVersion:         account.AudiencePolicyVersion,
		LatestBroadcastSeq:            account.LatestBroadcastSeq,
		CreatedByOperatorUserID:       account.CreatedByOperatorUserID,
		CreatedAt:                     account.CreatedAt, UpdatedAt: account.UpdatedAt,
	}
}

func presentPlatformBroadcast(broadcast serviceaccountstore.PlatformBroadcast) dto.AdminPlatformBroadcastResponse {
	title := ""
	var plan struct {
		Title string `json:"title"`
	}
	if err := json.Unmarshal(broadcast.PayloadSnapshot, &plan); err == nil {
		title = strings.TrimSpace(plan.Title)
	}
	return dto.AdminPlatformBroadcastResponse{
		ID: broadcast.ID, PlatformServiceAccountID: broadcast.PlatformServiceAccountID,
		OperatorPrincipalID:            broadcast.OperatorPrincipalID,
		OperatorDisplayName:            broadcast.OperatorPrincipalID,
		Title:                          title,
		ClientBroadcastID:              broadcast.ClientBroadcastID,
		AcceptanceOrdinal:              broadcast.AcceptanceOrdinal,
		RequiredProvisioningGeneration: broadcast.RequiredProvisioningGeneration,
		BroadcastSeq:                   broadcast.BroadcastSeq, PayloadDigest: broadcast.PayloadDigest,
		State: broadcast.State, EnumerationComplete: broadcast.EnumerationComplete,
		TargetCount: broadcast.TargetCount, DeliveredCount: broadcast.DeliveredCount,
		SkippedCount: broadcast.SkippedCount, ExceptionCount: broadcast.ExceptionCount,
		Version: broadcast.Version, CorrelationID: broadcast.CorrelationID,
		CreatedAt: broadcast.CreatedAt, UpdatedAt: broadcast.UpdatedAt,
		AllocatedAt: broadcast.AllocatedAt, CompletedAt: broadcast.CompletedAt, ClosedAt: broadcast.ClosedAt,
		StatusURL: "/api/v1/admin/platform-service-accounts/" + broadcast.PlatformServiceAccountID + "/broadcasts/" + broadcast.ID,
	}
}

func presentPlatformBroadcastListItem(item serviceaccount.PlatformBroadcastListItem) dto.AdminPlatformBroadcastResponse {
	result := presentPlatformBroadcast(item.Broadcast)
	if displayName := strings.TrimSpace(item.OperatorDisplayName); displayName != "" {
		result.OperatorDisplayName = displayName
	}
	result.OperatorAvatarURL = strings.TrimSpace(item.OperatorAvatarURL)
	result.WorkspaceServiceAccountID = strings.TrimSpace(item.WorkspaceServiceAccountID)
	result.WorkspaceMessageID = strings.TrimSpace(item.WorkspaceMessageID)
	return result
}
