package controlplane

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/pingcap-inc/nexuscrm-controlplane/packs"
	"github.com/pingcap-inc/nexuscrm-controlplane/packspec"
	metadatafeature "github.com/nexuscrm/metadata"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	schemacatalog "github.com/pingcap-inc/nexuscrm-contract/pkg/schemacatalog"
)

func buildL0Oracle() (*l0Oracle, error) {
	o := &l0Oracle{
		objectNames:        map[string]struct{}{},
		tableNames:         map[string]struct{}{},
		fieldKeys:          map[string]struct{}{},
		relationshipKeys:   map[string]struct{}{},
		listViewKeys:       map[string]struct{}{},
		runtimePackNames:   map[string]struct{}{},
		declaredComponents: map[string]packs.DeclaredComponent{},
		permissionAPINames: map[string]struct{}{},
		profileIDs:         map[string]struct{}{},
	}
	logicalSchemas := map[string]*models.ObjectMetadata{}

	for _, def := range managedTableDefinitions() {
		addSet(o.objectNames, def.TableName)
		addSet(o.tableNames, def.TableName)
		schema := logicalSchemas[strings.ToLower(def.TableName)]
		if schema == nil {
			schema = &models.ObjectMetadata{APIName: def.TableName}
			logicalSchemas[strings.ToLower(def.TableName)] = schema
		}
		for _, col := range def.Columns {
			addSet(o.fieldKeys, fieldComponentKey(def.TableName, col.Name))
			schema.Fields = append(schema.Fields, models.FieldMetadata{APIName: col.Name})
			for _, parent := range col.ReferenceTo {
				addSet(o.relationshipKeys, relationshipComponentKey(def.TableName, col.Name, parent))
			}
		}
	}
	addSet(o.tableNames, l0BootstrapStateTable)

	systemSpecs, err := packspec.StandardObjectsSpecByCategoryJSON("system")
	if err != nil {
		return nil, fmt.Errorf("load platform-core object specs: %w", err)
	}
	var specs []struct {
		APIName  string `json:"apiName"`
		Category string `json:"category"`
		Fields   []struct {
			APIName     string   `json:"apiName"`
			ReferenceTo []string `json:"referenceTo,omitempty"`
		} `json:"fields,omitempty"`
		ManagedRelationships []struct {
			FieldAPIName string `json:"fieldApiName"`
			ParentObject string `json:"parentObject"`
		} `json:"managedRelationships,omitempty"`
	}
	if err := json.Unmarshal(systemSpecs, &specs); err != nil {
		return nil, fmt.Errorf("parse platform-core object specs: %w", err)
	}
	for _, spec := range specs {
		addSet(o.objectNames, spec.APIName)
		addSet(o.tableNames, spec.APIName)
		schema := logicalSchemas[strings.ToLower(spec.APIName)]
		if schema == nil {
			schema = &models.ObjectMetadata{APIName: spec.APIName}
			logicalSchemas[strings.ToLower(spec.APIName)] = schema
		}
		for _, field := range spec.Fields {
			addSet(o.fieldKeys, fieldComponentKey(spec.APIName, field.APIName))
			schema.Fields = append(schema.Fields, models.FieldMetadata{APIName: field.APIName})
			for _, parent := range field.ReferenceTo {
				addSet(o.relationshipKeys, relationshipComponentKey(spec.APIName, field.APIName, parent))
			}
		}
		for _, rel := range spec.ManagedRelationships {
			addSet(o.relationshipKeys, relationshipComponentKey(spec.APIName, rel.FieldAPIName, rel.ParentObject))
		}
		// RecordType is currently the only system-category object spec
		// materialized through the object-schema path that injects the platform's
		// standard audit/identity fields even when the spec omits them.
		if injectStandardSystemFieldsForL0Oracle(spec.APIName, spec.Category) {
			for _, fieldName := range constants.StandardSystemFields() {
				addSet(o.fieldKeys, fieldComponentKey(spec.APIName, fieldName))
			}
		}
	}
	for _, schema := range logicalSchemas {
		for _, fieldName := range metadatafeature.PlatformCompoundVirtualFieldNames(schema) {
			addSet(o.fieldKeys, fieldComponentKey(schema.APIName, fieldName))
		}
	}
	for profileID := range constants.PlatformDefaultProfileIDs {
		addSet(o.profileIDs, profileID)
	}

	runtimePacks := packs.RuntimeRequiredPacks()
	for _, p := range runtimePacks {
		addSet(o.runtimePackNames, p.Name())
	}
	declaredComponents, err := packs.DeclaredComponentsForPacks(runtimePacks)
	if err != nil {
		return nil, fmt.Errorf("load runtime pack component manifests: %w", err)
	}
	for _, component := range declaredComponents {
		o.declaredComponents[declaredComponentKey(component.PackName, component.Kind, component.BusinessKey)] = component
	}

	permissions, err := schemacatalog.StandardPermissions()
	if err != nil {
		return nil, fmt.Errorf("load platform permissions: %w", err)
	}
	for _, row := range permissions {
		addSet(o.permissionAPINames, row.APIName)
	}

	apps, err := packs.PlatformBootstrapConnectedApps()
	if err != nil {
		return nil, err
	}
	o.connectedApps = apps

	o.objectNamesSorted = sortedSet(o.objectNames)
	o.tableNamesSorted = sortedSet(o.tableNames)
	o.profileIDsSorted = sortedSet(o.profileIDs)
	o.runtimePacksSorted = sortedSet(o.runtimePackNames)
	o.permissionsSorted = sortedSet(o.permissionAPINames)
	return o, nil
}

func injectStandardSystemFieldsForL0Oracle(objectAPIName, category string) bool {
	return strings.EqualFold(category, "system") && strings.EqualFold(objectAPIName, "RecordType")
}
