package models

import (
	"encoding/json"
	"fmt"
	"slices"
	"strings"
	"time"
)

// Date is a date-only time value that marshals as "YYYY-MM-DD" in JSON.
type Date struct{ time.Time }

// ParseDate parses a date string in "YYYY-MM-DD" or RFC3339 format.
func ParseDate(s string) (Date, error) {
	for _, layout := range []string{"2006-01-02", time.RFC3339} {
		if t, err := time.Parse(layout, s); err == nil {
			return Date{t.UTC()}, nil
		}
	}
	return Date{}, fmt.Errorf("cannot parse date %q: expected YYYY-MM-DD", s)
}

func (d Date) MarshalJSON() ([]byte, error) {
	return json.Marshal(d.Format("2006-01-02"))
}

func (d *Date) UnmarshalJSON(b []byte) error {
	var s string
	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}
	parsed, err := ParseDate(s)
	if err != nil {
		return err
	}
	*d = parsed
	return nil
}

type Node struct {
	ID        string              `json:"id"`
	Title     string              `json:"title"`
	Content   string              `json:"content,omitempty"`
	DueDate   *Date               `json:"due_date,omitempty"`
	CreatedAt string              `json:"created_at"`
	UpdatedAt string              `json:"updated_at"`
	Tags      []string            `json:"tags,omitempty"`
	Relations map[string][]string `json:"relations,omitempty"`
}

func NewNode() *Node {
	return &Node{
		Relations: make(map[string][]string),
	}
}

func (n *Node) AddTag(tag string) {
	if tag == "" {
		return
	}
	// If it's a property (name::value format), replace any existing tag with the same prefix.
	if idx := strings.Index(tag, "::"); idx >= 0 {
		prefix := tag[:idx+2]
		var newTags []string
		for _, t := range n.Tags {
			if !strings.HasPrefix(t, prefix) {
				newTags = append(newTags, t)
			}
		}
		n.Tags = newTags
	}
	if !slices.Contains(n.Tags, tag) {
		n.Tags = append(n.Tags, tag)
	}
}

func (n *Node) RemoveTag(tag string) {
	var newTags []string
	for _, t := range n.Tags {
		if t != tag {
			newTags = append(newTags, t)
		}
	}
	n.Tags = newTags
}

func (n *Node) AddRelation(relType RelType, target string) {
	if n.Relations == nil {
		n.Relations = make(map[string][]string)
	}
	if relType == RelAssignee || relType == RelCreated {
		n.Relations[string(relType)] = []string{target}
		return
	}
	if !slices.Contains(n.Relations[string(relType)], target) {
		n.Relations[string(relType)] = append(n.Relations[string(relType)], target)
	}
}

func (n *Node) RemoveRelation(relType RelType, target string) {
	if n.Relations == nil {
		return
	}
	var newTgts []string
	for _, tgt := range n.Relations[string(relType)] {
		if tgt != target {
			newTgts = append(newTgts, tgt)
		}
	}
	if len(newTgts) == 0 {
		delete(n.Relations, string(relType))
	} else {
		n.Relations[string(relType)] = newTgts
	}
}

func (n *Node) GetProperty(k string) string {
	prefix := "_" + k + "::"
	for _, t := range n.Tags {
		if strings.HasPrefix(t, prefix) {
			return strings.TrimPrefix(t, prefix)
		}
	}
	return ""
}

func (n *Node) GetDisplayTags() []string {
	var tags []string
	for _, t := range n.Tags {
		if !strings.HasPrefix(t, "_") {
			tags = append(tags, t)
		}
	}
	return tags
}
