package analytics

import (
	"context"
	"database/sql"
	"strings"

	domainrls "github.com/nexuscrm/kernel/pkg/rls"
	"github.com/nexuscrm/shared/pkg/constants"
	neutralcontract "github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/models"
)

// MetadataSource is the analytics-local consumer interface for schema metadata.
// The backend MetadataSource satisfies it structurally and is
// injected at the composition root. Defining it here severs the analytics ->
// metadata (github.com/nexuscrm/metadata) edge so analytics can live in its own
// sibling module.
type MetadataSource interface {
	GetSchema(ctx context.Context, apiName string) *models.ObjectMetadata
	GetSchemas(ctx context.Context) []*models.ObjectMetadata
	GetValidationRules(ctx context.Context, objectAPIName string) []*models.ValidationRule
	GetSystemFields(ctx context.Context, objectAPIName string) []string
	GetChildRelationships(ctx context.Context, parentObjectAPIName string) []*models.ObjectMetadata
	TargetObjectsForForeignKey(ctx context.Context, objectAPIName, foreignKey string) ([]string, bool)
	ForeignKeyForRelationship(ctx context.Context, objectAPIName, relationshipName string) (string, bool)
}

// PermissionChecker is the analytics-local consumer interface for the
// permission/RLS service. The backend PermissionChecker satisfies
// it structurally and is injected at the composition root. RLS results are
// expressed in terms of the kernel rls.Result type (the backend
// domainrls.Result is a type alias for it), so there is no backend type
// edge.
type PermissionChecker interface {
	CheckObjectPermissionWithUser(ctx context.Context, objectAPIName string, operation string, user *models.UserSession) bool
	CheckFieldVisibilityWithUser(ctx context.Context, objectAPIName, fieldAPIName string, user *models.UserSession) (bool, error)
	CheckRecordAccess(ctx context.Context, schema *models.ObjectMetadata, record models.SObject, operation string, user *models.UserSession) bool
	PrimeReadAccessInputCache(ctx context.Context, objectAPIName string, records []models.SObject, user *models.UserSession)
	BuildRLSResult(ctx context.Context, schema *models.ObjectMetadata, objectName string, user *models.UserSession) *domainrls.Result
	IsFieldReadBlocked(ctx context.Context, objectAPIName, fieldAPIName string) bool
	IsBlockedObject(ctx context.Context, objectAPIName string, operation string) bool
	GetSubordinateUserIDs(ctx context.Context, roleID string) ([]string, error)
	GetPermissionSetGrantsMany(ctx context.Context, permSetIDs []string) (map[string]map[string]bool, error)
}

// SchemaBoundFieldPermissionChecker is an optional bulk-read optimization.
// Independent Runtime metadata returns detached object snapshots, so a caller
// that already owns the request-bound schema must not resolve it again for
// every field. Implementations must preserve CheckFieldVisibilityWithUser's
// authorization semantics against the supplied immutable schema.
type SchemaBoundFieldPermissionChecker interface {
	CheckFieldVisibilityWithSchema(ctx context.Context, schema *models.ObjectMetadata, field *models.FieldMetadata, user *models.UserSession) (bool, error)
}

// MetadataProvider is the narrow subset of MetadataSource needed by
// SecurityValidator's SQL-rewrite path.
type MetadataProvider interface {
	GetSchema(ctx context.Context, objectName string) *models.ObjectMetadata
}

// HistoryViewResolver resolves the physical history table/view name for an
// object. Analytics' own FormulaViewService satisfies it and is injected at the
// composition root; the audit backend consumes the same shape.
type HistoryViewResolver interface {
	ResolveTableName(ctx context.Context, schema *models.ObjectMetadata) string
}

// HistoryQuery executes a neutral field-history query. The audit package owns
// the concrete RLS/FLS/SQL implementation; analytics owns the public contract.
type HistoryQuery interface {
	QueryHistory(ctx context.Context, req neutralcontract.HistoryQueryRequest, user *models.UserSession) (*neutralcontract.HistoryQueryResult, error)
}

// HistoryQueryFactory builds a HistoryQuery from the analytics-side
// dependencies. The composition root injects an audit-backed adapter that closes
// over audit.NewHistoryQueryService, inverting the analytics -> audit edge.
type HistoryQueryFactory interface {
	Build(db *sql.DB, metadata MetadataSource, permissions PermissionChecker, views HistoryViewResolver) HistoryQuery
}

// ---- Carried neutral helpers (formerly in features/permissions) ----
// These are package-level helpers whose only non-test consumer is analytics.
// They operate purely on kernel/shared types, so they are carried into
// analytics rather than kept in the backend permissions package.

// FieldReadBlockedByMetadata reports whether a field is read-blocked by its
// metadata-declared ReadProtectionPolicy.
func FieldReadBlockedByMetadata(field *models.FieldMetadata) bool {
	if field == nil {
		return false
	}
	return strings.EqualFold(strings.TrimSpace(field.ReadProtectionPolicy), constants.FieldReadProtectionBlocked)
}

// ProjectPermissionGrantFieldsOntoRecord projects system-permission virtual
// fields onto a record using the resolved grant map.
func ProjectPermissionGrantFieldsOntoRecord(record models.SObject, fields []models.FieldMetadata, grants map[string]bool) {
	for _, f := range fields {
		if !f.IsVirtual || f.VirtualKind != constants.VirtualFieldSystemPermission {
			continue
		}
		record[f.APIName] = grants[f.VirtualSourceAPIName]
	}
}

// RLSResultToFilterExpr renders an RLS result into a SQL WHERE fragment for the
// given table qualifier.
func RLSResultToFilterExpr(result *domainrls.Result, tableQualifier string) string {
	return domainrls.ToSQLFilter(result, tableQualifier)
}
