package repo

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/gitlink-org/gitlink-cli/internal/client"
	"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)

func runBatchDeleteShortcut(t *testing.T, server *httptest.Server, owner string, args map[string]string) error {
	t.Helper()
	s := findShortcut(t, "batch-delete")
	ctx := &common.RuntimeContext{
		Client: &client.Client{HTTP: server.Client(), BaseURL: server.URL},
		Owner:  owner,
		Format: "json",
		Args:   args,
	}
	return s.Run(ctx)
}

func TestBatchDelete_DryRun(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		t.Fatalf("no API calls expected in dry-run mode, got %s %s", r.Method, r.URL.Path)
	}))
	defer server.Close()

	err := runBatchDeleteShortcut(t, server, "testuser", map[string]string{
		"names":   "repo1,repo2",
		"dry-run": "true",
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestBatchDelete_FromNames(t *testing.T) {
	var deletedPaths []string
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "DELETE" {
			t.Fatalf("expected DELETE, got %s", r.Method)
		}
		deletedPaths = append(deletedPaths, r.URL.Path)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"ok":true}`))
	}))
	defer server.Close()

	err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{
		"names": "test-batch-1,test-batch-2,test-batch-3",
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(deletedPaths) != 3 {
		t.Fatalf("expected 3 DELETE calls, got %d", len(deletedPaths))
	}
	expected := []string{"/zzx-coder/test-batch-1.json", "/zzx-coder/test-batch-2.json", "/zzx-coder/test-batch-3.json"}
	for i, p := range deletedPaths {
		if p != expected[i] {
			t.Fatalf("path[%d]: got %q, want %q", i, p, expected[i])
		}
	}
}

func TestBatchDelete_NoNames(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
	}))
	defer server.Close()

	err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{})
	if err == nil {
		t.Fatal("expected error, got nil")
	}
	if !strings.Contains(err.Error(), "no repository names provided") {
		t.Fatalf("error should mention no names, got: %v", err)
	}
}

func TestBatchDelete_NoOwner(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
	}))
	defer server.Close()

	err := runBatchDeleteShortcut(t, server, "", map[string]string{
		"names": "repo1,repo2",
	})
	if err == nil {
		t.Fatal("expected error, got nil")
	}
	if !strings.Contains(err.Error(), "--owner is required") {
		t.Fatalf("error should mention --owner required, got: %v", err)
	}
}

func TestBatchDelete_DryRunNoNamesErrors(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path)
	}))
	defer server.Close()

	err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{"dry-run": "true"})
	if err == nil {
		t.Fatal("expected error for no names even in dry-run, got nil")
	}
}

func TestBatchDelete_FromCSV(t *testing.T) {
	csvPath := writeTempCSV(t, "name\ncsv-repo1\ncsv-repo2\n")

	var deletedPaths []string
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "DELETE" {
			t.Fatalf("expected DELETE, got %s", r.Method)
		}
		deletedPaths = append(deletedPaths, r.URL.Path)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"ok":true}`))
	}))
	defer server.Close()

	err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{"from": csvPath})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(deletedPaths) != 2 {
		t.Fatalf("expected 2 DELETE calls, got %d", len(deletedPaths))
	}
	if deletedPaths[0] != "/zzx-coder/csv-repo1.json" {
		t.Fatalf("first path: got %q", deletedPaths[0])
	}
	if deletedPaths[1] != "/zzx-coder/csv-repo2.json" {
		t.Fatalf("second path: got %q", deletedPaths[1])
	}
}

func TestBatchDelete_CSVAndNamesCombined(t *testing.T) {
	csvPath := writeTempCSV(t, "name\ncsv-repo\n")

	var deletedPaths []string
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		deletedPaths = append(deletedPaths, r.URL.Path)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"ok":true}`))
	}))
	defer server.Close()

	err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{
		"names": "inline-repo",
		"from":  csvPath,
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(deletedPaths) != 2 {
		t.Fatalf("expected 2 deletes (inline + csv), got %d", len(deletedPaths))
	}
	if deletedPaths[0] != "/zzx-coder/inline-repo.json" {
		t.Fatalf("first: got %q", deletedPaths[0])
	}
	if deletedPaths[1] != "/zzx-coder/csv-repo.json" {
		t.Fatalf("second: got %q", deletedPaths[1])
	}
}

func TestBatchDelete_PartialFailure(t *testing.T) {
	callCount := 0
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		callCount++
		if callCount == 2 {
			w.WriteHeader(http.StatusNotFound)
			w.Write([]byte(`{"message":"repo not found"}`))
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"ok":true}`))
	}))
	defer server.Close()

	err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{
		"names": "ok1,fail1,ok2",
	})
	if err == nil {
		t.Fatal("expected error from partial failure, got nil")
	}
	if !strings.Contains(err.Error(), "failed to delete") {
		t.Fatalf("error should mention failed count, got: %v", err)
	}
}

func TestBatchDelete_TrimsWhitespace(t *testing.T) {
	var deletedPaths []string
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		deletedPaths = append(deletedPaths, r.URL.Path)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"ok":true}`))
	}))
	defer server.Close()

	err := runBatchDeleteShortcut(t, server, "zzx-coder", map[string]string{
		"names": "  repo-a ,  repo-b  ,",
	})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(deletedPaths) != 2 {
		t.Fatalf("expected 2 deletes after trimming, got %d", len(deletedPaths))
	}
	if deletedPaths[0] != "/zzx-coder/repo-a.json" {
		t.Fatalf("first: got %q", deletedPaths[0])
	}
	if deletedPaths[1] != "/zzx-coder/repo-b.json" {
		t.Fatalf("second: got %q", deletedPaths[1])
	}
}
