package createsourceguard

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

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/assertion"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/connector"
)

// standardObjectFieldMappingOwner is copied verbatim from
// backend/internal/bootstrap/field_mappings.go (the original definition stays
// there for that package's other callers) so the relocated owner-scoped
// coverage default does not change behavior.
const standardObjectFieldMappingOwner = "standard"

// VerifyCreateFromSourceGuardCoverageForFieldMappingGroups is the owner-scoped
// pack/overlay verification hook for SourceLookupField-backed create-from-
// source guard coverage. It checks only the field-mapping groups declared by
// the owner currently being verified, and only against StandardActions stamped
// with the same OwnerPack, so it is safe for source-backed pack/component
// verify without reading unrelated tenant or peer-pack metadata.
func VerifyCreateFromSourceGuardCoverageForFieldMappingGroups(db *sql.DB, groups []connector.FieldMappingGroup, owner string) error {
	if db == nil || !fieldMappingGroupsNeedCreateFromSourceGuardCoverage(groups) {
		return nil
	}
	result := &assertion.AssertionResult{Violations: []assertion.AssertionViolation{}}
	assertOwnerScopedCreateFromSourceGuardCoverage(db, groups, owner, result)
	if len(result.Violations) == 0 {
		return nil
	}
	var b strings.Builder
	fmt.Fprintf(&b, "%d violation(s)", len(result.Violations))
	for _, violation := range result.Violations {
		fmt.Fprintf(&b, "; [%s] %s: %s", violation.Severity, violation.Object, violation.Description)
	}
	return fmt.Errorf("%s", b.String())
}

func fieldMappingGroupsNeedCreateFromSourceGuardCoverage(groups []connector.FieldMappingGroup) bool {
	for _, group := range groups {
		if len(group.Mappings) == 0 {
			continue
		}
		if createFromSourceTupleNeedsGuardCoverage(effectiveCreateFromSourceTuple{
			SourceObject: strings.TrimSpace(group.SourceObject),
			TargetObject: strings.TrimSpace(group.TargetObject),
			ActionType:   strings.TrimSpace(group.ActionType),
		}) {
			return true
		}
	}
	return false
}

func assertOwnerScopedCreateFromSourceGuardCoverage(db *sql.DB, groups []connector.FieldMappingGroup, owner string, result *assertion.AssertionResult) {
	facts := createFromSourceMappingFactsFromGroups(groups)
	if len(facts.Mappings) == 0 && len(facts.Tuples) == 0 {
		return
	}
	if err := validateCreateFromSourceLookupFields(context.Background(), db, facts.Mappings); err != nil {
		result.Violations = append(result.Violations, createFromSourceGuardCoverageAssertionFailure(
			"_System_ObjectFieldMapping",
			fmt.Sprintf("could not verify owner-scoped create-from-source guard coverage: %v", err),
		))
		return
	}
	flows, err := getActiveStandardActionFlowsForCreateSourceOwner(db, owner)
	if err != nil {
		result.Violations = append(result.Violations, createFromSourceGuardCoverageAssertionFailure(
			"_System_StandardAction",
			fmt.Sprintf("could not verify owner-scoped create-from-source guard action flows for owner %q: %v", owner, err),
		))
		return
	}
	ownerIndex := buildCreateFromSourceFlowOwnerIndex(flows)
	mappings, err := expandCreateFromSourceGuardMappingsFromFlowAssignments(context.Background(), db, facts, ownerIndex)
	if err != nil {
		result.Violations = append(result.Violations, createFromSourceGuardCoverageAssertionFailure(
			"_System_ObjectFieldMapping",
			fmt.Sprintf("could not verify owner-scoped create-from-source guard coverage: %v", err),
		))
		return
	}
	if err := assertUnsupportedBlankCreateFromSourceGuardAssignments(db, facts, ownerIndex, owner, result); err != nil {
		result.Violations = append(result.Violations, assertion.AssertionViolation{
			Category:    "create_from_source_guard_coverage",
			Severity:    constants.SeverityError,
			Object:      "_System_ObjectFieldMapping",
			Description: fmt.Sprintf("could not verify owner-scoped blank SourceLookupField create-from-source guard assignments: %v", err),
		})
		return
	}
	for _, mapping := range mappings {
		if mapping.ActionType == "insertInheritFromLookup" || createFromSourceMappingCoveredByFlow(mapping, ownerIndex) {
			continue
		}
		result.Violations = append(result.Violations, assertion.AssertionViolation{
			Category: "create_from_source_guard_coverage",
			Severity: constants.SeverityError,
			Object:   mapping.TargetObject,
			Description: fmt.Sprintf(
				"owner %q ObjectFieldMapping on %q SourceLookupField=%q ActionType=%q SourceObject=%q has no same-owner active StandardAction Flow recordCreate.fieldMapping and write surface covering it; "+
					"the create-from-source bypass guard will not fire for this row — pack or overlay drift",
				strings.TrimSpace(owner), mapping.TargetObject, mapping.SourceLookupField, mapping.ActionType, mapping.SourceObject,
			),
		})
	}
}

func createFromSourceMappingFactsFromGroups(groups []connector.FieldMappingGroup) effectiveCreateFromSourceMappingFacts {
	tupleIndex := map[string]int{}
	positionIndex := map[string]int{}
	var mappings []effectiveCreateFromSourceMapping
	var tuples []effectiveCreateFromSourceTuple
	for _, group := range groups {
		if len(group.Mappings) == 0 {
			continue
		}
		sourceObject := strings.TrimSpace(group.SourceObject)
		targetObject := strings.TrimSpace(group.TargetObject)
		actionType := strings.TrimSpace(group.ActionType)
		if actionType == "insertInheritFromLookup" {
			continue
		}
		tupleKey := sourceObject + "\x00" + targetObject + "\x00" + actionType
		if _, ok := tupleIndex[tupleKey]; !ok {
			tupleIndex[tupleKey] = len(tuples)
			tuples = append(tuples, effectiveCreateFromSourceTuple{
				SourceObject: sourceObject,
				TargetObject: targetObject,
				ActionType:   actionType,
			})
		}
		for _, mapping := range group.Mappings {
			sourceLookupField := strings.TrimSpace(mapping.SourceLookupField)
			if sourceLookupField == "" {
				continue
			}
			positionKey := tupleKey + "\x00" + sourceLookupField
			idx, ok := positionIndex[positionKey]
			if !ok {
				idx = len(mappings)
				positionIndex[positionKey] = idx
				mappings = append(mappings, effectiveCreateFromSourceMapping{
					SourceObject:      sourceObject,
					TargetObject:      targetObject,
					ActionType:        actionType,
					SourceLookupField: sourceLookupField,
				})
			}
			if fieldMappingWritesSourceIDToLookup(mapping.SourceField, mapping.TargetField, sourceLookupField) {
				mappings[idx].FieldMappingWritesSourceLookup = true
			}
		}
	}
	return effectiveCreateFromSourceMappingFacts{Mappings: mappings, Tuples: tuples}
}

func assertUnsupportedBlankCreateFromSourceGuardAssignments(
	db *sql.DB,
	facts effectiveCreateFromSourceMappingFacts,
	ownerIndex map[createFromSourceFlowOwnerKey][]createFromSourceFlowOwner,
	owner string,
	result *assertion.AssertionResult,
) error {
	if !createFromSourceOwnerIndexHasUnboundAssignedFields(ownerIndex) {
		return nil
	}
	explicitMappingKeys := createFromSourceExplicitMappingKeys(facts.Mappings)
	explicitTupleKeys := createFromSourceExplicitMappingTupleKeys(facts.Mappings)
	allAssignedOwnerIndex := createFromSourceFlowOwnerIndexWithAllAssignedFields(ownerIndex)
	allAssignedMappings, err := expandCreateFromSourceGuardMappingsFromFlowAssignments(context.Background(), db, facts, allAssignedOwnerIndex)
	if err != nil {
		return err
	}
	sourceBoundMappings, err := expandCreateFromSourceGuardMappingsFromFlowAssignments(context.Background(), db, facts, ownerIndex)
	if err != nil {
		return err
	}
	sourceBoundKeys := createFromSourceMappingKeys(sourceBoundMappings)
	for _, mapping := range allAssignedMappings {
		mappingKey := createFromSourceMappingKey(mapping)
		if _, explicit := explicitMappingKeys[mappingKey]; explicit {
			continue
		}
		tupleKey := createFromSourceFlowOwnerKeyForMapping(mapping)
		if _, explicitTuple := explicitTupleKeys[tupleKey]; explicitTuple {
			continue
		}
		if _, sourceBound := sourceBoundKeys[mappingKey]; sourceBound {
			continue
		}
		result.Violations = append(result.Violations, assertion.AssertionViolation{
			Category: "create_from_source_guard_coverage",
			Severity: constants.SeverityError,
			Object:   mapping.TargetObject,
			Description: fmt.Sprintf(
				"owner %q ObjectFieldMapping tuple TargetObject=%q ActionType=%q SourceObject=%q has blank SourceLookupField rows and a same-owner StandardAction Flow assignment to source FK %q, "+
					"but the assignment is not bound to the fieldMapping source record Id; the runtime create-from-source bypass guard will not infer this row - pack or overlay drift",
				strings.TrimSpace(owner), mapping.TargetObject, mapping.ActionType, mapping.SourceObject, mapping.SourceLookupField,
			),
		})
	}
	return nil
}

func createFromSourceOwnerIndexHasUnboundAssignedFields(ownerIndex map[createFromSourceFlowOwnerKey][]createFromSourceFlowOwner) bool {
	for _, owners := range ownerIndex {
		for _, owner := range owners {
			for field := range owner.allAssignedFields {
				if _, sourceBound := owner.assignedFields[field]; !sourceBound {
					return true
				}
			}
		}
	}
	return false
}

func createFromSourceFlowOwnerIndexWithAllAssignedFields(ownerIndex map[createFromSourceFlowOwnerKey][]createFromSourceFlowOwner) map[createFromSourceFlowOwnerKey][]createFromSourceFlowOwner {
	out := make(map[createFromSourceFlowOwnerKey][]createFromSourceFlowOwner, len(ownerIndex))
	for key, owners := range ownerIndex {
		copied := make([]createFromSourceFlowOwner, 0, len(owners))
		for _, owner := range owners {
			owner.assignedFields = owner.allAssignedFields
			copied = append(copied, owner)
		}
		out[key] = copied
	}
	return out
}

func createFromSourceExplicitMappingKeys(mappings []effectiveCreateFromSourceMapping) map[string]struct{} {
	return createFromSourceMappingKeys(mappings)
}

func createFromSourceMappingKeys(mappings []effectiveCreateFromSourceMapping) map[string]struct{} {
	out := map[string]struct{}{}
	for _, mapping := range mappings {
		out[createFromSourceMappingKey(mapping)] = struct{}{}
	}
	return out
}

func fieldMappingWritesSourceIDToLookup(sourceField, targetField, sourceLookupField string) bool {
	return strings.EqualFold(strings.TrimSpace(sourceField), constants.FieldID) &&
		strings.EqualFold(strings.TrimSpace(targetField), strings.TrimSpace(sourceLookupField))
}

func createFromSourceTupleNeedsGuardCoverage(tuple effectiveCreateFromSourceTuple) bool {
	return strings.TrimSpace(tuple.SourceObject) != "" &&
		strings.TrimSpace(tuple.TargetObject) != "" &&
		strings.TrimSpace(tuple.ActionType) != "" &&
		strings.TrimSpace(tuple.ActionType) != "insertInheritFromLookup"
}

func createFromSourceExplicitMappingTupleKeys(mappings []effectiveCreateFromSourceMapping) map[createFromSourceFlowOwnerKey]struct{} {
	out := map[createFromSourceFlowOwnerKey]struct{}{}
	for _, mapping := range mappings {
		if strings.TrimSpace(mapping.SourceLookupField) == "" {
			continue
		}
		out[createFromSourceFlowOwnerKeyForMapping(mapping)] = struct{}{}
	}
	return out
}

func getActiveStandardActionFlowsForCreateSourceOwner(db *sql.DB, owner string) ([]standardActionFlowForCreateSource, error) {
	owner = strings.TrimSpace(owner)
	if owner == "" {
		owner = standardObjectFieldMappingOwner
	}
	query := fmt.Sprintf(`
SELECT sa.Id,
       sa.Name,
       f.Metadata
  FROM %s AS sa
  JOIN %s AS fd
    ON fd.DeveloperName = sa.FlowDeveloperName
   AND fd.IsDeleted = 0
  JOIN %s AS f
    ON f.Id = fd.ActiveVersionId
   AND f.IsDeleted = 0
 WHERE sa.IsActive = 1 AND sa.IsDeleted = 0
   AND COALESCE(sa.OwnerPack, '') = ?
   AND sa.FlowDeveloperName IS NOT NULL
   AND sa.FlowDeveloperName <> ''`,
		constants.TableStandardAction, constants.TableFlowDefinition, constants.TableFlow,
	)
	rows, err := db.Query(query, owner)
	if err != nil {
		return nil, fmt.Errorf("query active owner-scoped standard action flows for create-from-source guard: %w", err)
	}
	defer rows.Close()

	var out []standardActionFlowForCreateSource
	for rows.Next() {
		var id, name, metadata sql.NullString
		if err := rows.Scan(&id, &name, &metadata); err != nil {
			return nil, fmt.Errorf("could not scan active owner-scoped standard action flow for create-from-source guard: %w", err)
		}
		out = append(out, standardActionFlowForCreateSource{
			ID:       strings.TrimSpace(id.String),
			Name:     strings.TrimSpace(name.String),
			Metadata: metadata.String,
		})
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("could not iterate active owner-scoped standard action flows for create-from-source guard: %w", err)
	}
	return out, nil
}

func createFromSourceMappingCoveredByFlow(mapping effectiveCreateFromSourceMapping, ownerIndex map[createFromSourceFlowOwnerKey][]createFromSourceFlowOwner) bool {
	for _, owner := range ownerIndex[createFromSourceFlowOwnerKeyForMapping(mapping)] {
		if flowOwnerOwnsCreateFromSourceMapping(owner, mapping) {
			return true
		}
	}
	return false
}

func createFromSourceGuardCoverageAssertionFailure(objectName, description string) assertion.AssertionViolation {
	return assertion.AssertionViolation{
		Category:    "create_from_source_guard_coverage",
		Severity:    constants.SeverityError,
		Object:      objectName,
		Description: description,
	}
}
