package serviceaccount

import (
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"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"
	serviceaccountstore "github.com/tidbcloud/AgentTeam/internal/store/serviceaccount"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
	"github.com/tidbcloud/AgentTeam/internal/workspace/owner"
)

// DeleteWorkspaceAccountForUser permanently deletes one workspace-owned
// Service Account and all of its exclusive local substrate. Workspace
// administrators and current account managers share the same indivisible
// authority; the creator has that authority only through its initial manager
// assignment.
func (service *Service) DeleteWorkspaceAccountForUser(
	ctx context.Context,
	userID string,
	workspaceID string,
	accountID string,
	expectedVersion int64,
	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
	}
	accountID = strings.TrimSpace(accountID)
	if accountID == "" || expectedVersion <= 0 {
		return serviceaccountstore.Account{}, storeerr.BadRequest("serviceAccountId and expectedVersion are required")
	}
	now = normalizedNow(now)
	var deleted 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 accounts are deleted by platform operators")
		}
		permissionBasisKind, permissionBasisRef, err := requireWorkspaceDeleteAuthority(
			ctx, accounts, caller, current,
		)
		if err != nil {
			return err
		}
		// Authority is checked before the optimistic version fence so callers
		// without management rights cannot use DELETE as a version oracle.
		if current.Version != expectedVersion {
			return storeerr.Conflict("service account version changed")
		}
		metadata, err := json.Marshal(map[string]any{
			"name": current.Name, "messageScopeId": current.MessageScopeID,
		})
		if err != nil {
			return fmt.Errorf("encode service account deletion audit: %w", err)
		}
		auditID, err := identifiers.NewEntityID("saaudit")
		if err != nil {
			return err
		}
		if err := accounts.InsertAuditEvent(ctx, serviceaccountstore.AuditEvent{
			ID: auditID, WorkspaceID: workspaceID, ServiceAccountID: current.ID,
			EventType: "service_account.deleted", SubjectType: "service_account",
			SubjectID: current.ID, SubjectVersion: current.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,
		}); err != nil {
			return err
		}
		if err := accounts.DeleteLocalAccountSubstrate(ctx, current); err != nil {
			return err
		}
		deleted = current
		return nil
	})
	if err == nil {
		// The destructive transaction is already committed; runtime notification
		// failure cannot turn the API result into a failed deletion.
		_ = notifyLocalAccountsDeleted(ctx, service.deletionObserver, []serviceaccountstore.Account{deleted})
	}
	return deleted, err
}

// requireWorkspaceDeleteAuthority resolves the caller's current canonical
// authority while both the Human and account rows are locked.
func requireWorkspaceDeleteAuthority(
	ctx context.Context,
	accounts *serviceaccountstore.TxStore,
	caller access.HumanWorkspaceMember,
	account serviceaccountstore.Account,
) (string, string, error) {
	if access.HasWorkspaceAdminRole(domain.WorkspaceRole(caller.WorkspaceRole)) {
		return "workspace_role", caller.WorkspaceRole, nil
	}
	manager, err := accounts.HasActiveManagerForUpdate(
		ctx, account.WorkspaceID, account.ID, caller.Member.ID,
	)
	if err != nil {
		return "", "", err
	}
	if !manager {
		return "", "", storeerr.Forbidden("workspace administrator or active service account manager authority is required")
	}
	return "service_account_manager", caller.Member.ID, nil
}

// DeletePlatformAccount permanently deletes the global principal, every
// workspace-local projection and message feed, and all outstanding platform
// provisioning/broadcast state under one optimistic root-version fence.
func (service *PlatformService) DeletePlatformAccount(ctx context.Context, input PlatformAccountDelete) (serviceaccountstore.PlatformAccount, error) {
	if err := service.validateControlPlane(); err != nil {
		return serviceaccountstore.PlatformAccount{}, err
	}
	input.PlatformServiceAccountID = strings.TrimSpace(input.PlatformServiceAccountID)
	input.OperatorUserID = strings.TrimSpace(input.OperatorUserID)
	input.CorrelationID = strings.TrimSpace(input.CorrelationID)
	input.Now = service.normalizedNow(input.Now)
	if input.PlatformServiceAccountID == "" || input.OperatorUserID == "" || input.ExpectedVersion <= 0 {
		return serviceaccountstore.PlatformAccount{}, storeerr.BadRequest("platformServiceAccountId, operatorUserId, and expectedVersion are required")
	}

	var deleted serviceaccountstore.PlatformAccount
	var deletedLocals []serviceaccountstore.Account
	err := coredb.WithTx(ctx, service.db, func(tx coredb.Tx) error {
		if err := requirePlatformOperatorTx(ctx, tx, input.OperatorUserID); err != nil {
			return err
		}
		accounts := service.accounts.InTx(tx)
		current, err := accounts.LoadPlatformAccountForUpdate(ctx, input.PlatformServiceAccountID)
		if err != nil {
			return err
		}
		if current.Version != input.ExpectedVersion {
			return storeerr.Conflict("platform service account version changed")
		}
		locals, err := accounts.ListPlatformLocalAccountsForUpdate(ctx, current.ID)
		if err != nil {
			return err
		}
		metadata, err := json.Marshal(map[string]any{
			"name":               current.Name,
			"workspaceInstances": len(locals),
		})
		if err != nil {
			return fmt.Errorf("encode platform service account deletion audit: %w", err)
		}
		auditID, err := identifiers.NewEntityID("saaudit")
		if err != nil {
			return err
		}
		if err := accounts.InsertAuditEvent(ctx, serviceaccountstore.AuditEvent{
			ID: auditID, PlatformServiceAccountID: current.ID,
			EventType:   "platform_service_account.deleted",
			SubjectType: "platform_service_account", SubjectID: current.ID,
			SubjectVersion: current.Version,
			ActorType:      "platform_operator", ActorID: input.OperatorUserID,
			CausedByActorType: "platform_operator", CausedByActorID: input.OperatorUserID,
			ExecutionActorType: "platform_operator", ExecutionActorID: input.OperatorUserID,
			PermissionBasisKind: "platform_operator", PermissionBasisRef: input.OperatorUserID,
			CorrelationID: input.CorrelationID, Metadata: metadata, CreatedAt: input.Now,
		}); err != nil {
			return err
		}
		for _, local := range locals {
			if err := insertPlatformAccountDeletionDelivery(ctx, accounts, local, input.Now); err != nil {
				return err
			}
		}
		if err := accounts.DeletePlatformWorkspaceInstances(ctx, current.ID); err != nil {
			return err
		}
		for _, local := range locals {
			if err := accounts.DeleteLocalAccountSubstrate(ctx, local); err != nil {
				return err
			}
		}
		if err := accounts.DeletePlatformAccount(ctx, current); err != nil {
			return err
		}
		deleted = current
		deletedLocals = append(deletedLocals, locals...)
		return nil
	})
	if err == nil {
		_ = notifyLocalAccountsDeleted(
			ctx,
			service.deletionObserver,
			managedDeletedLocalAccounts(service.managedWorkspaces, deletedLocals),
		)
	}
	return deleted, err
}

func managedDeletedLocalAccounts(managed owner.ManagedWorkspaceSet, accounts []serviceaccountstore.Account) []serviceaccountstore.Account {
	if len(accounts) == 0 {
		return nil
	}
	result := make([]serviceaccountstore.Account, 0, len(accounts))
	for _, account := range accounts {
		if owner.IsManaged(managed, account.WorkspaceID) {
			result = append(result, account)
		}
	}
	return result
}

// insertPlatformAccountDeletionDelivery captures one workspace projection
// tombstone in the same transaction as the platform-principal deletion. The
// delivery deliberately owns scalar snapshots rather than foreign keys to the
// account, principal, or scope rows that this transaction removes.
func insertPlatformAccountDeletionDelivery(
	ctx context.Context,
	accounts *serviceaccountstore.TxStore,
	account serviceaccountstore.Account,
	now time.Time,
) error {
	deliveryID := platformDeterministicID(
		"sadel",
		"platform-service-account-deletion-delivery",
		account.WorkspaceID,
		account.ID,
		strconv.FormatInt(account.Version, 10),
		account.MessageScopeID,
	)
	if err := accounts.InsertPlatformDeletionDelivery(ctx, serviceaccountstore.PlatformDeletionDeliveryInsert{
		ID:                             deliveryID,
		WorkspaceID:                    account.WorkspaceID,
		PlatformServiceAccountID:       account.PlatformServiceAccountID,
		LocalServiceAccountID:          account.ID,
		LocalServiceAccountPrincipalID: account.PrincipalID,
		MessageScopeID:                 account.MessageScopeID,
		Now:                            now,
	}); err != nil {
		return fmt.Errorf("enqueue platform service account deletion delivery: %w", err)
	}
	return nil
}
