package d2talalayout

import (
	
	
	
	

	
	
	

	
	
)

type resultEdgeTopology struct {
	from layoutgraph.EntityID
	to   layoutgraph.EntityID
}

// validateLayoutResultTopology binds a completed workspace to its immutable
// input while allowing sequence-defining edges to be consumed by the layoutgraph.
// All other input records must still be present with the same identity and
// endpoints, and the result may not introduce extra records.
func validateLayoutResultTopology( context.Context, ,  *layoutgraph.Graph) (map[layoutgraph.EntityID]struct{}, error) {
	,  := grouping.SequenceDefiningEdges(, )
	if  != nil {
		return nil, 
	}
	if  := validateTopology(, , , );  != nil {
		return nil, 
	}
	return , nil
}

func validateTopology( context.Context, ,  *layoutgraph.Graph,  map[layoutgraph.EntityID]struct{}) error {
	if  == nil ||  == nil {
		return fmt.Errorf("cannot compare nil graph topology")
	}
	if len(.Nodes) != len(.Nodes) {
		return fmt.Errorf("node count changed from %d to %d", len(.Nodes), len(.Nodes))
	}
	 := make(map[layoutgraph.EntityID]struct{}, len(.Nodes))
	for ,  := range .Nodes {
		if  := .Err();  != nil {
			return 
		}
		if  == nil {
			return fmt.Errorf("input contains a nil node")
		}
		if ,  := [.ID];  {
			return fmt.Errorf("input contains duplicate node ID %d", .ID)
		}
		[.ID] = struct{}{}
	}
	for ,  := range .Nodes {
		if  := .Err();  != nil {
			return 
		}
		if ,  := [.ID]; ! {
			return fmt.Errorf("result contains unexpected node ID %d", .ID)
		}
	}

	 := make(map[layoutgraph.EntityID]resultEdgeTopology, len(.Edges))
	for ,  := range .Edges {
		if  := .Err();  != nil {
			return 
		}
		if  == nil || .From == nil || .To == nil {
			return fmt.Errorf("input contains an edge with a nil endpoint")
		}
		if ,  := [.ID];  {
			return fmt.Errorf("input contains duplicate edge ID %d", .ID)
		}
		[.ID] = resultEdgeTopology{from: .From.ID, to: .To.ID}
	}
	 := make(map[layoutgraph.EntityID]struct{}, len(.Edges))
	for ,  := range .Edges {
		if  := .Err();  != nil {
			return 
		}
		if  == nil || .From == nil || .To == nil {
			return fmt.Errorf("result contains an edge with a nil endpoint")
		}
		if ,  := [.ID];  {
			return fmt.Errorf("result contains duplicate edge ID %d", .ID)
		}
		[.ID] = struct{}{}
		,  := [.ID]
		if ! {
			return fmt.Errorf("result contains unexpected edge ID %d", .ID)
		}
		if .From.ID != .from || .To.ID != .to {
			return fmt.Errorf(
				"edge %d endpoints changed from %d->%d to %d->%d",
				.ID,
				.from,
				.to,
				.From.ID,
				.To.ID,
			)
		}
	}
	for  := range  {
		if  := .Err();  != nil {
			return 
		}
		if ,  := [];  {
			continue
		}
		if ,  := [];  {
			continue
		}
		return fmt.Errorf("result is missing edge ID %d", )
	}
	return nil
}

// validateLayoutResultMetadata checks the immutable geometry semantics that a
// completed layout attempt must preserve. Attempt-owned
// boxes, routes, font-size values, mutable node-label geometry, edge-label
// positions, Near relationships, and derived layout structures deliberately
// remain outside this comparison. Near relationships can be both inferred and
// removed by hierarchy/cluster processing. Fixed-position and desired-size values are
// bound as metadata, but whether the result satisfies those constraints is an
// output-validity concern, not an immutable-metadata concern.
func validateLayoutResultMetadata(
	 context.Context,
	,  *layoutgraph.Graph,
	 map[layoutgraph.EntityID]struct{},
) error {
	if  == nil ||  == nil {
		return fmt.Errorf("cannot compare nil graph metadata")
	}
	if  := .Err();  != nil {
		return 
	}
	if .IsRootHierarchy != .IsRootHierarchy {
		return fmt.Errorf("root hierarchy metadata changed")
	}
	if ,  := sameDirectionMetadata(, , );  != nil {
		return 
	} else if ! {
		return fmt.Errorf("graph direction metadata changed")
	}

	 := make(map[layoutgraph.EntityID]*layoutgraph.Node, len(.Nodes))
	for ,  := range .Nodes {
		if  := .Err();  != nil {
			return 
		}
		if  == nil {
			return fmt.Errorf("input contains a nil node")
		}
		[.ID] = 
	}
	for ,  := range .Nodes {
		if  := .Err();  != nil {
			return 
		}
		if  == nil {
			return fmt.Errorf("result contains a nil node")
		}
		 := [.ID]
		if  == nil {
			return fmt.Errorf("result contains unexpected node ID %d", .ID)
		}
		if .ShapeType() != .ShapeType() {
			return fmt.Errorf("node %d shape changed from %q to %q", .ID, .ShapeType(), .ShapeType())
		}
		if (.Container == nil) != (.Container == nil) ||
			.Container != nil && .Container.ID != .Container.ID {
			return fmt.Errorf("node %d container ownership changed", .ID)
		}
		if .NumColumns() != .NumColumns() {
			return fmt.Errorf("node %d table column count changed from %d to %d", .ID, .NumColumns(), .NumColumns())
		}
		if .Is3D != .Is3D {
			return fmt.Errorf("node %d 3D outline metadata changed", .ID)
		}
		if .IsMultiple != .IsMultiple {
			return fmt.Errorf("node %d multiple outline metadata changed", .ID)
		}
		if .IsInvisible != .IsInvisible {
			return fmt.Errorf("node %d visibility metadata changed", .ID)
		}
		if !sameOptionalValue(.FixedTopLeft, .FixedTopLeft) {
			return fmt.Errorf("node %d fixed position metadata changed", .ID)
		}
		if !sameOptionalValue(.DesiredWidth, .DesiredWidth) ||
			!sameOptionalValue(.DesiredHeight, .DesiredHeight) {
			return fmt.Errorf("node %d desired-size metadata changed", .ID)
		}
		if .ForceHierarchy != .ForceHierarchy {
			return fmt.Errorf("node %d forced-hierarchy metadata changed", .ID)
		}
		if !sameLabelIdentity(.Label, .Label) {
			return fmt.Errorf("node %d label identity changed", .ID)
		}
		if .Label != nil && .Label.Position != label.Unset && .Label.Position != .Label.Position {
			return fmt.Errorf("node %d fixed label position changed", .ID)
		}
		if (.Icon == nil) != (.Icon == nil) {
			return fmt.Errorf("node %d icon presence changed", .ID)
		}
		if .Icon != nil && .Icon.Position != label.Unset && .Icon.Position != .Icon.Position {
			return fmt.Errorf("node %d fixed icon position changed", .ID)
		}
		if !isValidResultFontSize(.FontSize, .FontSize) {
			return fmt.Errorf("node %d font size is outside the layout output domain", .ID)
		}
	}

	 := make(map[layoutgraph.EntityID]*layoutgraph.Edge, len(.Edges))
	for ,  := range .Edges {
		if  := .Err();  != nil {
			return 
		}
		if  == nil {
			return fmt.Errorf("input contains a nil edge")
		}
		[.ID] = 
	}
	 := make(map[layoutgraph.EntityID]struct{}, len(.Edges))
	for ,  := range .Edges {
		if  := .Err();  != nil {
			return 
		}
		if  == nil {
			return fmt.Errorf("result contains a nil edge")
		}
		[.ID] = struct{}{}
		 := [.ID]
		if  == nil {
			return fmt.Errorf("result contains unexpected edge ID %d", .ID)
		}
		if .SourceArrowhead != .SourceArrowhead || .TargetArrowhead != .TargetArrowhead {
			return fmt.Errorf("edge %d arrowhead metadata changed", .ID)
		}
		if !sameImmutableLabel(.SourceArrowheadLabel, .SourceArrowheadLabel) {
			return fmt.Errorf("edge %d source arrowhead label metadata changed", .ID)
		}
		if !sameImmutableLabel(.TargetArrowheadLabel, .TargetArrowheadLabel) {
			return fmt.Errorf("edge %d target arrowhead label metadata changed", .ID)
		}
		if .MinWidth != .MinWidth || .MinHeight != .MinHeight {
			return fmt.Errorf("edge %d minimum geometry changed", .ID)
		}
		if !sameOptionalValue(.FromTableColumnIndex, .FromTableColumnIndex) ||
			!sameOptionalValue(.ToTableColumnIndex, .ToTableColumnIndex) {
			return fmt.Errorf("edge %d table column attachment metadata changed", .ID)
		}
		if .IsInvisible != .IsInvisible {
			return fmt.Errorf("edge %d visibility metadata changed", .ID)
		}
		if !sameImmutableEdgeStyle(.Style, .Style) {
			return fmt.Errorf("edge %d style metadata changed", .ID)
		}
		if !sameEdgeLabelMetadata(.Label, .Label) {
			return fmt.Errorf("edge %d label identity or dimensions changed", .ID)
		}
	}
	for  := range  {
		if  := .Err();  != nil {
			return 
		}
		if ,  := [];  {
			continue
		}
		if ,  := [];  {
			continue
		}
		return fmt.Errorf("result is missing edge ID %d", )
	}
	return nil
}

func sameLabelIdentity(,  *layoutgraph.Label) bool {
	if  == nil ||  == nil {
		return  == 
	}
	return .Text == .Text
}

func sameEdgeLabelMetadata(,  *layoutgraph.Label) bool {
	if  == nil ||  == nil {
		return  == 
	}
	return .Text == .Text &&
		.Width == .Width &&
		.Height == .Height
}

func sameImmutableLabel(,  *layoutgraph.Label) bool {
	if  == nil ||  == nil {
		return  == 
	}
	return .Text == .Text &&
		.Position == .Position &&
		.Width == .Width &&
		.Height == .Height
}

// EquivalentStyles is intentionally a routing-specific comparison and omits
// renderer fields. Layout validation has a stronger contract: every attempt
// must return each carried input style unchanged, including fields such as
// BorderRadius that other layout stages may consume.
func sameImmutableEdgeStyle(,  layoutgraph.EdgeStyle) bool {
	 := [...]*layoutgraph.StyleScalar{
		.Opacity,
		.Stroke,
		.Fill,
		.FillPattern,
		.StrokeWidth,
		.StrokeDash,
		.BorderRadius,
		.Shadow,
		.ThreeDee,
		.Multiple,
		.Font,
		.FontSize,
		.FontColor,
		.Animated,
		.Bold,
		.Italic,
		.Underline,
		.Filled,
		.DoubleBorder,
		.TextTransform,
	}
	 := [...]*layoutgraph.StyleScalar{
		.Opacity,
		.Stroke,
		.Fill,
		.FillPattern,
		.StrokeWidth,
		.StrokeDash,
		.BorderRadius,
		.Shadow,
		.ThreeDee,
		.Multiple,
		.Font,
		.FontSize,
		.FontColor,
		.Animated,
		.Bold,
		.Italic,
		.Underline,
		.Filled,
		.DoubleBorder,
		.TextTransform,
	}
	for  := range  {
		if !sameOptionalValue([], []) {
			return false
		}
	}
	return true
}

func sameOptionalValue[ comparable](,  *) bool {
	if  == nil ||  == nil {
		return  == 
	}
	return * == *
}

func isValidResultFontSize(,  *int) bool {
	if  == nil ||  == nil {
		return  == 
	}
	return * == * || slices.Contains(d2fonts.FontSizes, *)
}

func sameDirectionMetadata( context.Context, ,  *layoutgraph.Graph) (bool, error) {
	type  struct {
		   layoutgraph.EntityID
		 bool
	}
	 := func( *layoutgraph.Graph) (map[]geo.Orientation, error) {
		 := make(map[]geo.Orientation, len(.Directions))
		for ,  := range .Directions {
			if  := .Err();  != nil {
				return nil, 
			}
			 := {:  == nil}
			if  != nil {
				. = .ID
			}
			if ,  := [];  {
				return nil, fmt.Errorf("graph direction metadata repeats a canonical key")
			}
			[] = 
		}
		return , nil
	}
	,  := ()
	if  != nil {
		return false, 
	}
	,  := ()
	if  != nil {
		return false, 
	}
	if len() != len() {
		return false, nil
	}
	for ,  := range  {
		if  := .Err();  != nil {
			return false, 
		}
		if ,  := []; ! ||  !=  {
			return false, nil
		}
	}
	return true, nil
}

func validateCompletedGraph( context.Context,  *layoutgraph.Graph) error {
	if  == nil {
		return fmt.Errorf("graph is nil")
	}
	 := make(map[*layoutgraph.Node]struct{}, len(.Nodes))
	 := make(map[layoutgraph.EntityID]struct{}, len(.Nodes))
	for ,  := range .Nodes {
		if  := .Err();  != nil {
			return 
		}
		if  == nil {
			return fmt.Errorf("node %d is nil", )
		}
		if ,  := [];  {
			return fmt.Errorf("node %d is repeated", .ID)
		}
		[] = struct{}{}
		if ,  := [.ID];  {
			return fmt.Errorf("node ID %d is duplicated", .ID)
		}
		[.ID] = struct{}{}
		if  := validateResultNode();  != nil {
			return 
		}
	}

	 := make(map[*layoutgraph.Edge]struct{}, len(.Edges))
	 := make(map[layoutgraph.EntityID]struct{}, len(.Edges))
	for ,  := range .Edges {
		if  := .Err();  != nil {
			return 
		}
		if  == nil {
			return fmt.Errorf("edge %d is nil", )
		}
		if ,  := [];  {
			return fmt.Errorf("edge %d is repeated", .ID)
		}
		[] = struct{}{}
		if ,  := [.ID];  {
			return fmt.Errorf("edge ID %d is duplicated", .ID)
		}
		[.ID] = struct{}{}
		if ,  := [.From]; ! {
			return fmt.Errorf("edge %d has a source outside the result graph", .ID)
		}
		if ,  := [.To]; ! {
			return fmt.Errorf("edge %d has a destination outside the result graph", .ID)
		}
		if  := validateResultEdge(, );  != nil {
			return 
		}
	}
	return nil
}

func validateResultNode( *layoutgraph.Node) error {
	if  == nil {
		return fmt.Errorf("node is nil")
	}
	if .TopLeft == nil {
		return fmt.Errorf("node %d has no position", .ID)
	}
	if  := validateResultCoordinate("node x", .TopLeft.X);  != nil {
		return fmt.Errorf("node %d: %w", .ID, )
	}
	if  := validateResultCoordinate("node y", .TopLeft.Y);  != nil {
		return fmt.Errorf("node %d: %w", .ID, )
	}
	if  := validateResultDimension("node width", .Width, false);  != nil {
		return fmt.Errorf("node %d: %w", .ID, )
	}
	if  := validateResultDimension("node height", .Height, false);  != nil {
		return fmt.Errorf("node %d: %w", .ID, )
	}
	if !isFiniteResultNumber(.TopLeft.X+.Width) || !isFiniteResultNumber(.TopLeft.Y+.Height) {
		return fmt.Errorf("node %d bounds overflow", .ID)
	}
	if .Label != nil {
		if  := validateResultDimension("node label width", .Label.Width, true);  != nil {
			return fmt.Errorf("node %d: %w", .ID, )
		}
		if  := validateResultDimension("node label height", .Label.Height, true);  != nil {
			return fmt.Errorf("node %d: %w", .ID, )
		}
	}
	return nil
}

func validateResultEdge( context.Context,  *layoutgraph.Edge) error {
	if  == nil {
		return fmt.Errorf("edge is nil")
	}
	if len(.Points) < 2 {
		return fmt.Errorf("edge %d has an incomplete route", .ID)
	}
	for ,  := range .Points {
		if %256 == 0 {
			if  := .Err();  != nil {
				return 
			}
		}
		if  == nil {
			return fmt.Errorf("edge %d route point %d is nil", .ID, )
		}
		if  := validateResultCoordinate("route x", .X);  != nil {
			return fmt.Errorf("edge %d point %d: %w", .ID, , )
		}
		if  := validateResultCoordinate("route y", .Y);  != nil {
			return fmt.Errorf("edge %d point %d: %w", .ID, , )
		}
	}
	if !isFiniteResultNumber(.LabelPercentage) || .LabelPercentage < 0 || .LabelPercentage > 1 {
		return fmt.Errorf("edge %d has invalid label percentage %v", .ID, .LabelPercentage)
	}
	 := []struct {
		  string
		 *layoutgraph.Label
	}{
		{: "label", : .Label},
		{: "source arrowhead label", : .SourceArrowheadLabel},
		{: "target arrowhead label", : .TargetArrowheadLabel},
	}
	for ,  := range  {
		if . == nil {
			continue
		}
		if  := validateResultDimension(.+" width", ..Width, true);  != nil {
			return fmt.Errorf("edge %d: %w", .ID, )
		}
		if  := validateResultDimension(.+" height", ..Height, true);  != nil {
			return fmt.Errorf("edge %d: %w", .ID, )
		}
	}
	return nil
}

func validateResultCoordinate( string,  float64) error {
	if !isFiniteResultNumber() || math.Abs() > maxResultCoordinate {
		return fmt.Errorf("%s is outside the finite supported range: %v", , )
	}
	return nil
}

func validateResultDimension( string,  float64,  bool) error {
	if !isFiniteResultNumber() ||  < 0 || (! &&  == 0) ||  > maxResultCoordinate {
		return fmt.Errorf("%s is outside the supported range: %v", , )
	}
	return nil
}

func isFiniteResultNumber( float64) bool {
	return !math.IsNaN() && !math.IsInf(, 0)
}