package permissions

import (
	"context"
	"strings"

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

// SystemPermissionVirtualFieldPrefix is the wire-shape prefix a
// PermissionSet virtual field surfaces under. "ViewSetup" in the catalog
// is seen by Salesforce clients as "PermissionsViewSetup" on the
// PermissionSet SObject. The prefix lives here so the decorator is the
// single code point that writes it; downstream handlers read
// FieldMetadata.VirtualSourceAPIName instead of stripping the prefix
// themselves.
const SystemPermissionVirtualFieldPrefix = "Permissions"

// catalogLister is the subset of PermissionService the decorator needs.
// Declared here (where it is used) rather than importing the full
// service so the dependency surface is explicit.
type catalogLister interface {
	ListSystemPermissions(ctx context.Context) ([]models.SystemPermission, error)
}

// SchemaDecorator synthesizes PermissionSet's Permissions<X> virtual
// fields from the _System_Permission catalog at metadata cache-build
// time. Exactly one instance is registered on MetadataService.
//
// The decorator is idempotent: applying it twice to the same cached
// schema produces the same effective schema (the second application
// would collide on APINames and the later entry wins, see
// models.CloneWithAdditionalFields). In practice the decorator runs
// once per cache generation — callers never re-decorate a decorated
// schema because MetadataService applies decorators to the raw
// repository output.
type SchemaDecorator struct {
	catalog catalogLister
}

// NewSchemaDecorator constructs a decorator backed by the given
// permission service. catalog must be non-nil; a nil catalog would make
// PermissionSet's virtual field surface empty, which is indistinguishable
// from "catalog read failed" and masks a wiring bug.
func NewSchemaDecorator(catalog catalogLister) *SchemaDecorator {
	if catalog == nil {
		panic("permissions.NewSchemaDecorator: nil catalog")
	}
	return &SchemaDecorator{catalog: catalog}
}

// DecorateSchema returns schema unchanged for every object except
// PermissionSet. For PermissionSet it reads the active permission
// catalog and returns a clone whose Fields slice additionally contains
// one synthesized boolean virtual field per catalog row.
//
// Capability bits on the synthesized fields:
//   - Type: boolean, Nillable: false, DefaultValue: false (on the wire).
//   - Createable, Updateable: true — matches Salesforce's PermissionSet
//     describe shape.
//   - Filterable, Sortable, Groupable: NOT set here. The first version
//     of this refactor does not rewrite SOQL WHERE / ORDER BY on
//     permission virtual fields to junction-table logic; declaring
//     those capabilities true would make describe lie about what the
//     query engine can honor. A later version may add junction rewrite
//     and flip these bits.
//
// The catalog query is wrapped in a context and the decorator returns
// the input schema unchanged (not an error) when the read fails — a
// degraded PermissionSet schema is better than an aborted full-cache
// refresh that disables unrelated features. The error is logged at
// the catalog layer; callers detect the condition via the empty
// virtual-field set surfacing in describe.
func (d *SchemaDecorator) DecorateSchema(ctx context.Context, schema *models.ObjectMetadata) (*models.ObjectMetadata, error) {
	if schema == nil {
		return nil, nil
	}
	if !strings.EqualFold(schema.APIName, constants.TablePermissionSet) {
		return schema, nil
	}

	catalog, err := d.catalog.ListSystemPermissions(ctx)
	if err != nil {
		// Degrade gracefully: PermissionSet loses its virtual field
		// surface this generation; the next cache refresh will retry.
		// Returning an error here would fail buildCoreSnapshot and take
		// the whole metadata cache down with it.
		return schema, nil
	}

	additions := make([]models.FieldMetadata, 0, len(catalog))
	for _, p := range catalog {
		if !p.IsActive {
			continue
		}
		additions = append(additions, models.FieldMetadata{
			APIName:              SystemPermissionVirtualFieldPrefix + p.APIName,
			Label:                p.Label,
			Type:                 constants.FieldTypeBoolean,
			IsSystem:             true,
			Createable:           true,
			Updateable:           true,
			IsVirtual:            true,
			VirtualKind:          constants.VirtualFieldSystemPermission,
			VirtualSourceAPIName: p.APIName,
		})
	}

	return models.CloneWithAdditionalFields(schema, additions), nil
}
