package service

import (
	"axolotl/models"
	"axolotl/store"
	"net/http"
)

// NodeService is the single entry point for all node operations.
// All data-model integrity rules are enforced here; callers cannot produce
// invalid state by interacting with this interface alone.
//
// Every NodeService instance is bound to a specific user (see User()).
type NodeService interface {
	// User returns the name/ID of the user this service instance acts on behalf of.
	User() string

	// Query
	GetByID(id string) (*models.Node, error)
	List(filter ListFilter) ([]*models.Node, error)

	// Lifecycle
	Add(input AddInput) (*models.Node, error)
	Update(id string, input UpdateInput) (*models.Node, error)
	Delete(id string) error

	// User management
	AddUser(name string) (*models.Node, error)
	ListUsers() ([]*models.Node, error)
}

// AddInput describes a new node to create.
// Rels may contain tag rels (Target == ""), property rels (Target == "",
// Type is "prefix::value"), and edge rels (Target is a node name or ID).
// The service applies defaults (type=issue, status=open for issues) and validates.
type AddInput struct {
	Title     string
	Content   string
	DueDate   string
	Namespace string // namespace name or ID; defaults to the user's personal namespace
	Rels      []RelInput
}

// UpdateInput describes changes to apply to an existing node.
// AddRels and RemoveRels accept both tag rels (Target == "") and edge rels.
// Setting _status::done in AddRels is rejected when the node has open blockers.
// Adding an assignee rel replaces the previous single target.
// Setting Namespace transfers ownership from the current namespace to the new one.
type UpdateInput struct {
	Title      *string
	Content    *string
	DueDate    *string // nil = no change; pointer to "" = clear due date
	Namespace  *string // nil = no change; namespace name or ID to move node into
	AddRels    []RelInput
	RemoveRels []RelInput
}

// ListFilter specifies which nodes to return. Empty slices are ignored.
// Tag filters (Target == "") match by rel_name prefix.
// Edge filters (Target != "") are resolved to node IDs.
type ListFilter struct {
	Namespace  string // when non-empty, only return nodes owned by this namespace
	Rels       []RelInput
	HasDueDate bool // when true, only return nodes that have a due date set
	DueWithin  *int // when non-nil, only return nodes due within this many days (includes overdue)
}

// RelInput is a typed, directed rel with a target that may be a name or node ID.
// Target == "" means this is a tag or property rel (no target node).
type RelInput struct {
	Type   models.RelType
	Target string // name or node ID; the service resolves names. Empty = tag rel.
}

func NewLocalNodeService(st store.GraphStore, user string) NodeService {
	return &nodeServiceImpl{store: st, userID: user}
}

func NewRemoteNodeService(base, user string) NodeService {
	return &apiClient{base: base, user: user, http: &http.Client{}}
}
