package woodpecker

import (
	"context"
	"log/slog"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
	"time"

	"github.com/orchard9/rdev/internal/domain"
)

func TestNewClient_Validation(t *testing.T) {
	tests := []struct {
		name    string
		url     string
		token   string
		wantErr string
	}{
		{
			name:    "empty URL",
			url:     "",
			token:   "test-token",
			wantErr: "woodpecker URL is required",
		},
		{
			name:    "empty token",
			url:     "https://ci.example.com",
			token:   "",
			wantErr: "woodpecker token is required",
		},
		{
			name:    "valid inputs",
			url:     "https://ci.example.com",
			token:   "test-token",
			wantErr: "",
		},
		{
			name:    "URL with trailing slash",
			url:     "https://ci.example.com/",
			token:   "test-token",
			wantErr: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			client, err := NewClient(tt.url, tt.token)
			if tt.wantErr != "" {
				if err == nil {
					t.Errorf("expected error containing %q, got nil", tt.wantErr)
					return
				}
				if err.Error() != tt.wantErr {
					t.Errorf("expected error %q, got %q", tt.wantErr, err.Error())
				}
				return
			}
			if err != nil {
				t.Errorf("unexpected error: %v", err)
				return
			}
			if client == nil {
				t.Error("expected non-nil client")
			}
		})
	}
}

func TestNewClient_WithLogger(t *testing.T) {
	logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
	client, err := NewClient("https://ci.example.com", "test-token", WithLogger(logger))
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if client.logger != logger {
		t.Error("expected custom logger to be set")
	}
}

func TestNewClient_WithNilLogger(t *testing.T) {
	client, err := NewClient("https://ci.example.com", "test-token", WithLogger(nil))
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	// Should use default logger when nil is passed
	if client.logger == nil {
		t.Error("expected non-nil logger")
	}
}

func TestTokenTransport_ClonesRequest(t *testing.T) {
	// Create a test server that records headers
	var receivedAuth string
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		receivedAuth = r.Header.Get("Authorization")
		w.WriteHeader(http.StatusOK)
	}))
	defer server.Close()

	transport := &tokenTransport{
		token: "test-token",
		base:  http.DefaultTransport,
	}

	// Create original request
	req, _ := http.NewRequest("GET", server.URL, nil)
	originalAuth := req.Header.Get("Authorization")

	// Execute request through transport
	resp, err := transport.RoundTrip(req)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	defer func() { _ = resp.Body.Close() }()

	// Verify original request was not mutated
	if req.Header.Get("Authorization") != originalAuth {
		t.Error("original request was mutated")
	}

	// Verify server received the token
	if receivedAuth != "Bearer test-token" {
		t.Errorf("expected server to receive 'Bearer test-token', got %q", receivedAuth)
	}
}

func TestContextCancellation(t *testing.T) {
	client, err := NewClient("https://ci.example.com", "test-token")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	// Create a cancelled context
	ctx, cancel := context.WithCancel(context.Background())
	cancel()

	// Test ActivateRepo
	_, err = client.ActivateRepo(ctx, "gitea", "owner", "repo")
	if err != context.Canceled {
		t.Errorf("ActivateRepo: expected context.Canceled, got %v", err)
	}

	// Test DeactivateRepo
	err = client.DeactivateRepo(ctx, "owner", "repo")
	if err != context.Canceled {
		t.Errorf("DeactivateRepo: expected context.Canceled, got %v", err)
	}

	// Test GetRepo
	_, err = client.GetRepo(ctx, "owner", "repo")
	if err != context.Canceled {
		t.Errorf("GetRepo: expected context.Canceled, got %v", err)
	}

	// Test ListRepos
	_, err = client.ListRepos(ctx)
	if err != context.Canceled {
		t.Errorf("ListRepos: expected context.Canceled, got %v", err)
	}

	// Test AddSecret
	err = client.AddSecret(ctx, "owner", "repo", domain.CISecret{Name: "test"})
	if err != context.Canceled {
		t.Errorf("AddSecret: expected context.Canceled, got %v", err)
	}

	// Test DeleteSecret
	err = client.DeleteSecret(ctx, "owner", "repo", "secret")
	if err != context.Canceled {
		t.Errorf("DeleteSecret: expected context.Canceled, got %v", err)
	}
}

func TestContextDeadline(t *testing.T) {
	client, err := NewClient("https://ci.example.com", "test-token")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	// Create a context with an already expired deadline
	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-1*time.Second))
	defer cancel()

	// Test ActivateRepo with expired context
	_, err = client.ActivateRepo(ctx, "gitea", "owner", "repo")
	if err != context.DeadlineExceeded {
		t.Errorf("expected context.DeadlineExceeded, got %v", err)
	}
}

func TestRepoFromWoodpecker(t *testing.T) {
	tests := []struct {
		name          string
		forgeRemoteID string
		wantForgeID   int64
	}{
		{
			name:          "valid numeric ID",
			forgeRemoteID: "12345",
			wantForgeID:   12345,
		},
		{
			name:          "empty ID",
			forgeRemoteID: "",
			wantForgeID:   0,
		},
		{
			name:          "non-numeric ID",
			forgeRemoteID: "abc-123",
			wantForgeID:   0,
		},
		{
			name:          "very large ID",
			forgeRemoteID: "9223372036854775807",
			wantForgeID:   9223372036854775807,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Note: We can't test repoFromWoodpecker directly since it's unexported
			// and takes a woodpecker.Repo which is from the SDK.
			// This test documents expected behavior for ForgeRemoteID parsing.
			// In a real scenario, we'd use integration tests or mock the SDK.
		})
	}
}
