package notes

import (
	"fmt"
	"regexp"
	"time"

	"gorm.io/gorm"
)

// Note represents a note in the system
type Note struct {
	ID        string    `json:"id" gorm:"primaryKey"`
	Title     string    `json:"title" gorm:"not null"`
	Content   string    `json:"content" gorm:"not null"`
	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`
	// Link relationships
	LinksTo  []*Note `json:"linksTo,omitempty" gorm:"many2many:note_links;joinForeignKey:source_note_id;joinReferences:target_note_id"`
	LinkedBy []*Note `json:"linkedBy,omitempty" gorm:"many2many:note_links;joinForeignKey:target_note_id;joinReferences:source_note_id"`
}

// NoteLink represents a connection between two notes
type NoteLink struct {
	SourceNoteID string    `gorm:"primaryKey"`
	TargetNoteID string    `gorm:"primaryKey"`
	CreatedAt    time.Time `gorm:"autoCreateTime"`
}

// ExtractLinks finds all [[note-title]] style links in the content
func (n *Note) ExtractLinks(content string) []string {
	re := regexp.MustCompile(`\[\[(.*?)\]\]`)
	matches := re.FindAllStringSubmatch(content, -1)
	titles := make([]string, 0, len(matches))
	for _, match := range matches {
		if len(match) > 1 {
			titles = append(titles, match[1])
		}
	}
	return titles
}

// UpdateLinks updates the note's links based on its content
func (n *Note) UpdateLinks(db *gorm.DB) error {
	// Delete existing links
	if err := db.Where("source_note_id = ?", n.ID).Delete(&NoteLink{}).Error; err != nil {
		return fmt.Errorf("failed to delete existing links: %w", err)
	}

	// Extract and create new links
	titles := n.ExtractLinks(n.Content)

	// Use a map to track unique target IDs to avoid duplicates
	processedTargets := make(map[string]bool)

	for _, title := range titles {
		var target Note
		if err := db.Where("title = ?", title).First(&target).Error; err != nil {
			if err == gorm.ErrRecordNotFound {
				// Skip non-existent notes
				continue
			}
			return fmt.Errorf("failed to find target note %q: %w", title, err)
		}

		// Skip if we've already processed this target
		if processedTargets[target.ID] {
			continue
		}
		processedTargets[target.ID] = true

		link := NoteLink{
			SourceNoteID: n.ID,
			TargetNoteID: target.ID,
		}

		// Use FirstOrCreate to avoid unique constraint errors
		var existingLink NoteLink
		result := db.Where("source_note_id = ? AND target_note_id = ?", n.ID, target.ID).FirstOrCreate(&existingLink, link)
		if result.Error != nil {
			return fmt.Errorf("failed to create link to %q: %w", title, result.Error)
		}
	}

	return nil
}