package mapping

import (
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/layoutauthoringv1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
)

func toWireLayoutAuthoringDocument(document semantic.LayoutDocument) *runtimev1.LayoutAuthoringDocument {
	sections := make([]*runtimev1.LayoutAuthoringSection, 0, len(document.Sections))
	for _, section := range document.Sections {
		sections = append(sections, toWireLayoutAuthoringSection(section))
	}
	relatedLists := make([]*runtimev1.LayoutAuthoringRelatedList, 0, len(document.RelatedLists))
	for _, relatedList := range document.RelatedLists {
		relatedLists = append(relatedLists, toWireLayoutAuthoringRelatedList(relatedList))
	}
	panels := make([]*runtimev1.LayoutAuthoringRecordPanel, 0, len(document.RecordPanels))
	for _, panel := range document.RecordPanels {
		panels = append(panels, toWireLayoutAuthoringRecordPanel(panel))
	}
	states := make([]*runtimev1.LayoutAuthoringRecordStateRule, 0, len(document.RecordStates))
	for _, state := range document.RecordStates {
		states = append(states, toWireLayoutAuthoringRecordStateRule(state))
	}
	return &runtimev1.LayoutAuthoringDocument{
		LayoutId: document.LayoutID, ObjectId: document.ObjectID, Name: document.Name, Kind: document.Kind,
		LayoutVersion: document.LayoutVersion, CompactFields: cloneStrings(document.CompactFields),
		Tabs: cloneStrings(document.Tabs), Sections: sections, RelatedLists: relatedLists, RecordPanels: panels,
		HeaderActions: toWireLayoutAuthoringActionPlacement(document.HeaderActions),
		QuickActions:  toWireLayoutAuthoringActionPlacement(document.QuickActions),
		RecordStates:  states, PrimaryHeaderActions: cloneStrings(document.PrimaryHeaderActions),
	}
}

func fromWireLayoutAuthoringDocument(document *runtimev1.LayoutAuthoringDocument) (semantic.LayoutDocument, error) {
	if document == nil {
		return semantic.LayoutDocument{}, invalidPlatformWire()
	}
	result := semantic.LayoutDocument{
		LayoutID: document.GetLayoutId(), ObjectID: document.GetObjectId(), Name: document.GetName(), Kind: document.GetKind(),
		LayoutVersion: document.GetLayoutVersion(), CompactFields: cloneStrings(document.GetCompactFields()),
		Tabs: cloneStrings(document.GetTabs()), Sections: make([]semantic.LayoutSection, 0, len(document.GetSections())),
		RelatedLists:         make([]semantic.RelatedList, 0, len(document.GetRelatedLists())),
		RecordPanels:         make([]semantic.RecordPanel, 0, len(document.GetRecordPanels())),
		RecordStates:         make([]semantic.RecordStateRule, 0, len(document.GetRecordStates())),
		PrimaryHeaderActions: cloneStrings(document.GetPrimaryHeaderActions()),
	}
	var err error
	if result.HeaderActions, err = fromWireLayoutAuthoringActionPlacement(document.GetHeaderActions()); err != nil {
		return semantic.LayoutDocument{}, err
	}
	if result.QuickActions, err = fromWireLayoutAuthoringActionPlacement(document.GetQuickActions()); err != nil {
		return semantic.LayoutDocument{}, err
	}
	for _, section := range document.GetSections() {
		mapped, err := fromWireLayoutAuthoringSection(section)
		if err != nil {
			return semantic.LayoutDocument{}, err
		}
		result.Sections = append(result.Sections, mapped)
	}
	for _, relatedList := range document.GetRelatedLists() {
		mapped, err := fromWireLayoutAuthoringRelatedList(relatedList)
		if err != nil {
			return semantic.LayoutDocument{}, err
		}
		result.RelatedLists = append(result.RelatedLists, mapped)
	}
	for _, panel := range document.GetRecordPanels() {
		mapped, err := fromWireLayoutAuthoringRecordPanel(panel)
		if err != nil {
			return semantic.LayoutDocument{}, err
		}
		result.RecordPanels = append(result.RecordPanels, mapped)
	}
	for _, state := range document.GetRecordStates() {
		mapped, err := fromWireLayoutAuthoringRecordStateRule(state)
		if err != nil {
			return semantic.LayoutDocument{}, err
		}
		result.RecordStates = append(result.RecordStates, mapped)
	}
	return result, nil
}

func toWireLayoutAuthoringSection(section semantic.LayoutSection) *runtimev1.LayoutAuthoringSection {
	labels := make([]*runtimev1.LayoutAuthoringFieldLabel, 0, len(section.FieldLabels))
	for _, label := range section.FieldLabels {
		labels = append(labels, &runtimev1.LayoutAuthoringFieldLabel{FieldReference: label.FieldReference, Label: label.Label})
	}
	behaviors := make([]*runtimev1.LayoutAuthoringFieldBehaviorGroup, 0, len(section.FieldBehaviors))
	for _, group := range section.FieldBehaviors {
		wireGroup := &runtimev1.LayoutAuthoringFieldBehaviorGroup{FieldReference: group.FieldReference}
		if group.Behaviors != nil {
			values := make([]*runtimev1.LayoutAuthoringFieldBehavior, 0, len(*group.Behaviors))
			for _, behavior := range *group.Behaviors {
				values = append(values, &runtimev1.LayoutAuthoringFieldBehavior{Mode: behavior.Mode, Behavior: behavior.Behavior})
			}
			wireGroup.Behaviors = &runtimev1.LayoutAuthoringFieldBehaviorList{Values: values}
		}
		behaviors = append(behaviors, wireGroup)
	}
	return &runtimev1.LayoutAuthoringSection{
		SectionId: section.SectionID, Label: section.Label, TabId: cloneOptionalString(section.TabID),
		Kind: cloneOptionalString(section.Kind), ComponentName: cloneOptionalString(section.ComponentName),
		ComponentConfiguration: toWireLayoutAuthoringNamedValues(section.ComponentConfiguration), Columns: section.Columns,
		Fields: cloneStrings(section.Fields), FieldLabels: labels, FieldBehaviors: behaviors,
		VisibilityCondition: cloneOptionalString(section.VisibilityCondition),
	}
}

func fromWireLayoutAuthoringSection(section *runtimev1.LayoutAuthoringSection) (semantic.LayoutSection, error) {
	if section == nil {
		return semantic.LayoutSection{}, invalidPlatformWire()
	}
	configuration, err := fromWireLayoutAuthoringNamedValues(section.GetComponentConfiguration())
	if err != nil {
		return semantic.LayoutSection{}, err
	}
	result := semantic.LayoutSection{
		SectionID: section.GetSectionId(), Label: section.GetLabel(), TabID: cloneOptionalString(section.TabId),
		Kind: cloneOptionalString(section.Kind), ComponentName: cloneOptionalString(section.ComponentName),
		ComponentConfiguration: configuration, Columns: section.GetColumns(), Fields: cloneStrings(section.GetFields()),
		FieldLabels:         make([]semantic.FieldLabel, 0, len(section.GetFieldLabels())),
		FieldBehaviors:      make([]semantic.FieldBehaviorGroup, 0, len(section.GetFieldBehaviors())),
		VisibilityCondition: cloneOptionalString(section.VisibilityCondition),
	}
	for _, label := range section.GetFieldLabels() {
		if label == nil {
			return semantic.LayoutSection{}, invalidPlatformWire()
		}
		result.FieldLabels = append(result.FieldLabels, semantic.FieldLabel{FieldReference: label.GetFieldReference(), Label: label.GetLabel()})
	}
	for _, group := range section.GetFieldBehaviors() {
		if group == nil {
			return semantic.LayoutSection{}, invalidPlatformWire()
		}
		mapped := semantic.FieldBehaviorGroup{FieldReference: group.GetFieldReference()}
		if group.Behaviors != nil {
			values := make([]semantic.FieldBehavior, 0, len(group.Behaviors.GetValues()))
			for _, behavior := range group.Behaviors.GetValues() {
				if behavior == nil {
					return semantic.LayoutSection{}, invalidPlatformWire()
				}
				values = append(values, semantic.FieldBehavior{Mode: behavior.GetMode(), Behavior: behavior.GetBehavior()})
			}
			mapped.Behaviors = &values
		}
		result.FieldBehaviors = append(result.FieldBehaviors, mapped)
	}
	return result, nil
}

func toWireLayoutAuthoringRelatedList(value semantic.RelatedList) *runtimev1.LayoutAuthoringRelatedList {
	mappings := make([]*runtimev1.LayoutAuthoringPrefillMapping, 0, len(value.PrefillMappings))
	for _, mapping := range value.PrefillMappings {
		mappings = append(mappings, &runtimev1.LayoutAuthoringPrefillMapping{TargetFieldId: mapping.TargetFieldID, SourceFieldId: mapping.SourceFieldID})
	}
	return &runtimev1.LayoutAuthoringRelatedList{
		RelatedListId: value.RelatedListID, Label: value.Label, SecondaryLabel: value.SecondaryLabel,
		ObjectId: value.ObjectID, LookupFieldId: value.LookupFieldID, FieldIds: cloneStrings(value.FieldIDs), PrefillMappings: mappings,
	}
}

func fromWireLayoutAuthoringRelatedList(value *runtimev1.LayoutAuthoringRelatedList) (semantic.RelatedList, error) {
	if value == nil {
		return semantic.RelatedList{}, invalidPlatformWire()
	}
	result := semantic.RelatedList{
		RelatedListID: value.GetRelatedListId(), Label: value.GetLabel(), SecondaryLabel: value.GetSecondaryLabel(),
		ObjectID: value.GetObjectId(), LookupFieldID: value.GetLookupFieldId(), FieldIDs: cloneStrings(value.GetFieldIds()),
		PrefillMappings: make([]semantic.PrefillMapping, 0, len(value.GetPrefillMappings())),
	}
	for _, mapping := range value.GetPrefillMappings() {
		if mapping == nil {
			return semantic.RelatedList{}, invalidPlatformWire()
		}
		result.PrefillMappings = append(result.PrefillMappings, semantic.PrefillMapping{TargetFieldID: mapping.GetTargetFieldId(), SourceFieldID: mapping.GetSourceFieldId()})
	}
	return result, nil
}

func toWireLayoutAuthoringRecordPanel(value semantic.RecordPanel) *runtimev1.LayoutAuthoringRecordPanel {
	return &runtimev1.LayoutAuthoringRecordPanel{
		PanelId: value.PanelID, RendererId: value.RendererID, Position: value.Position, TabId: value.TabID,
		TabLabel: value.TabLabel, Order: value.Order, VisibilityCondition: value.VisibilityCondition,
		RendererConfiguration: toWireLayoutAuthoringNamedValues(value.RendererConfiguration),
	}
}

func fromWireLayoutAuthoringRecordPanel(value *runtimev1.LayoutAuthoringRecordPanel) (semantic.RecordPanel, error) {
	if value == nil {
		return semantic.RecordPanel{}, invalidPlatformWire()
	}
	configuration, err := fromWireLayoutAuthoringNamedValues(value.GetRendererConfiguration())
	if err != nil {
		return semantic.RecordPanel{}, err
	}
	return semantic.RecordPanel{
		PanelID: value.GetPanelId(), RendererID: value.GetRendererId(), Position: value.GetPosition(), TabID: value.GetTabId(),
		TabLabel: value.GetTabLabel(), Order: value.GetOrder(), VisibilityCondition: value.GetVisibilityCondition(),
		RendererConfiguration: configuration,
	}, nil
}

func toWireLayoutAuthoringActionPlacement(value semantic.ActionPlacement) *runtimev1.LayoutAuthoringActionPlacement {
	actions := make([]*runtimev1.LayoutAuthoringAction, 0, len(value.Actions))
	for _, action := range value.Actions {
		actions = append(actions, &runtimev1.LayoutAuthoringAction{
			Name: action.Name, Label: action.Label, Kind: action.Kind, Icon: cloneOptionalString(action.Icon),
			TargetObjectId: cloneOptionalString(action.TargetObjectID), Component: cloneOptionalString(action.Component),
			VisibilityCondition: cloneOptionalString(action.VisibilityCondition),
			Configuration:       toWireLayoutAuthoringNamedValues(action.Configuration), Extensions: toWireLayoutAuthoringNamedValues(action.Extensions),
		})
	}
	return &runtimev1.LayoutAuthoringActionPlacement{Authored: value.Authored, Actions: actions}
}

func fromWireLayoutAuthoringActionPlacement(value *runtimev1.LayoutAuthoringActionPlacement) (semantic.ActionPlacement, error) {
	if value == nil {
		return semantic.ActionPlacement{}, invalidPlatformWire()
	}
	result := semantic.ActionPlacement{Authored: value.GetAuthored(), Actions: make([]semantic.LayoutAction, 0, len(value.GetActions()))}
	for _, action := range value.GetActions() {
		if action == nil {
			return semantic.ActionPlacement{}, invalidPlatformWire()
		}
		configuration, err := fromWireLayoutAuthoringNamedValues(action.GetConfiguration())
		if err != nil {
			return semantic.ActionPlacement{}, err
		}
		extensions, err := fromWireLayoutAuthoringNamedValues(action.GetExtensions())
		if err != nil {
			return semantic.ActionPlacement{}, err
		}
		result.Actions = append(result.Actions, semantic.LayoutAction{
			Name: action.GetName(), Label: action.GetLabel(), Kind: action.GetKind(), Icon: cloneOptionalString(action.Icon),
			TargetObjectID: cloneOptionalString(action.TargetObjectId), Component: cloneOptionalString(action.Component),
			VisibilityCondition: cloneOptionalString(action.VisibilityCondition), Configuration: configuration, Extensions: extensions,
		})
	}
	return result, nil
}

func toWireLayoutAuthoringRecordStateRule(value semantic.RecordStateRule) *runtimev1.LayoutAuthoringRecordStateRule {
	links := make([]*runtimev1.LayoutAuthoringRecordStateLink, 0, len(value.Links))
	for _, link := range value.Links {
		links = append(links, &runtimev1.LayoutAuthoringRecordStateLink{
			LinkId: link.LinkID, Label: link.Label, ReferenceFieldId: link.ReferenceFieldID, ObjectId: link.ObjectID, Placement: link.Placement,
		})
	}
	overrides := make([]*runtimev1.LayoutAuthoringRecordStateActionOverride, 0, len(value.ActionOverrides))
	for _, override := range value.ActionOverrides {
		overrides = append(overrides, &runtimev1.LayoutAuthoringRecordStateActionOverride{ActionKey: override.ActionKey, Mode: override.Mode, Reason: override.Reason})
	}
	result := &runtimev1.LayoutAuthoringRecordStateRule{RuleId: value.RuleID, Condition: value.Condition, Priority: value.Priority, Links: links, ActionOverrides: overrides}
	if value.Banner != nil {
		result.Banner = &runtimev1.LayoutAuthoringRecordStateBanner{Severity: value.Banner.Severity, Title: value.Banner.Title, Body: value.Banner.Body, LinkIds: cloneStrings(value.Banner.LinkIDs)}
	}
	if value.ReadOnly != nil {
		result.ReadOnly = &runtimev1.LayoutAuthoringRecordStateReadOnly{Enabled: value.ReadOnly.Enabled, Reason: value.ReadOnly.Reason}
	}
	return result
}

func fromWireLayoutAuthoringRecordStateRule(value *runtimev1.LayoutAuthoringRecordStateRule) (semantic.RecordStateRule, error) {
	if value == nil {
		return semantic.RecordStateRule{}, invalidPlatformWire()
	}
	result := semantic.RecordStateRule{
		RuleID: value.GetRuleId(), Condition: value.GetCondition(), Priority: value.GetPriority(),
		Links:           make([]semantic.RecordStateLink, 0, len(value.GetLinks())),
		ActionOverrides: make([]semantic.RecordStateActionOverride, 0, len(value.GetActionOverrides())),
	}
	if value.Banner != nil {
		result.Banner = &semantic.RecordStateBanner{Severity: value.Banner.GetSeverity(), Title: value.Banner.GetTitle(), Body: value.Banner.GetBody(), LinkIDs: cloneStrings(value.Banner.GetLinkIds())}
	}
	if value.ReadOnly != nil {
		result.ReadOnly = &semantic.RecordStateReadOnly{Enabled: value.ReadOnly.GetEnabled(), Reason: value.ReadOnly.GetReason()}
	}
	for _, link := range value.GetLinks() {
		if link == nil {
			return semantic.RecordStateRule{}, invalidPlatformWire()
		}
		result.Links = append(result.Links, semantic.RecordStateLink{
			LinkID: link.GetLinkId(), Label: link.GetLabel(), ReferenceFieldID: link.GetReferenceFieldId(), ObjectID: link.GetObjectId(), Placement: link.GetPlacement(),
		})
	}
	for _, override := range value.GetActionOverrides() {
		if override == nil {
			return semantic.RecordStateRule{}, invalidPlatformWire()
		}
		result.ActionOverrides = append(result.ActionOverrides, semantic.RecordStateActionOverride{ActionKey: override.GetActionKey(), Mode: override.GetMode(), Reason: override.GetReason()})
	}
	return result, nil
}

func toWireLayoutAuthoringNamedValues(values []semantic.NamedValue) []*runtimev1.LayoutAuthoringNamedValue {
	result := make([]*runtimev1.LayoutAuthoringNamedValue, 0, len(values))
	for _, value := range values {
		result = append(result, &runtimev1.LayoutAuthoringNamedValue{Name: value.Name, Value: toWireLayoutAuthoringValue(value.Value)})
	}
	return result
}

func fromWireLayoutAuthoringNamedValues(values []*runtimev1.LayoutAuthoringNamedValue) ([]semantic.NamedValue, error) {
	result := make([]semantic.NamedValue, 0, len(values))
	for _, value := range values {
		if value == nil {
			return nil, invalidPlatformWire()
		}
		mapped, err := fromWireLayoutAuthoringValue(value.GetValue())
		if err != nil {
			return nil, err
		}
		result = append(result, semantic.NamedValue{Name: value.GetName(), Value: mapped})
	}
	return result, nil
}

func toWireLayoutAuthoringValue(value semantic.Value) *runtimev1.LayoutAuthoringValue {
	result := &runtimev1.LayoutAuthoringValue{}
	switch value.Kind {
	case semantic.ValueNull:
		result.Value = &runtimev1.LayoutAuthoringValue_NullValue{NullValue: &runtimev1.LayoutAuthoringNullValue{}}
	case semantic.ValueBoolean:
		result.Value = &runtimev1.LayoutAuthoringValue_BooleanValue{BooleanValue: *value.Boolean}
	case semantic.ValueString:
		result.Value = &runtimev1.LayoutAuthoringValue_StringValue{StringValue: *value.String}
	case semantic.ValueDecimal:
		result.Value = &runtimev1.LayoutAuthoringValue_DecimalValue{DecimalValue: *value.Decimal}
	case semantic.ValueList:
		values := make([]*runtimev1.LayoutAuthoringValue, 0, len(*value.List))
		for _, item := range *value.List {
			values = append(values, toWireLayoutAuthoringValue(item))
		}
		result.Value = &runtimev1.LayoutAuthoringValue_ListValue{ListValue: &runtimev1.LayoutAuthoringValueList{Values: values}}
	case semantic.ValueObject:
		result.Value = &runtimev1.LayoutAuthoringValue_ObjectValue{ObjectValue: &runtimev1.LayoutAuthoringNamedValueList{Values: toWireLayoutAuthoringNamedValues(*value.Object)}}
	}
	return result
}

func fromWireLayoutAuthoringValue(value *runtimev1.LayoutAuthoringValue) (semantic.Value, error) {
	if value == nil {
		return semantic.Value{}, invalidPlatformWire()
	}
	switch typed := value.Value.(type) {
	case *runtimev1.LayoutAuthoringValue_NullValue:
		if typed.NullValue == nil {
			return semantic.Value{}, invalidPlatformWire()
		}
		return semantic.Value{Kind: semantic.ValueNull}, nil
	case *runtimev1.LayoutAuthoringValue_BooleanValue:
		return semantic.Value{Kind: semantic.ValueBoolean, Boolean: layoutAuthoringPointer(typed.BooleanValue)}, nil
	case *runtimev1.LayoutAuthoringValue_StringValue:
		return semantic.Value{Kind: semantic.ValueString, String: layoutAuthoringPointer(typed.StringValue)}, nil
	case *runtimev1.LayoutAuthoringValue_DecimalValue:
		return semantic.Value{Kind: semantic.ValueDecimal, Decimal: layoutAuthoringPointer(typed.DecimalValue)}, nil
	case *runtimev1.LayoutAuthoringValue_ListValue:
		if typed.ListValue == nil {
			return semantic.Value{}, invalidPlatformWire()
		}
		values := make([]semantic.Value, 0, len(typed.ListValue.GetValues()))
		for _, item := range typed.ListValue.GetValues() {
			mapped, err := fromWireLayoutAuthoringValue(item)
			if err != nil {
				return semantic.Value{}, err
			}
			values = append(values, mapped)
		}
		return semantic.Value{Kind: semantic.ValueList, List: &values}, nil
	case *runtimev1.LayoutAuthoringValue_ObjectValue:
		if typed.ObjectValue == nil {
			return semantic.Value{}, invalidPlatformWire()
		}
		values, err := fromWireLayoutAuthoringNamedValues(typed.ObjectValue.GetValues())
		if err != nil {
			return semantic.Value{}, err
		}
		return semantic.Value{Kind: semantic.ValueObject, Object: &values}, nil
	default:
		return semantic.Value{}, invalidPlatformWire()
	}
}

func cloneStrings(values []string) []string {
	return append([]string{}, values...)
}

func layoutAuthoringPointer[T any](value T) *T {
	return &value
}
