package notes

import (
	"testing"
	"time"
)

func TestService_Create(t *testing.T) {
	db := setupTestDB(t)
	service := NewService(db)

	note := &Note{
		Title:     "Test Note",
		Content:   "Content with [[Another Note]]",
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}

	// Test creating a note
	if err := service.Create(note); err != nil {
		t.Fatalf("Failed to create note: %v", err)
	}

	if note.ID == "" {
		t.Error("Note ID was not set")
	}

	// Test that no links were created (target note doesn't exist)
	if len(note.LinksTo) != 0 {
		t.Errorf("Expected 0 links, got %d", len(note.LinksTo))
	}

	// Create target note and update original note
	another := &Note{
		Title:     "Another Note",
		Content:   "Some content",
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}
	if err := service.Create(another); err != nil {
		t.Fatalf("Failed to create another note: %v", err)
	}

	// Update original note to create the link
	if err := service.Update(note.ID, map[string]interface{}{
		"content": note.Content,
	}); err != nil {
		t.Fatalf("Failed to update note: %v", err)
	}

	// Get the note and verify the link was created
	updated, err := service.Get(note.ID)
	if err != nil {
		t.Fatalf("Failed to get note: %v", err)
	}

	if len(updated.LinksTo) != 1 {
		t.Errorf("Expected 1 link, got %d", len(updated.LinksTo))
	}

	// Verify that linked note only contains id and title
	if link := updated.LinksTo[0]; link != nil {
		if link.Content != "" {
			t.Error("Link should not include content")
		}
		if link.ID == "" {
			t.Error("Link should include ID")
		}
		if link.Title == "" {
			t.Error("Link should include Title")
		}
		if !link.CreatedAt.IsZero() {
			t.Error("Link should not include CreatedAt")
		}
		if !link.UpdatedAt.IsZero() {
			t.Error("Link should not include UpdatedAt")
		}
	}
}

func TestService_Get(t *testing.T) {
	db := setupTestDB(t)
	service := NewService(db)

	// Test getting non-existent note
	note, err := service.Get("non-existent")
	if err != nil {
		t.Fatalf("Expected nil error for non-existent note, got %v", err)
	}
	if note != nil {
		t.Error("Expected nil note for non-existent ID")
	}

	// Create a note and test getting it
	created := &Note{
		Title:     "Test Note",
		Content:   "Test content",
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}
	if err := service.Create(created); err != nil {
		t.Fatalf("Failed to create note: %v", err)
	}

	// Create another note that links to the first one
	linking := &Note{
		Title:     "Linking Note",
		Content:   "Links to [[Test Note]]",
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}
	if err := service.Create(linking); err != nil {
		t.Fatalf("Failed to create linking note: %v", err)
	}

	// Get the first note and verify relationships
	note, err = service.Get(created.ID)
	if err != nil {
		t.Fatalf("Failed to get note: %v", err)
	}
	if note == nil {
		t.Fatal("Expected note, got nil")
	}

	// Verify basic fields
	if note.Title != created.Title {
		t.Errorf("Expected title %q, got %q", created.Title, note.Title)
	}

	// Verify LinkedBy relationship
	if len(note.LinkedBy) != 1 {
		t.Errorf("Expected 1 LinkedBy relationship, got %d", len(note.LinkedBy))
	}

	// Verify that linking note only contains id and title
	if link := note.LinkedBy[0]; link != nil {
		if link.Content != "" {
			t.Error("LinkedBy should not include content")
		}
		if link.ID == "" {
			t.Error("LinkedBy should include ID")
		}
		if link.Title == "" {
			t.Error("LinkedBy should include Title")
		}
		if !link.CreatedAt.IsZero() {
			t.Error("LinkedBy should not include CreatedAt")
		}
		if !link.UpdatedAt.IsZero() {
			t.Error("LinkedBy should not include UpdatedAt")
		}
	}
}

func TestService_List(t *testing.T) {
	db := setupTestDB(t)
	service := NewService(db)

	// Test empty list
	notes, err := service.List()
	if err != nil {
		t.Fatalf("Failed to list notes: %v", err)
	}
	if len(notes) != 0 {
		t.Errorf("Expected empty list, got %d notes", len(notes))
	}

	// Create some notes
	note1 := &Note{
		Title:     "First Note",
		Content:   "First content",
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}
	note2 := &Note{
		Title:     "Second Note",
		Content:   "Second content with [[First Note]]",
		CreatedAt: time.Now(),
		UpdatedAt: time.Now().Add(time.Hour), // Later update time
	}

	if err := service.Create(note1); err != nil {
		t.Fatalf("Failed to create note1: %v", err)
	}
	if err := service.Create(note2); err != nil {
		t.Fatalf("Failed to create note2: %v", err)
	}

	// Test listing notes
	notes, err = service.List()
	if err != nil {
		t.Fatalf("Failed to list notes: %v", err)
	}
	if len(notes) != 2 {
		t.Errorf("Expected 2 notes, got %d", len(notes))
	}

	// Verify order (most recently updated first)
	if notes[0].ID != note2.ID {
		t.Error("Notes not ordered by updated_at desc")
	}

	// Verify relationships
	firstNote := notes[1] // note1 should be second due to update time
	if len(firstNote.LinkedBy) != 1 {
		t.Errorf("Expected First Note to have 1 LinkedBy, got %d", len(firstNote.LinkedBy))
	}

	// Verify that linking note only contains id and title
	if link := firstNote.LinkedBy[0]; link != nil {
		if link.Content != "" {
			t.Error("LinkedBy should not include content")
		}
		if link.ID == "" {
			t.Error("LinkedBy should include ID")
		}
		if link.Title == "" {
			t.Error("LinkedBy should include Title")
		}
		if !link.CreatedAt.IsZero() {
			t.Error("LinkedBy should not include CreatedAt")
		}
		if !link.UpdatedAt.IsZero() {
			t.Error("LinkedBy should not include UpdatedAt")
		}
	}

	secondNote := notes[0] // note2 should be first due to update time
	if len(secondNote.LinksTo) != 1 {
		t.Errorf("Expected Second Note to have 1 LinksTo, got %d", len(secondNote.LinksTo))
	}

	// Verify that linked note only contains id and title
	if link := secondNote.LinksTo[0]; link != nil {
		if link.Content != "" {
			t.Error("LinksTo should not include content")
		}
		if link.ID == "" {
			t.Error("LinksTo should include ID")
		}
		if link.Title == "" {
			t.Error("LinksTo should include Title")
		}
		if !link.CreatedAt.IsZero() {
			t.Error("LinksTo should not include CreatedAt")
		}
		if !link.UpdatedAt.IsZero() {
			t.Error("LinksTo should not include UpdatedAt")
		}
	}
}

func TestService_Delete(t *testing.T) {
	db := setupTestDB(t)
	service := NewService(db)

	// Create a note
	note := &Note{
		Title:     "Test Note",
		Content:   "Test content",
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}
	if err := service.Create(note); err != nil {
		t.Fatalf("Failed to create note: %v", err)
	}

	// Delete the note
	if err := service.Delete(note.ID); err != nil {
		t.Fatalf("Failed to delete note: %v", err)
	}

	// Verify note is deleted
	found, err := service.Get(note.ID)
	if err != nil {
		t.Fatalf("Failed to check deleted note: %v", err)
	}
	if found != nil {
		t.Error("Note still exists after deletion")
	}
}

func TestService_Reset(t *testing.T) {
	db := setupTestDB(t)
	service := NewService(db)

	// Create some notes
	note1 := &Note{
		Title:     "First Note",
		Content:   "First content",
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}
	note2 := &Note{
		Title:     "Second Note",
		Content:   "Second content",
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}

	if err := service.Create(note1); err != nil {
		t.Fatalf("Failed to create note1: %v", err)
	}
	if err := service.Create(note2); err != nil {
		t.Fatalf("Failed to create note2: %v", err)
	}

	// Reset the database
	if err := service.Reset(); err != nil {
		t.Fatalf("Failed to reset database: %v", err)
	}

	// Verify all notes are deleted
	notes, err := service.List()
	if err != nil {
		t.Fatalf("Failed to list notes after reset: %v", err)
	}
	if len(notes) != 0 {
		t.Errorf("Expected empty database after reset, got %d notes", len(notes))
	}
}