package main

import (
	"context"
	"path/filepath"
	"testing"
	"time"

	"github.com/steveyegge/beads/internal/types"
)

func TestCreate_BasicIssue(t *testing.T) {
	tmpDir := t.TempDir()
	testDB := filepath.Join(tmpDir, ".beads", "beads.db")
	s := newTestStore(t, testDB)
	ctx := context.Background()

	issue := &types.Issue{
		Title:     "Test Issue",
		Priority:  1,
		IssueType: types.TypeBug,
		Status:    types.StatusOpen,
		CreatedAt: time.Now(),
	}

	if err := s.CreateIssue(ctx, issue, "test"); err != nil {
		t.Fatalf("failed to create issue: %v", err)
	}

	issues, err := s.SearchIssues(ctx, "", types.IssueFilter{})
	if err != nil {
		t.Fatalf("failed to search issues: %v", err)
	}

	if len(issues) != 1 {
		t.Fatalf("expected 1 issue, got %d", len(issues))
	}

	created := issues[0]
	if created.Title != "Test Issue" {
		t.Errorf("expected title 'Test Issue', got %q", created.Title)
	}
	if created.Priority != 1 {
		t.Errorf("expected priority 1, got %d", created.Priority)
	}
	if created.IssueType != types.TypeBug {
		t.Errorf("expected type bug, got %s", created.IssueType)
	}
}

func TestCreate_WithDescription(t *testing.T) {
	tmpDir := t.TempDir()
	testDB := filepath.Join(tmpDir, ".beads", "beads.db")
	s := newTestStore(t, testDB)
	ctx := context.Background()

	issue := &types.Issue{
		Title:       "Issue with desc",
		Description: "This is a description",
		Priority:    2,
		Status:      types.StatusOpen,
		IssueType:   types.TypeTask,
		CreatedAt:   time.Now(),
	}

	if err := s.CreateIssue(ctx, issue, "test"); err != nil {
		t.Fatalf("failed to create issue: %v", err)
	}

	issues, err := s.SearchIssues(ctx, "", types.IssueFilter{})
	if err != nil {
		t.Fatalf("failed to search issues: %v", err)
	}

	if len(issues) != 1 {
		t.Fatalf("expected 1 issue, got %d", len(issues))
	}

	if issues[0].Description != "This is a description" {
		t.Errorf("expected description, got %q", issues[0].Description)
	}
}

func TestCreate_WithDesignAndAcceptance(t *testing.T) {
	tmpDir := t.TempDir()
	testDB := filepath.Join(tmpDir, ".beads", "beads.db")
	s := newTestStore(t, testDB)
	ctx := context.Background()

	issue := &types.Issue{
		Title:              "Feature with design",
		Design:             "Use MVC pattern",
		AcceptanceCriteria: "All tests pass",
		IssueType:          types.TypeFeature,
		Priority:           2,
		Status:             types.StatusOpen,
		CreatedAt:          time.Now(),
	}

	if err := s.CreateIssue(ctx, issue, "test"); err != nil {
		t.Fatalf("failed to create issue: %v", err)
	}

	issues, err := s.SearchIssues(ctx, "", types.IssueFilter{})
	if err != nil {
		t.Fatalf("failed to search issues: %v", err)
	}

	if len(issues) != 1 {
		t.Fatalf("expected 1 issue, got %d", len(issues))
	}

	created := issues[0]
	if created.Design != "Use MVC pattern" {
		t.Errorf("expected design, got %q", created.Design)
	}
	if created.AcceptanceCriteria != "All tests pass" {
		t.Errorf("expected acceptance criteria, got %q", created.AcceptanceCriteria)
	}
}

func TestCreate_WithLabels(t *testing.T) {
	tmpDir := t.TempDir()
	testDB := filepath.Join(tmpDir, ".beads", "beads.db")
	s := newTestStore(t, testDB)
	ctx := context.Background()

	issue := &types.Issue{
		Title:     "Issue with labels",
		Priority:  0,
		Status:    types.StatusOpen,
		IssueType: types.TypeBug,
		CreatedAt: time.Now(),
	}

	if err := s.CreateIssue(ctx, issue, "test"); err != nil {
		t.Fatalf("failed to create issue: %v", err)
	}

	// Add labels
	if err := s.AddLabel(ctx, issue.ID, "bug", "test"); err != nil {
		t.Fatalf("failed to add bug label: %v", err)
	}
	if err := s.AddLabel(ctx, issue.ID, "critical", "test"); err != nil {
		t.Fatalf("failed to add critical label: %v", err)
	}

	labels, err := s.GetLabels(ctx, issue.ID)
	if err != nil {
		t.Fatalf("failed to get labels: %v", err)
	}

	if len(labels) != 2 {
		t.Errorf("expected 2 labels, got %d", len(labels))
	}

	labelMap := make(map[string]bool)
	for _, l := range labels {
		labelMap[l] = true
	}

	if !labelMap["bug"] || !labelMap["critical"] {
		t.Errorf("expected labels 'bug' and 'critical', got %v", labels)
	}
}

func TestCreate_WithDependencies(t *testing.T) {
	tmpDir := t.TempDir()
	testDB := filepath.Join(tmpDir, ".beads", "beads.db")
	s := newTestStore(t, testDB)
	ctx := context.Background()

	parent := &types.Issue{
		Title:     "Parent issue",
		Priority:  1,
		Status:    types.StatusOpen,
		IssueType: types.TypeTask,
		CreatedAt: time.Now(),
	}

	if err := s.CreateIssue(ctx, parent, "test"); err != nil {
		t.Fatalf("failed to create parent: %v", err)
	}

	child := &types.Issue{
		Title:     "Child issue",
		Priority:  1,
		Status:    types.StatusOpen,
		IssueType: types.TypeTask,
		CreatedAt: time.Now(),
	}

	if err := s.CreateIssue(ctx, child, "test"); err != nil {
		t.Fatalf("failed to create child: %v", err)
	}

	// Add dependency
	dep := &types.Dependency{
		IssueID:     child.ID,
		DependsOnID: parent.ID,
		Type:        types.DepBlocks,
		CreatedAt:   time.Now(),
	}

	if err := s.AddDependency(ctx, dep, "test"); err != nil {
		t.Fatalf("failed to add dependency: %v", err)
	}

	deps, err := s.GetDependencies(ctx, child.ID)
	if err != nil {
		t.Fatalf("failed to get dependencies: %v", err)
	}

	if len(deps) != 1 {
		t.Fatalf("expected 1 dependency, got %d", len(deps))
	}

	if deps[0].ID != parent.ID {
		t.Errorf("expected dependency on %s, got %s", parent.ID, deps[0].ID)
	}
}

func TestCreate_WithDiscoveredFromDependency(t *testing.T) {
	tmpDir := t.TempDir()
	testDB := filepath.Join(tmpDir, ".beads", "beads.db")
	s := newTestStore(t, testDB)
	ctx := context.Background()

	parent := &types.Issue{
		Title:     "Parent work",
		Priority:  1,
		Status:    types.StatusOpen,
		IssueType: types.TypeTask,
		CreatedAt: time.Now(),
	}

	if err := s.CreateIssue(ctx, parent, "test"); err != nil {
		t.Fatalf("failed to create parent: %v", err)
	}

	discovered := &types.Issue{
		Title:     "Found bug",
		Priority:  1,
		Status:    types.StatusOpen,
		IssueType: types.TypeBug,
		CreatedAt: time.Now(),
	}

	if err := s.CreateIssue(ctx, discovered, "test"); err != nil {
		t.Fatalf("failed to create discovered issue: %v", err)
	}

	// Add discovered-from dependency
	dep := &types.Dependency{
		IssueID:     discovered.ID,
		DependsOnID: parent.ID,
		Type:        types.DepDiscoveredFrom,
		CreatedAt:   time.Now(),
	}

	if err := s.AddDependency(ctx, dep, "test"); err != nil {
		t.Fatalf("failed to add dependency: %v", err)
	}

	deps, err := s.GetDependencies(ctx, discovered.ID)
	if err != nil {
		t.Fatalf("failed to get dependencies: %v", err)
	}

	if len(deps) != 1 {
		t.Fatalf("expected 1 dependency, got %d", len(deps))
	}

	if deps[0].ID != parent.ID {
		t.Errorf("expected dependency on %s, got %s", parent.ID, deps[0].ID)
	}
}

func TestCreate_WithExplicitID(t *testing.T) {
	tmpDir := t.TempDir()
	testDB := filepath.Join(tmpDir, ".beads", "beads.db")
	s := newTestStore(t, testDB)
	ctx := context.Background()

	issue := &types.Issue{
		ID:        "test-abc123",
		Title:     "Custom ID issue",
		Priority:  1,
		Status:    types.StatusOpen,
		IssueType: types.TypeTask,
		CreatedAt: time.Now(),
	}

	if err := s.CreateIssue(ctx, issue, "test"); err != nil {
		t.Fatalf("failed to create issue: %v", err)
	}

	issues, err := s.SearchIssues(ctx, "", types.IssueFilter{})
	if err != nil {
		t.Fatalf("failed to search issues: %v", err)
	}

	if len(issues) != 1 {
		t.Fatalf("expected 1 issue, got %d", len(issues))
	}

	if issues[0].ID != "test-abc123" {
		t.Errorf("expected ID 'test-abc123', got %q", issues[0].ID)
	}
}

func TestCreate_WithAssignee(t *testing.T) {
	tmpDir := t.TempDir()
	testDB := filepath.Join(tmpDir, ".beads", "beads.db")
	s := newTestStore(t, testDB)
	ctx := context.Background()

	issue := &types.Issue{
		Title:     "Assigned issue",
		Assignee:  "alice",
		Priority:  1,
		Status:    types.StatusOpen,
		IssueType: types.TypeTask,
		CreatedAt: time.Now(),
	}

	if err := s.CreateIssue(ctx, issue, "test"); err != nil {
		t.Fatalf("failed to create issue: %v", err)
	}

	issues, err := s.SearchIssues(ctx, "", types.IssueFilter{})
	if err != nil {
		t.Fatalf("failed to search issues: %v", err)
	}

	if len(issues) != 1 {
		t.Fatalf("expected 1 issue, got %d", len(issues))
	}

	if issues[0].Assignee != "alice" {
		t.Errorf("expected assignee 'alice', got %q", issues[0].Assignee)
	}
}

func TestCreate_AllIssueTypes(t *testing.T) {
	tmpDir := t.TempDir()
	testDB := filepath.Join(tmpDir, ".beads", "beads.db")
	s := newTestStore(t, testDB)
	ctx := context.Background()

	issueTypes := []types.IssueType{
		types.TypeBug,
		types.TypeFeature,
		types.TypeTask,
		types.TypeEpic,
		types.TypeChore,
	}

	for _, issueType := range issueTypes {
		issue := &types.Issue{
			Title:     "Test " + string(issueType),
			IssueType: issueType,
			Priority:  2,
			Status:    types.StatusOpen,
			CreatedAt: time.Now(),
		}

		if err := s.CreateIssue(ctx, issue, "test"); err != nil {
			t.Fatalf("failed to create issue type %s: %v", issueType, err)
		}
	}

	issues, err := s.SearchIssues(ctx, "", types.IssueFilter{})
	if err != nil {
		t.Fatalf("failed to search issues: %v", err)
	}

	if len(issues) != 5 {
		t.Errorf("expected 5 issues, got %d", len(issues))
	}
}

func TestCreate_MultipleDependencies(t *testing.T) {
	tmpDir := t.TempDir()
	testDB := filepath.Join(tmpDir, ".beads", "beads.db")
	s := newTestStore(t, testDB)
	ctx := context.Background()

	parent1 := &types.Issue{
		Title:     "Parent 1",
		Priority:  1,
		Status:    types.StatusOpen,
		IssueType: types.TypeTask,
		CreatedAt: time.Now(),
	}

	parent2 := &types.Issue{
		Title:     "Parent 2",
		Priority:  1,
		Status:    types.StatusOpen,
		IssueType: types.TypeTask,
		CreatedAt: time.Now(),
	}

	child := &types.Issue{
		Title:     "Child",
		Priority:  1,
		Status:    types.StatusOpen,
		IssueType: types.TypeTask,
		CreatedAt: time.Now(),
	}

	if err := s.CreateIssue(ctx, parent1, "test"); err != nil {
		t.Fatalf("failed to create parent1: %v", err)
	}
	if err := s.CreateIssue(ctx, parent2, "test"); err != nil {
		t.Fatalf("failed to create parent2: %v", err)
	}
	if err := s.CreateIssue(ctx, child, "test"); err != nil {
		t.Fatalf("failed to create child: %v", err)
	}

	// Add multiple dependencies
	dep1 := &types.Dependency{
		IssueID:     child.ID,
		DependsOnID: parent1.ID,
		Type:        types.DepBlocks,
		CreatedAt:   time.Now(),
	}
	dep2 := &types.Dependency{
		IssueID:     child.ID,
		DependsOnID: parent2.ID,
		Type:        types.DepRelated,
		CreatedAt:   time.Now(),
	}

	if err := s.AddDependency(ctx, dep1, "test"); err != nil {
		t.Fatalf("failed to add dep1: %v", err)
	}
	if err := s.AddDependency(ctx, dep2, "test"); err != nil {
		t.Fatalf("failed to add dep2: %v", err)
	}

	deps, err := s.GetDependencies(ctx, child.ID)
	if err != nil {
		t.Fatalf("failed to get dependencies: %v", err)
	}

	if len(deps) != 2 {
		t.Fatalf("expected 2 dependencies, got %d", len(deps))
	}
}
