package authoring

import (
	"context"
	"errors"
	"os"
	"os/exec"
	"path/filepath"
	"testing"

	"github.com/nomadcode/nomadcode-core/internal/roadmapsync"
)

func setupTestGitRepo(t *testing.T) string {
	t.Helper()
	tmpDir, err := os.MkdirTemp("", "nomadcode-test-repo-*")
	if err != nil {
		t.Fatalf("failed to create temp dir: %v", err)
	}

	// git init
	cmd := exec.Command("git", "init")
	cmd.Dir = tmpDir
	if err := cmd.Run(); err != nil {
		t.Fatalf("failed to git init: %v", err)
	}

	// git config user.name and user.email (just in case)
	cmd = exec.Command("git", "config", "user.name", "test")
	cmd.Dir = tmpDir
	_ = cmd.Run()
	cmd = exec.Command("git", "config", "user.email", "test@test.com")
	cmd.Dir = tmpDir
	_ = cmd.Run()

	return tmpDir
}

func TestEnsureProviderIdentityInjectsMissingBlock(t *testing.T) {
	tmpDir := setupTestGitRepo(t)
	defer os.RemoveAll(tmpDir)

	milestoneDir := filepath.Join(tmpDir, "agent-roadmap", "phase", "test-phase", "milestones")
	if err := os.MkdirAll(milestoneDir, 0755); err != nil {
		t.Fatalf("failed to create milestone dir: %v", err)
	}

	milestonePath := filepath.Join(milestoneDir, "test-milestone.md")
	content := "# Test Milestone\nSome description here.\n"
	if err := os.WriteFile(milestonePath, []byte(content), 0644); err != nil {
		t.Fatalf("failed to write milestone file: %v", err)
	}

	writer := NewLocalIdentityWriter()
	ident := roadmapsync.Identity{
		Provider:      "plane",
		WorkItemID:    "work-123",
		Tenant:        "tenant-abc",
		Project:       "project-xyz",
		RoadmapItemID: "roadmap-999",
	}

	res, err := writer.EnsureProviderIdentity(context.Background(), EnsureProviderIdentityInput{
		WorkspacePath: tmpDir,
		Identity:      ident,
	})
	if err != nil {
		t.Fatalf("EnsureProviderIdentity failed: %v", err)
	}

	if len(res.InjectedPaths) != 1 || res.InjectedPaths[0] != "agent-roadmap/phase/test-phase/milestones/test-milestone.md" {
		t.Errorf("unexpected injected paths: got %v", res.InjectedPaths)
	}

	// Read and verify injected content
	newContentBytes, err := os.ReadFile(milestonePath)
	if err != nil {
		t.Fatalf("failed to read file: %v", err)
	}
	newContent := string(newContentBytes)

	parsed, err := roadmapsync.ParseMilestoneIdentity("agent-roadmap/phase/test-phase/milestones/test-milestone.md", newContent)
	if err != nil {
		t.Fatalf("failed to parse injected identity: %v", err)
	}

	if parsed.Provider != "plane" || parsed.WorkItemID != "work-123" || parsed.Tenant != "tenant-abc" || parsed.Project != "project-xyz" || parsed.RoadmapItemID != "roadmap-999" {
		t.Errorf("unexpected parsed identity: %+v", parsed)
	}
}

func TestEnsureProviderIdentityKeepsExistingBlock(t *testing.T) {
	tmpDir := setupTestGitRepo(t)
	defer os.RemoveAll(tmpDir)

	milestoneDir := filepath.Join(tmpDir, "agent-roadmap", "phase", "test-phase", "milestones")
	if err := os.MkdirAll(milestoneDir, 0755); err != nil {
		t.Fatalf("failed to create milestone dir: %v", err)
	}

	milestonePath := filepath.Join(milestoneDir, "test-milestone.md")
	content := `# Test Milestone
Some description here.

## Provider identity
- provider: plane
- tenant: tenant-abc
- project: project-xyz
- work item id: work-123
- milestone id: roadmap-999
`
	if err := os.WriteFile(milestonePath, []byte(content), 0644); err != nil {
		t.Fatalf("failed to write milestone file: %v", err)
	}

	writer := NewLocalIdentityWriter()
	ident := roadmapsync.Identity{
		Provider:      "plane",
		WorkItemID:    "work-123",
		Tenant:        "tenant-abc",
		Project:       "project-xyz",
		RoadmapItemID: "roadmap-999",
	}

	res, err := writer.EnsureProviderIdentity(context.Background(), EnsureProviderIdentityInput{
		WorkspacePath: tmpDir,
		Identity:      ident,
	})
	if err != nil {
		t.Fatalf("EnsureProviderIdentity failed: %v", err)
	}

	if len(res.InjectedPaths) != 0 {
		t.Errorf("expected no injected paths, got %v", res.InjectedPaths)
	}
}

func TestEnsureProviderIdentityFailsWhenNoMilestoneChanged(t *testing.T) {
	tmpDir := setupTestGitRepo(t)
	defer os.RemoveAll(tmpDir)

	// No milestone files are created, or they are clean (not changed/untracked)
	writer := NewLocalIdentityWriter()
	ident := roadmapsync.Identity{
		Provider:   "plane",
		WorkItemID: "work-123",
	}

	_, err := writer.EnsureProviderIdentity(context.Background(), EnsureProviderIdentityInput{
		WorkspacePath: tmpDir,
		Identity:      ident,
	})
	if !errors.Is(err, ErrNoMilestonesChanged) {
		t.Errorf("expected ErrNoMilestonesChanged, got %v", err)
	}
}

func TestEnsureProviderIdentityRejectsMissingWorkItemID(t *testing.T) {
	tmpDir := setupTestGitRepo(t)
	defer os.RemoveAll(tmpDir)

	writer := NewLocalIdentityWriter()
	ident := roadmapsync.Identity{
		Provider: "plane",
		// WorkItemID is missing
	}

	_, err := writer.EnsureProviderIdentity(context.Background(), EnsureProviderIdentityInput{
		WorkspacePath: tmpDir,
		Identity:      ident,
	})
	if !errors.Is(err, roadmapsync.ErrInvalidIdentity) {
		t.Errorf("expected ErrInvalidIdentity, got %v", err)
	}
}

func TestEnsureProviderIdentityRejectsMismatchedExistingBlock(t *testing.T) {
	tmpDir := setupTestGitRepo(t)
	defer os.RemoveAll(tmpDir)

	milestoneDir := filepath.Join(tmpDir, "agent-roadmap", "phase", "test-phase", "milestones")
	if err := os.MkdirAll(milestoneDir, 0755); err != nil {
		t.Fatalf("failed to create milestone dir: %v", err)
	}

	milestonePath := filepath.Join(milestoneDir, "test-milestone.md")
	content := `# Test Milestone
Some description here.

## Provider identity
- provider: plane
- tenant: tenant-abc
- project: project-xyz
- work item id: work-123
- milestone id: roadmap-999
`
	if err := os.WriteFile(milestonePath, []byte(content), 0644); err != nil {
		t.Fatalf("failed to write milestone file: %v", err)
	}

	writer := NewLocalIdentityWriter()

	// Task identity has mismatched work item id
	ident := roadmapsync.Identity{
		Provider:      "plane",
		WorkItemID:    "work-456", // mismatch
		Tenant:        "tenant-abc",
		Project:       "project-xyz",
		RoadmapItemID: "roadmap-999",
	}

	_, err := writer.EnsureProviderIdentity(context.Background(), EnsureProviderIdentityInput{
		WorkspacePath: tmpDir,
		Identity:      ident,
	})
	if !errors.Is(err, ErrMismatchedProviderIdentity) {
		t.Errorf("expected ErrMismatchedProviderIdentity, got %v", err)
	}
}
