package catalogsystem

import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"

	systemauthorityv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/systemlayoutbundleauthorityv1"
	"github.com/pingcap-inc/nexuscrm-controlplane/internal/catalogsystemprotocol"
)

// New constructs an unprovisioned authority.
func New(db *sql.DB) (*Authority, error) {
	if db == nil {
		return nil, errors.New("catalog system slot authority: database is required")
	}
	return &Authority{db: db, identity: &authorityIdentity{}}, nil
}

// bindAuthenticatedWriter creates a dormant process-local writer after a
// concrete control-plane admission path has supplied one authenticated
// principal binding and an exact reviewed key/operation grant set. No public
// self-asserted writer constructor exists in this gate.
func (a *Authority) bindAuthenticatedWriter(
	principal authenticatedPrincipal,
	grants []writerGrant,
) (*systemSlotWriter, error) {
	if a == nil || a.db == nil || a.identity == nil {
		return nil, ErrUnauthorized
	}
	if err := validatePrincipal(principal); err != nil {
		return nil, err
	}
	if len(grants) == 0 {
		return nil, errors.New("catalog system slot authority: at least one writer grant is required")
	}
	authorized := make(map[systemauthorityv1.BundleKey]writerOperations, len(grants))
	for index, grant := range grants {
		if err := systemauthorityv1.ValidateBundleKey(grant.BundleKey); err != nil {
			return nil, fmt.Errorf("writer grant %d: %w", index, err)
		}
		if !grant.AllowCreate && !grant.AllowRetire {
			return nil, fmt.Errorf("writer grant %d authorizes no operation", index)
		}
		if _, exists := authorized[grant.BundleKey]; exists {
			return nil, fmt.Errorf("writer grant %d duplicates a BundleKey", index)
		}
		authorized[grant.BundleKey] = writerOperations{
			create: grant.AllowCreate,
			retire: grant.AllowRetire,
		}
	}
	return &systemSlotWriter{
		authority: a,
		identity:  a.identity,
		principal: principal,
		grants:    authorized,
	}, nil
}

func (w *systemSlotWriter) authorize(
	request systemauthorityv1.SystemLayoutBundleSlotCommandRequest,
) error {
	if w == nil || w.authority == nil || w.authority.db == nil ||
		w.identity == nil || w.identity != w.authority.identity {
		return ErrUnauthorized
	}
	operations, exists := w.grants[request.TargetBundleKey]
	if !exists {
		return ErrUnauthorized
	}
	switch request.Operation {
	case systemauthorityv1.OperationCreate:
		if !operations.create {
			return ErrUnauthorized
		}
	case systemauthorityv1.OperationRetire:
		if !operations.retire {
			return ErrUnauthorized
		}
	default:
		return ErrUnauthorized
	}
	return nil
}

func validatePrincipal(principal authenticatedPrincipal) error {
	if err := catalogsystemprotocol.ValidatePrincipal(
		principal.PrincipalID,
		principal.AuditDigest,
	); err != nil {
		return fmt.Errorf("catalog system slot authority: principal binding: %w", err)
	}
	return nil
}

// MarshalJSON rejects attempts to transfer or persist live writer authority.
func (w *systemSlotWriter) MarshalJSON() ([]byte, error) {
	return nil, errors.New("catalog system slot writer capability has no JSON form")
}

var _ json.Marshaler = (*systemSlotWriter)(nil)
