package layoutroutingsecurity

import (
	"database/sql"
	"errors"
)

// ErrSchema reports that a routing-security table or table pair is absent or
// differs from the single strict-v2 shape.
var ErrSchema = errors.New("layout routing security: unexpected table shape")

var (
	// ErrViewNotFound reports that no immutable canonical view exists for the
	// requested exact org and digest.
	ErrViewNotFound = errors.New("layout routing security: canonical view not found")
	// ErrViewDigestCollision reports that an immutable key is already occupied
	// by bytes other than the caller's canonical bytes.
	ErrViewDigestCollision = errors.New("layout routing security: canonical view digest collision")
	// ErrTransactionRequired reports an attempt to use a canonical-view writer
	// without a transaction that was created by its verified view store.
	ErrTransactionRequired = errors.New("layout routing security: verified view-store transaction is required")
)

const (
	liveStateTableName         = "_System_RuntimeSecurityState"
	shadowStateTableName       = "_System_RuntimeSecurityStateV2Shadow"
	liveAttestationTableName   = "_System_LayoutRoutingSecurityAttestation"
	shadowAttestationTableName = "_System_LayoutRoutingSecurityAttestationV2Shadow"
	liveViewStoreTableName     = "_System_LayoutRoutingSecurityView"
	shadowViewStoreTableName   = "_System_LayoutRoutingSecurityViewV2Shadow"
)

type tableIdentity struct {
	name string
}

var (
	liveStateTableIdentity         = &tableIdentity{name: liveStateTableName}
	shadowStateTableIdentity       = &tableIdentity{name: shadowStateTableName}
	liveAttestationTableIdentity   = &tableIdentity{name: liveAttestationTableName}
	shadowAttestationTableIdentity = &tableIdentity{name: shadowAttestationTableName}
	liveViewStoreTableIdentity     = &tableIdentity{name: liveViewStoreTableName}
	shadowViewStoreTableIdentity   = &tableIdentity{name: shadowViewStoreTableName}
)

// VerifiedTables is an opaque capability for one exact strict-v2 table pair.
// Its fields intentionally cannot be initialized by callers.
type VerifiedTables struct {
	db          *sql.DB
	state       *tableIdentity
	attestation *tableIdentity
}

// VerifiedViewStore is an opaque capability for exactly one canonical
// routing-security view table. Its fields intentionally cannot be initialized
// by callers.
type VerifiedViewStore struct {
	db    *sql.DB
	table *tableIdentity
}

// VerifiedViewStoreTx is an opaque transaction capability created only by a
// VerifiedViewStore. It never exposes its raw *sql.Tx, so callers cannot pair
// an independently opened transaction with a verified store from another
// database.
type VerifiedViewStoreTx struct {
	store *VerifiedViewStore
	tx    *sql.Tx
}

func requireVerifiedTables(tables *VerifiedTables) (*sql.DB, *tableIdentity, *tableIdentity, error) {
	if tables == nil || tables.db == nil {
		return nil, nil, nil, ErrSchema
	}
	switch {
	case tables.state == liveStateTableIdentity && tables.attestation == liveAttestationTableIdentity:
		return tables.db, tables.state, tables.attestation, nil
	case tables.state == shadowStateTableIdentity && tables.attestation == shadowAttestationTableIdentity:
		return tables.db, tables.state, tables.attestation, nil
	default:
		return nil, nil, nil, ErrSchema
	}
}

func requireVerifiedViewStore(store *VerifiedViewStore) (*sql.DB, *tableIdentity, error) {
	if store == nil || store.db == nil {
		return nil, nil, ErrSchema
	}
	switch store.table {
	case liveViewStoreTableIdentity, shadowViewStoreTableIdentity:
		return store.db, store.table, nil
	default:
		return nil, nil, ErrSchema
	}
}
