package extension

import (
	"context"
	"fmt"
	"strings"
	"time"
)

// InvocationAudit is the deliberately secret-free durable invocation record.
// Its type has no field capable of carrying provider credentials, headers,
// cookies, URLs, raw query values, body plaintext, connection IDs, or grant IDs.
type InvocationAudit struct {
	ID                             string
	WorkspaceID                    string
	AgentID                        string
	ServiceInstallationExtensionID string
	ExtensionName                  string
	Domain                         string
	Action                         string
	DefinitionVersion              int
	DefinitionDigest               string
	CatalogVersion                 int64
	RequestHash                    string
	ExecutionStatusClass           string
	ResultClassification           string
	Latency                        time.Duration
	RequestBytes                   int64
	ResponseBytes                  int64
	OccurredAt                     time.Time
}

func (store *Store) WriteInvocationAudit(
	ctx context.Context,
	record InvocationAudit,
) error {
	return store.writeInvocationAudit(ctx, record, false)
}

// WriteInvocationAuditOnce inserts one immutable fact by caller-owned ID.
// Replaying that ID is a no-op: the first fact wins and a later conservative
// classification cannot rewrite it.
func (store *Store) WriteInvocationAuditOnce(
	ctx context.Context,
	record InvocationAudit,
) error {
	return store.writeInvocationAudit(ctx, record, true)
}

func (store *Store) writeInvocationAudit(
	ctx context.Context,
	record InvocationAudit,
	insertOnce bool,
) error {
	if err := store.Validate(); err != nil {
		return err
	}
	if record.ID == "" || record.WorkspaceID == "" || record.AgentID == "" ||
		record.ServiceInstallationExtensionID == "" ||
		record.ExtensionName == "" ||
		record.Domain == "" ||
		record.Action == "" ||
		record.DefinitionVersion <= 0 || record.CatalogVersion <= 0 ||
		!validSHA256Identity(record.DefinitionDigest) ||
		!validSHA256Identity(record.RequestHash) ||
		record.ExecutionStatusClass == "" || record.ResultClassification == "" ||
		record.Latency < 0 || record.RequestBytes < 0 || record.ResponseBytes < 0 {
		return fmt.Errorf("extension invocation audit is incomplete")
	}
	occurredAt := record.OccurredAt.UTC()
	if occurredAt.IsZero() {
		return fmt.Errorf("extension invocation audit occurredAt is required")
	}
	statement := `
INSERT INTO extension_invocation_audits (
    id, workspace_id, agent_id, service_installation_extension_id,
    extension_name, domain, action,
    definition_version, definition_digest, catalog_version, request_hash,
    execution_status_class, result_classification, latency_ms,
    request_bytes, response_bytes, occurred_at
) VALUES (
    $1, $2, $3, $4,
    $5, $6, $7,
    $8, $9, $10, $11,
    $12, $13, $14,
    $15, $16, $17
)
`
	if insertOnce {
		statement += "ON DUPLICATE KEY UPDATE id = VALUES(id)"
	}
	_, err := store.db.Exec(ctx, statement,
		record.ID, record.WorkspaceID, record.AgentID,
		record.ServiceInstallationExtensionID,
		record.ExtensionName, record.Domain, record.Action,
		record.DefinitionVersion, record.DefinitionDigest,
		record.CatalogVersion, record.RequestHash,
		record.ExecutionStatusClass, record.ResultClassification,
		record.Latency.Milliseconds(), record.RequestBytes, record.ResponseBytes,
		occurredAt,
	)
	if err != nil {
		return fmt.Errorf("write extension invocation audit: %w", err)
	}
	return nil
}

func validSHA256Identity(value string) bool {
	if !strings.HasPrefix(value, "sha256:") || len(value) != len("sha256:")+64 {
		return false
	}
	for _, ch := range value[len("sha256:"):] {
		if (ch < '0' || ch > '9') && (ch < 'a' || ch > 'f') {
			return false
		}
	}
	return true
}
