package app

import (
	"context"
	"fmt"
	"time"

	"github.com/dtoro/oikos/internal/core/domain"
	"github.com/dtoro/oikos/internal/core/ports"
)

// RelationshipService owns the relationship aggregate's use-cases: create
// with ontology edge validation, soft-delete, and list reads. The adapter
// resolves slug→entity and extracts types before calling the service; the
// service validates endpoint types against the (cached) ontology.
type RelationshipService struct {
	rels ports.RelationshipRepository
	onto ports.OntologyStore
}

// NewRelationshipService wires the service.
func NewRelationshipService(rels ports.RelationshipRepository, onto ports.OntologyStore) *RelationshipService {
	return &RelationshipService{rels: rels, onto: onto}
}

// CreateRelationshipCmd is one edge creation. SourceType and TargetType are
// the resolved entity types (used for ontology edge validation).
type CreateRelationshipCmd struct {
	SourceID   domain.UUID
	TargetID   domain.UUID
	SourceType string
	TargetType string
	Type       string
	Attributes map[string]any
	ActorType  string
	Actor      string
	Method     string
	Path       string
}

// Create validates the edge against the ontology and creates it in one
// transaction.
func (s *RelationshipService) Create(ctx context.Context, cmd CreateRelationshipCmd) (domain.Relationship, error) {
	tree, err := s.onto.LoadTypeTree(ctx)
	if err != nil {
		return domain.Relationship{}, err
	}
	if err := tree.ValidateEdge(cmd.Type, cmd.SourceType, cmd.TargetType); err != nil {
		return domain.Relationship{}, fmt.Errorf("invalid edge: %w", err)
	}

	rel := domain.Relationship{
		SourceID:   cmd.SourceID,
		TargetID:   cmd.TargetID,
		Type:       cmd.Type,
		Attributes: cmd.Attributes,
		ValidFrom:  time.Now(),
	}

	created, err := s.rels.Create(ctx, ports.RelationshipCreateInput{
		Relationship: rel,
		Audit: []ports.AuditEntry{{
			ActorType: cmd.ActorType, ActorLabel: cmd.Actor, Action: "create",
			EntityID: cmd.SourceID, Method: cmd.Method, Path: cmd.Path,
			Details: map[string]any{"source": cmd.SourceID, "target": cmd.TargetID, "type": cmd.Type},
		}},
	})
	if err != nil {
		return domain.Relationship{}, err
	}
	return created, nil
}

// End terminates an active relationship (soft-delete). Audit is kept at the
// adapter level since End is a simple state toggle with no ontology check.
func (s *RelationshipService) End(ctx context.Context, source, target domain.UUID, relType string) error {
	return s.rels.End(ctx, source, target, relType)
}