package serviceaccount

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
	"github.com/tidbcloud/AgentTeam/internal/core/identifiers"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/access"
	"github.com/tidbcloud/AgentTeam/internal/store/attachment"
	serviceaccountstore "github.com/tidbcloud/AgentTeam/internal/store/serviceaccount"
	"github.com/tidbcloud/AgentTeam/internal/store/serviceactor"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

// UpdateWorkspaceAccountAvatarForUser updates the canonical account and
// service-member projections. Workspace admins and active account managers
// share the same indivisible management/publishing authority.
func (service *Service) UpdateWorkspaceAccountAvatarForUser(
	ctx context.Context,
	userID string,
	workspaceID string,
	accountID string,
	avatarAssetID string,
	now time.Time,
) (serviceaccountstore.Account, error) {
	if err := service.validate(); err != nil {
		return serviceaccountstore.Account{}, err
	}
	expectedCaller, err := service.loadWorkspaceHuman(ctx, userID, workspaceID)
	if err != nil {
		return serviceaccountstore.Account{}, err
	}
	avatarAssetID = strings.TrimSpace(avatarAssetID)
	avatarURL := attachment.AvatarAssetURL(avatarAssetID)
	now = normalizedNow(now)

	var updated serviceaccountstore.Account
	err = coredb.WithTx(ctx, service.db, func(tx coredb.Tx) error {
		caller, err := service.loadWorkspaceHumanForUpdate(ctx, tx, userID, workspaceID, expectedCaller.Member.ID)
		if err != nil {
			return err
		}
		accounts := service.accounts.InTx(tx)
		current, err := accounts.LoadAccountForUpdate(ctx, workspaceID, accountID)
		if err != nil {
			return err
		}
		if current.ManagementKind != serviceaccountstore.ManagementKindWorkspace {
			return storeerr.Forbidden("platform service account profiles are managed by platform operators")
		}
		if err := requireManagerMutationAuthority(ctx, accounts, caller, workspaceID, accountID); err != nil {
			return err
		}
		permissionBasisKind := "service_account_manager"
		permissionBasisRef := caller.Member.ID
		if access.HasWorkspaceAdminRole(domain.WorkspaceRole(caller.WorkspaceRole)) {
			permissionBasisKind = "workspace_role"
			permissionBasisRef = caller.WorkspaceRole
		}
		if err := requireServiceAccountAvatarAsset(ctx, tx, avatarAssetID, current.ID, workspaceID); err != nil {
			return err
		}
		member, err := service.actors.InTx(tx).LoadMemberByInstallation(ctx, current.ServiceInstallationID)
		if err != nil {
			return err
		}
		if _, err := service.actors.InTx(tx).UpsertMember(ctx, serviceactor.MemberUpsert{
			ID: member.ID, WorkspaceID: member.WorkspaceID,
			ServiceDefinitionID:   member.ServiceDefinitionID,
			ServiceInstallationID: member.ServiceInstallationID,
			MemberID:              member.MemberID, ServiceKind: member.ServiceKind,
			Handle: member.Handle, DisplayName: member.DisplayName,
			AvatarURL: avatarURL, Status: member.Status, Now: now,
		}); err != nil {
			return err
		}
		updated, err = accounts.UpdateWorkspaceAccountAvatar(
			ctx, workspaceID, accountID, current.Version, avatarAssetID, now,
		)
		if err != nil {
			return err
		}
		metadata, err := json.Marshal(map[string]any{
			"avatarAssetId": avatarAssetID,
		})
		if err != nil {
			return fmt.Errorf("encode service account avatar audit: %w", err)
		}
		auditID, err := identifiers.NewEntityID("saaudit")
		if err != nil {
			return err
		}
		return accounts.InsertAuditEvent(ctx, serviceaccountstore.AuditEvent{
			ID: auditID, WorkspaceID: workspaceID,
			ServiceAccountID: accountID,
			EventType:        "service_account.avatar_updated",
			SubjectType:      "service_account", SubjectID: accountID,
			SubjectVersion: updated.Version,
			ActorType:      "human", ActorID: caller.Member.ID,
			CausedByActorType: "human", CausedByActorID: caller.Member.ID,
			ExecutionActorType: "human", ExecutionActorID: caller.Member.ID,
			PermissionBasisKind: permissionBasisKind,
			PermissionBasisRef:  permissionBasisRef,
			Metadata:            metadata, CreatedAt: now,
		})
	})
	return updated, err
}

func requireServiceAccountAvatarAsset(ctx context.Context, tx coredb.Tx, avatarAssetID string, accountID string, workspaceID string) error {
	if strings.TrimSpace(avatarAssetID) == "" {
		return nil
	}
	var count int64
	if err := tx.QueryRow(ctx, `
SELECT COUNT(*)
FROM avatars
WHERE id = $1
  AND owner_kind = 'service_account'
  AND owner_id = $2
  AND ($3 = '' OR workspace_id = $3)
`, strings.TrimSpace(avatarAssetID), strings.TrimSpace(accountID), strings.TrimSpace(workspaceID)).Scan(&count); err != nil {
		return fmt.Errorf("validate service account avatar asset: %w", err)
	}
	if count != 1 {
		return storeerr.BadRequest("avatar asset does not belong to service account")
	}
	return nil
}
