package runtimeapi

import (
	"context"
	"encoding/json"
	"errors"
	"math"
	"sort"
	"time"

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/pingcap-inc/nexuscrm-api-host/runtimegrpc"
	metadataadminsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/metadataadminreadv1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/mapping"
)

// MetadataAdminReadEngine is the existing repository-backed neutral metadata
// adapter consumed by the independent Runtime. It deliberately excludes every
// metadata mutation and every presentation-layout resolver.
type MetadataAdminReadEngine interface {
	ListObjects(context.Context) ([]models.ObjectMetadata, error)
	ListLayouts(context.Context, string) ([]*models.PageLayout, error)
	ListLayoutAssignments(context.Context, string) ([]*models.LayoutAssignment, error)
	GetLayoutForAuthoring(context.Context, string) (*models.PageLayout, error)
	ListValidationRules(context.Context, string) ([]*models.ValidationRule, error)
}

// MetadataAdminReadServer projects stored administration facts from the
// current MetadataService. SecurityAdmission supplies the delegated principal
// and MetadataAdminReadAdmission binds the exact workload. This server owns no
// Catalog projection, publication or mutable metadata path.
type MetadataAdminReadServer struct {
	runtimev1.UnimplementedMetadataAdminReadServiceServer
	metadata    MetadataAdminReadEngine
	permissions EffectivePermissionEngine
}

func NewMetadataAdminReadServer(
	metadata MetadataAdminReadEngine,
	permissions EffectivePermissionEngine,
) (*MetadataAdminReadServer, error) {
	if metadata == nil || permissions == nil {
		return nil, errors.New("runtime metadata administration read requires metadata and permission engines")
	}
	return &MetadataAdminReadServer{metadata: metadata, permissions: permissions}, nil
}

func (server *MetadataAdminReadServer) ReadObjectAdminIndex(
	ctx context.Context,
	wireRequest *runtimev1.ReadObjectAdminIndexRequest,
) (*runtimev1.ReadObjectAdminIndexResponse, error) {
	request, err := mapping.FromWireReadObjectAdminIndexRequest(ctx, wireRequest)
	if err != nil {
		return nil, metadataAdminReadStatus(err)
	}
	receipts, err := server.authorizeMetadataAdminRead(ctx, request.MinCatalogRevision)
	if err != nil {
		return nil, metadataAdminReadStatus(err)
	}
	objects, err := server.metadata.ListObjects(ctx)
	if err != nil {
		return nil, metadataAdminReadStatus(classifyMetadataAdminReadError(ctx, err))
	}
	projected := make([]metadataadminsemantic.ObjectAdminMetadata, 0, len(objects))
	for _, object := range objects {
		projected = append(projected, metadataadminsemantic.ObjectAdminMetadata{
			ObjectID: object.APIName, Description: cloneMetadataAdminString(object.Description),
			DeleteMode: object.DeleteMode,
		})
	}
	sort.Slice(projected, func(left, right int) bool {
		return projected[left].ObjectID < projected[right].ObjectID
	})
	response, err := mapping.ToWireReadObjectAdminIndexResponse(
		request,
		metadataadminsemantic.ObjectAdminIndexResponse{
			Objects:  projected,
			Receipts: receipts,
		},
	)
	if err != nil {
		return nil, metadataAdminReadStatus(err)
	}
	return response, nil
}

func (server *MetadataAdminReadServer) ReadLayoutAdminWorkspace(
	ctx context.Context,
	wireRequest *runtimev1.ReadLayoutAdminWorkspaceRequest,
) (*runtimev1.ReadLayoutAdminWorkspaceResponse, error) {
	request, err := mapping.FromWireReadLayoutAdminWorkspaceRequest(ctx, wireRequest)
	if err != nil {
		return nil, metadataAdminReadStatus(err)
	}
	receipts, err := server.authorizeMetadataAdminRead(ctx, request.MinCatalogRevision)
	if err != nil {
		return nil, metadataAdminReadStatus(err)
	}
	layouts, err := server.metadata.ListLayouts(ctx, request.ObjectID)
	if err != nil {
		return nil, metadataAdminReadStatus(classifyMetadataAdminReadError(ctx, err))
	}
	assignments, err := server.metadata.ListLayoutAssignments(ctx, request.ObjectID)
	if err != nil {
		return nil, metadataAdminReadStatus(classifyMetadataAdminReadError(ctx, err))
	}
	projectedLayouts := make([]metadataadminsemantic.LayoutSummary, 0, len(layouts))
	for _, layout := range layouts {
		if layout == nil {
			return nil, metadataAdminReadStatus(&metadataadminsemantic.Failure{Category: metadataadminsemantic.FailureInternal})
		}
		projectedLayouts = append(projectedLayouts, metadataadminsemantic.LayoutSummary{
			LayoutID: layout.ID, ObjectID: layout.ObjectAPIName, Name: layout.LayoutName,
			Type: layout.Type, IsDefault: layout.IsDefault,
			CreatedAt:      metadataAdminReadTimestamp(layout.CreatedDate),
			LastModifiedAt: metadataAdminReadTimestamp(layout.LastModifiedDate),
		})
	}
	sort.Slice(projectedLayouts, func(left, right int) bool {
		return projectedLayouts[left].LayoutID < projectedLayouts[right].LayoutID
	})
	projectedAssignments := make([]metadataadminsemantic.LayoutAssignment, 0, len(assignments))
	for _, assignment := range assignments {
		if assignment == nil || assignment.Specificity < 0 || uint64(assignment.Specificity) > math.MaxUint32 {
			return nil, metadataAdminReadStatus(&metadataadminsemantic.Failure{Category: metadataadminsemantic.FailureInternal})
		}
		projectedAssignments = append(projectedAssignments, metadataadminsemantic.LayoutAssignment{
			AssignmentID: assignment.ID, ObjectID: assignment.ObjectAPIName, LayoutID: assignment.LayoutID,
			Dimensions: metadataAdminReadAssignmentDimensions(assignment), Specificity: uint32(assignment.Specificity),
			CreatedAt:      metadataAdminReadTimestamp(assignment.CreatedDate),
			LastModifiedAt: metadataAdminReadTimestamp(assignment.LastModifiedDate),
			CreatedByID:    cloneMetadataAdminString(assignment.CreatedByID),
		})
	}
	sort.Slice(projectedAssignments, func(left, right int) bool {
		return projectedAssignments[left].AssignmentID < projectedAssignments[right].AssignmentID
	})
	response, err := mapping.ToWireReadLayoutAdminWorkspaceResponse(
		request,
		metadataadminsemantic.LayoutAdminWorkspaceResponse{
			ObjectID: request.ObjectID, Layouts: projectedLayouts, Assignments: projectedAssignments,
			Receipts: receipts,
		},
	)
	if err != nil {
		return nil, metadataAdminReadStatus(err)
	}
	return response, nil
}

func (server *MetadataAdminReadServer) ReadStoredLayout(
	ctx context.Context,
	wireRequest *runtimev1.ReadStoredLayoutRequest,
) (*runtimev1.ReadStoredLayoutResponse, error) {
	request, err := mapping.FromWireReadStoredLayoutRequest(ctx, wireRequest)
	if err != nil {
		return nil, metadataAdminReadStatus(err)
	}
	receipts, err := server.authorizeMetadataAdminRead(ctx, request.MinCatalogRevision)
	if err != nil {
		return nil, metadataAdminReadStatus(err)
	}
	layout, err := server.metadata.GetLayoutForAuthoring(ctx, request.LayoutID)
	if err != nil {
		return nil, metadataAdminReadStatus(classifyMetadataAdminReadError(ctx, err))
	}
	if layout == nil {
		return nil, metadataAdminReadStatus(&metadataadminsemantic.Failure{Category: metadataadminsemantic.FailureMissing})
	}
	document, err := json.Marshal(layout)
	if err != nil {
		return nil, metadataAdminReadStatus(&metadataadminsemantic.Failure{Category: metadataadminsemantic.FailureInternal})
	}
	response, err := mapping.ToWireReadStoredLayoutResponse(
		request,
		metadataadminsemantic.StoredLayoutResponse{
			LayoutID: layout.ID, ObjectID: layout.ObjectAPIName, StoredDocument: document,
			Receipts: receipts,
		},
	)
	if err != nil {
		return nil, metadataAdminReadStatus(err)
	}
	return response, nil
}

func (server *MetadataAdminReadServer) ReadValidationRuleAdminIndex(
	ctx context.Context,
	wireRequest *runtimev1.ReadValidationRuleAdminIndexRequest,
) (*runtimev1.ReadValidationRuleAdminIndexResponse, error) {
	request, err := mapping.FromWireReadValidationRuleAdminIndexRequest(ctx, wireRequest)
	if err != nil {
		return nil, metadataAdminReadStatus(err)
	}
	receipts, err := server.authorizeMetadataAdminRead(ctx, request.MinCatalogRevision)
	if err != nil {
		return nil, metadataAdminReadStatus(err)
	}
	rules, err := server.metadata.ListValidationRules(ctx, request.ObjectID)
	if err != nil {
		return nil, metadataAdminReadStatus(classifyMetadataAdminReadError(ctx, err))
	}
	projected := make([]metadataadminsemantic.ValidationRuleMetadata, 0, len(rules))
	for _, rule := range rules {
		if rule == nil {
			return nil, metadataAdminReadStatus(&metadataadminsemantic.Failure{Category: metadataadminsemantic.FailureInternal})
		}
		bindings := make([]metadataadminsemantic.ValidationRuleRecordSetBinding, 0, len(rule.RecordSetBindings))
		for _, binding := range rule.RecordSetBindings {
			filters := make([]metadataadminsemantic.ValidationRuleRecordSetFilter, 0, len(binding.Filters))
			for _, filter := range binding.Filters {
				value, err := json.Marshal(filter.Value)
				if err != nil {
					return nil, metadataAdminReadStatus(&metadataadminsemantic.Failure{Category: metadataadminsemantic.FailureInternal})
				}
				filters = append(filters, metadataadminsemantic.ValidationRuleRecordSetFilter{
					Field: filter.Field, Operator: filter.Operator, ValueSource: filter.ValueSource, Value: value,
				})
			}
			bindings = append(bindings, metadataadminsemantic.ValidationRuleRecordSetBinding{
				Name: binding.Name, Mode: binding.Mode, ObjectID: binding.ObjectAPIName,
				FilterLogic: binding.FilterLogic, Filters: filters,
			})
		}
		projected = append(projected, metadataadminsemantic.ValidationRuleMetadata{
			RuleID: rule.ID, ObjectID: request.ObjectID, Name: rule.Name,
			Description: cloneMetadataAdminString(rule.Description), Active: rule.Active,
			Condition: rule.Condition, ErrorMessage: rule.ErrorMessage,
			ErrorDisplayField: cloneMetadataAdminString(rule.ErrorDisplayField), RecordSetBindings: bindings,
			IsCustomized: rule.IsCustomized, OwnerPack: cloneMetadataAdminString(rule.OwnerPack),
		})
	}
	sort.Slice(projected, func(left, right int) bool {
		return projected[left].RuleID < projected[right].RuleID
	})
	response, err := mapping.ToWireReadValidationRuleAdminIndexResponse(
		request,
		metadataadminsemantic.ValidationRuleAdminIndexResponse{
			ObjectID: request.ObjectID, Rules: projected, Receipts: receipts,
		},
	)
	if err != nil {
		return nil, metadataAdminReadStatus(err)
	}
	return response, nil
}

func metadataAdminReadAuthority(
	ctx context.Context,
	minimum *uint64,
) (metadataadminsemantic.Receipts, TrustedCall, error) {
	currentCapability, current := runtimegrpc.Capability(ctx)
	trusted, ok := trustedCallFromContext(ctx)
	if !current || currentCapability != metadataadminsemantic.CapabilityName ||
		!ok || !containsCapability(trusted.capabilities, metadataadminsemantic.CapabilityName) {
		return metadataadminsemantic.Receipts{}, TrustedCall{}, &metadataadminsemantic.Failure{
			Category: metadataadminsemantic.FailureUnavailable,
		}
	}
	// This port reads the current repository-backed MetadataService and does
	// not claim a Catalog publication revision. A caller that asks for one must
	// fail closed instead of receiving a fabricated binding.
	if minimum != nil {
		return metadataadminsemantic.Receipts{}, TrustedCall{}, &metadataadminsemantic.Failure{
			Category:         metadataadminsemantic.FailureStaleState,
			StaleAxis:        metadataadminsemantic.StaleAxisCatalog,
			RequiredRevision: *minimum,
			ServedRevision:   0,
		}
	}
	return metadataadminsemantic.Receipts{
		ProtocolVersion:   "1.0",
		SecurityEpoch:     trusted.securityEpoch,
		CapabilityVersion: metadataadminsemantic.CapabilityName,
	}, trusted, nil
}

func (server *MetadataAdminReadServer) authorizeMetadataAdminRead(
	ctx context.Context,
	minimum *uint64,
) (metadataadminsemantic.Receipts, error) {
	receipts, trusted, err := metadataAdminReadAuthority(ctx, minimum)
	if err != nil {
		return metadataadminsemantic.Receipts{}, err
	}
	allowed, err := server.hasCustomizeApplication(ctx, trusted)
	if err != nil {
		return metadataadminsemantic.Receipts{}, err
	}
	if !allowed {
		return metadataadminsemantic.Receipts{}, &metadataadminsemantic.Failure{
			Category: metadataadminsemantic.FailureForbidden,
		}
	}
	return receipts, nil
}

func (server *MetadataAdminReadServer) hasCustomizeApplication(
	ctx context.Context,
	trusted TrustedCall,
) (bool, error) {
	user := trusted.UserSession()
	result, err := server.permissions.EffectivePermissions(ctx, &user, "", trusted.PermissionFloor())
	if err != nil {
		return false, classifyEffectivePermissionError(ctx, err)
	}
	if result == nil {
		return false, &metadataadminsemantic.Failure{Category: metadataadminsemantic.FailureInternal}
	}
	return result.SystemPermissions[string(constants.PermCustomizeApplication)], nil
}

func classifyMetadataAdminReadError(ctx context.Context, err error) error {
	return classifyPlatformEngineError(ctx, err, metadataadminsemantic.InvalidComponentOperation)
}

func metadataAdminReadStatus(err error) error {
	return mapping.ToPlatformStatusError(err, metadataadminsemantic.CapabilityName)
}

func cloneMetadataAdminString(value *string) *string {
	if value == nil {
		return nil
	}
	copy := *value
	return &copy
}

func metadataAdminReadAssignmentDimensions(
	assignment *models.LayoutAssignment,
) []metadataadminsemantic.LayoutAssignmentDimension {
	result := make([]metadataadminsemantic.LayoutAssignmentDimension, 0, 3)
	for _, dimension := range []struct {
		key   string
		value *string
	}{
		{key: "profile", value: assignment.ProfileID},
		{key: "record-type", value: assignment.RecordTypeID},
		{key: "role", value: assignment.RoleID},
	} {
		if dimension.value != nil {
			result = append(result, metadataadminsemantic.LayoutAssignmentDimension{
				Key: dimension.key, Value: *dimension.value,
			})
		}
	}
	return result
}

func metadataAdminReadTimestamp(value time.Time) string {
	if value.IsZero() {
		return ""
	}
	return value.UTC().Format(time.RFC3339Nano)
}
