package serviceaccount

import (
	"context"
	"fmt"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
)

// InsertAuditEvent appends one immutable typed service-account audit fact.
func (store *TxStore) InsertAuditEvent(ctx context.Context, event AuditEvent) error {
	execer, err := txExecer(store)
	if err != nil {
		return err
	}
	event.ID = trim(event.ID)
	event.EventType = trim(event.EventType)
	event.SubjectType = trim(event.SubjectType)
	event.SubjectID = trim(event.SubjectID)
	event.ActorType = trim(event.ActorType)
	event.ActorID = trim(event.ActorID)
	if event.ID == "" || event.EventType == "" || event.SubjectType == "" || event.SubjectID == "" || event.ActorType == "" || event.ActorID == "" {
		return badRequest("service account audit identity, subject, and actor are required")
	}
	event.WorkspaceID = trim(event.WorkspaceID)
	event.ServiceAccountID = trim(event.ServiceAccountID)
	event.PlatformServiceAccountID = trim(event.PlatformServiceAccountID)
	if event.ServiceAccountID != "" {
		if event.WorkspaceID == "" {
			return badRequest("workspaceId is required for a local service account audit event")
		}
		var accountMatches bool
		if err := execer.QueryRow(ctx, `
SELECT EXISTS (
    SELECT 1
    FROM service_accounts
    WHERE id = $1
      AND workspace_id = $2
      AND (
          NULLIF($3, '') IS NULL
          OR platform_service_account_id = $3
      )
)
`, event.ServiceAccountID, event.WorkspaceID, event.PlatformServiceAccountID).Scan(&accountMatches); err != nil {
			return fmt.Errorf("validate service account audit target: %w", err)
		}
		if !accountMatches {
			return conflict("service account audit target belongs to a different workspace or platform principal")
		}
	}
	if event.PlatformServiceAccountID != "" {
		var platformExists bool
		if err := execer.QueryRow(ctx, `
SELECT EXISTS (SELECT 1 FROM platform_service_accounts WHERE id = $1)
`, event.PlatformServiceAccountID).Scan(&platformExists); err != nil {
			return fmt.Errorf("validate platform service account audit target: %w", err)
		}
		if !platformExists {
			return conflict("platform service account audit target does not exist")
		}
	}
	event.CreatedAt = utcOrNow(event.CreatedAt)
	if _, err := execer.Exec(ctx, `
INSERT INTO service_account_audit_events (
    id, workspace_id, service_account_id, platform_service_account_id,
    event_type, subject_type, subject_id, subject_version,
    actor_type, actor_id, caused_by_actor_type, caused_by_actor_id,
    execution_actor_type, execution_actor_id,
    permission_basis_kind, permission_basis_ref, correlation_id,
    metadata, created_at
) VALUES (
    $1, $2, $3, $4,
    $5, $6, $7, $8,
    $9, $10, $11, $12,
    $13, $14,
    $15, $16, $17,
    $18, $19
)
`, event.ID, nullableString(event.WorkspaceID), nullableString(event.ServiceAccountID), nullableString(event.PlatformServiceAccountID),
		event.EventType, event.SubjectType, event.SubjectID, event.SubjectVersion,
		event.ActorType, event.ActorID, trim(event.CausedByActorType), trim(event.CausedByActorID),
		trim(event.ExecutionActorType), trim(event.ExecutionActorID),
		trim(event.PermissionBasisKind), trim(event.PermissionBasisRef), trim(event.CorrelationID),
		event.Metadata, event.CreatedAt); err != nil {
		return fmt.Errorf("insert service account audit event: %w", err)
	}
	return nil
}

// LoadCreatedAuditMetadata loads the immutable receipt attached to the first
// creation event for one local service account.
func (store *TxStore) LoadCreatedAuditMetadata(ctx context.Context, workspaceID string, accountID string) ([]byte, error) {
	q, err := txExecer(store)
	if err != nil {
		return nil, err
	}
	var metadata []byte
	if err := q.QueryRow(ctx, `
SELECT metadata
FROM service_account_audit_events
WHERE workspace_id = $1
  AND service_account_id = $2
  AND event_type = 'service_account.created'
ORDER BY created_at ASC, id ASC
LIMIT 1
`, trim(workspaceID), trim(accountID)).Scan(&metadata); err != nil {
		if coredb.IsNotFound(err) {
			return nil, notFound("service account creation audit event not found")
		}
		return nil, fmt.Errorf("load service account creation audit metadata: %w", err)
	}
	return metadata, nil
}

func (store *Store) ListAuditEvents(ctx context.Context, workspaceID string, accountID string, limit int) ([]AuditEvent, error) {
	q, err := queryer(store)
	if err != nil {
		return nil, err
	}
	if limit <= 0 || limit > 100 {
		return nil, badRequest("limit must be between 1 and 100")
	}
	rows, err := q.Query(ctx, `
SELECT id, COALESCE(workspace_id, ''), COALESCE(service_account_id, ''),
       COALESCE(platform_service_account_id, ''), event_type, subject_type,
       subject_id, subject_version, actor_type, actor_id,
       caused_by_actor_type, caused_by_actor_id,
       execution_actor_type, execution_actor_id,
       permission_basis_kind, permission_basis_ref, correlation_id,
       metadata, created_at
FROM service_account_audit_events
WHERE workspace_id = $1 AND service_account_id = $2
ORDER BY created_at DESC, id DESC
LIMIT $3
`, trim(workspaceID), trim(accountID), limit)
	if err != nil {
		return nil, fmt.Errorf("list service account audit events: %w", err)
	}
	defer rows.Close()
	events := make([]AuditEvent, 0)
	for rows.Next() {
		var event AuditEvent
		if err := rows.Scan(
			&event.ID,
			&event.WorkspaceID,
			&event.ServiceAccountID,
			&event.PlatformServiceAccountID,
			&event.EventType,
			&event.SubjectType,
			&event.SubjectID,
			&event.SubjectVersion,
			&event.ActorType,
			&event.ActorID,
			&event.CausedByActorType,
			&event.CausedByActorID,
			&event.ExecutionActorType,
			&event.ExecutionActorID,
			&event.PermissionBasisKind,
			&event.PermissionBasisRef,
			&event.CorrelationID,
			&event.Metadata,
			&event.CreatedAt,
		); err != nil {
			return nil, fmt.Errorf("scan service account audit event: %w", err)
		}
		events = append(events, event)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate service account audit events: %w", err)
	}
	return events, nil
}
