package serviceaccount

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	serviceaccountstore "github.com/tidbcloud/AgentTeam/internal/store/serviceaccount"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

func loadLocalAccountForReplay(ctx context.Context, store *serviceaccountstore.TxStore, workspaceID string, accountID string) (serviceaccountstore.Account, bool, error) {
	account, err := store.LoadAccountForUpdate(ctx, workspaceID, accountID)
	if err == nil {
		return account, true, nil
	}
	if coredb.IsNotFound(err) || errors.Is(err, serviceaccountstore.ErrNotFound) {
		return serviceaccountstore.Account{}, false, nil
	}
	return serviceaccountstore.Account{}, false, err
}

// requireEquivalentMaterializationReplay compares only the immutable receipt
// captured by the created event. The account profile, initial manager assignments,
// audience projection, and platform profile may all legitimately evolve later.
func requireEquivalentMaterializationReplay(ctx context.Context, store *serviceaccountstore.TxStore, existing serviceaccountstore.Account, expectedKind string, candidateDigest string) error {
	encodedMetadata, err := store.LoadCreatedAuditMetadata(ctx, existing.WorkspaceID, existing.ID)
	if err != nil {
		if errors.Is(err, serviceaccountstore.ErrNotFound) {
			return storeerr.Conflict("accountId was already materialized without an immutable receipt")
		}
		return err
	}

	var metadata createdAuditMetadata
	if err := json.Unmarshal(encodedMetadata, &metadata); err != nil {
		return storeerr.Conflict("accountId was already materialized with an unreadable immutable receipt")
	}
	record := metadata.Materialization
	if record.DigestAlgorithm != receiptDigestAlgorithm || record.Receipt.Version != materializationReceiptVersion || record.Receipt.Kind != expectedKind {
		return storeerr.Conflict("accountId was already materialized with an incompatible immutable receipt")
	}
	storedDigest, err := digestMaterializationReceipt(record.Receipt)
	if err != nil {
		return err
	}
	if record.Digest != storedDigest {
		return storeerr.Conflict("accountId was already materialized with an invalid immutable receipt")
	}
	if candidateDigest != storedDigest {
		return storeerr.Conflict("accountId was already materialized from different logical input")
	}
	return nil
}

// requireHumanMembersActive validates only caller-referenced members. It never
// enumerates a workspace audience; that unbounded projection is reconciled
// asynchronously from the durable pending intent.
func requireHumanMembersActive(ctx context.Context, store *serviceaccountstore.TxStore, workspaceID string, required []string, label string) error {
	members := sortedUnique(required)
	if len(members) == 0 {
		return nil
	}
	allActive, err := store.AllMembersAreActiveHumans(ctx, workspaceID, members)
	if err != nil {
		return err
	}
	if !allActive {
		return storeerr.BadRequest(fmt.Sprintf("%s must be active Human members of the workspace", label))
	}
	return nil
}
