package handlers

import (
	"encoding/json"
	"net/http"
	"testing"

	"github.com/gin-gonic/gin"

	"chat-switchboard/config"
	"chat-switchboard/crypto"
	"chat-switchboard/database"
	"chat-switchboard/middleware"
	"chat-switchboard/store"
	postgres "chat-switchboard/store/postgres"
	sqlite "chat-switchboard/store/sqlite"
)

// ── Git Credentials Test Harness ──────────

type gitCredHarness struct {
	*testHarness
	userToken  string
	userID     string
	user2Token string
	user2ID    string
}

func setupGitCredHarness(t *testing.T) *gitCredHarness {
	t.Helper()
	database.RequireTestDB(t)
	database.TruncateAll(t)

	cfg := &config.Config{
		JWTSecret: testJWTSecret,
		BasePath:  "",
	}

	var stores store.Stores
	if database.IsSQLite() {
		stores = sqlite.NewStores(database.TestDB)
	} else {
		stores = postgres.NewStores(database.TestDB)
	}
	userCache := middleware.NewUserStatusCache()

	// Create a test vault with a static env key (32 bytes for AES-256)
	envKey := []byte("test-encryption-key-32-bytes!!")
	for len(envKey) < 32 {
		envKey = append(envKey, '0')
	}
	envKey = envKey[:32]
	vault := crypto.NewKeyResolver(envKey, nil)

	r := gin.New()
	api := r.Group("/api/v1")
	protected := api.Group("")
	protected.Use(middleware.Auth(cfg, stores.Users, userCache))

	gitCredH := NewGitCredentialHandler(stores, vault)
	protected.POST("/git-credentials", gitCredH.Create)
	protected.POST("/git-credentials/generate", gitCredH.Generate)
	protected.GET("/git-credentials", gitCredH.List)
	protected.GET("/git-credentials/:id/public-key", gitCredH.GetPublicKey)
	protected.DELETE("/git-credentials/:id", gitCredH.Delete)

	// Seed users
	userID := database.SeedTestUser(t, "gituser", "gituser@test.com")
	database.TestDB.Exec(dialectSQL("UPDATE users SET is_active = true WHERE id = $1"), userID)
	userToken := makeToken(userID, "gituser@test.com", "user")

	user2ID := database.SeedTestUser(t, "gituser2", "gituser2@test.com")
	database.TestDB.Exec(dialectSQL("UPDATE users SET is_active = true WHERE id = $1"), user2ID)
	user2Token := makeToken(user2ID, "gituser2@test.com", "user")

	return &gitCredHarness{
		testHarness: &testHarness{router: r, t: t},
		userToken:   userToken,
		userID:      userID,
		user2Token:  user2Token,
		user2ID:     user2ID,
	}
}

// ── GET /git-credentials — empty state ───

func TestGitCreds_List_Empty(t *testing.T) {
	h := setupGitCredHarness(t)

	resp := h.request("GET", "/api/v1/git-credentials", h.userToken, nil)
	if resp.Code != http.StatusOK {
		t.Fatalf("got %d, body: %s", resp.Code, resp.Body.String())
	}

	var body map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&body)
	data, ok := body["data"]
	if !ok {
		t.Fatal("response must have 'data' key")
	}
	arr := data.([]interface{})
	if len(arr) != 0 {
		t.Fatalf("expected empty array, got %d items", len(arr))
	}
}

// ── POST /git-credentials/generate ───────

func TestGitCreds_Generate(t *testing.T) {
	h := setupGitCredHarness(t)

	resp := h.request("POST", "/api/v1/git-credentials/generate", h.userToken, map[string]interface{}{
		"name": "Test Key",
	})
	if resp.Code != http.StatusCreated {
		t.Fatalf("generate: got %d, body: %s", resp.Code, resp.Body.String())
	}

	var cred map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&cred)

	// Must have public_key and fingerprint
	pubKey, _ := cred["public_key"].(string)
	fp, _ := cred["fingerprint"].(string)
	if pubKey == "" {
		t.Error("public_key should be non-empty")
	}
	if fp == "" {
		t.Error("fingerprint should be non-empty")
	}
	if cred["auth_type"] != "ssh_key" {
		t.Errorf("auth_type: got %v, want ssh_key", cred["auth_type"])
	}
	if cred["name"] != "Test Key" {
		t.Errorf("name: got %v", cred["name"])
	}

	// Must NOT have encrypted data
	if _, has := cred["encrypted_data"]; has {
		t.Error("encrypted_data must not appear in response")
	}
	if _, has := cred["nonce"]; has {
		t.Error("nonce must not appear in response")
	}
}

// ── GET /git-credentials/:id/public-key ──

func TestGitCreds_GetPublicKey(t *testing.T) {
	h := setupGitCredHarness(t)

	// Generate a key first
	resp := h.request("POST", "/api/v1/git-credentials/generate", h.userToken, map[string]interface{}{
		"name": "PK Test",
	})
	var cred map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&cred)
	id := cred["id"].(string)
	origPK := cred["public_key"].(string)

	// Retrieve public key
	resp = h.request("GET", "/api/v1/git-credentials/"+id+"/public-key", h.userToken, nil)
	if resp.Code != http.StatusOK {
		t.Fatalf("get public-key: got %d, body: %s", resp.Code, resp.Body.String())
	}

	var pkBody map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&pkBody)
	if pkBody["public_key"] != origPK {
		t.Errorf("public key mismatch")
	}
	if pkBody["fingerprint"] == nil || pkBody["fingerprint"] == "" {
		t.Error("fingerprint should be present")
	}
}

// ── List after generate ──────────────────

func TestGitCreds_ListAfterGenerate(t *testing.T) {
	h := setupGitCredHarness(t)

	h.request("POST", "/api/v1/git-credentials/generate", h.userToken, map[string]interface{}{
		"name": "Key A",
	})
	h.request("POST", "/api/v1/git-credentials/generate", h.userToken, map[string]interface{}{
		"name": "Key B",
	})

	resp := h.request("GET", "/api/v1/git-credentials", h.userToken, nil)
	var body map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&body)
	arr := body["data"].([]interface{})
	if len(arr) != 2 {
		t.Fatalf("expected 2 keys, got %d", len(arr))
	}

	// Verify summaries have public_key + fingerprint
	for _, raw := range arr {
		item := raw.(map[string]interface{})
		if item["public_key"] == nil || item["public_key"] == "" {
			t.Error("list item should have public_key")
		}
		if item["fingerprint"] == nil || item["fingerprint"] == "" {
			t.Error("list item should have fingerprint")
		}
	}
}

// ── Delete ───────────────────────────────

func TestGitCreds_Delete(t *testing.T) {
	h := setupGitCredHarness(t)

	resp := h.request("POST", "/api/v1/git-credentials/generate", h.userToken, map[string]interface{}{
		"name": "Delete Me",
	})
	var cred map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&cred)
	id := cred["id"].(string)

	// Delete
	resp = h.request("DELETE", "/api/v1/git-credentials/"+id, h.userToken, nil)
	if resp.Code != http.StatusOK {
		t.Fatalf("delete: got %d", resp.Code)
	}

	// Verify gone
	resp = h.request("GET", "/api/v1/git-credentials", h.userToken, nil)
	var body map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&body)
	arr := body["data"].([]interface{})
	if len(arr) != 0 {
		t.Fatalf("expected 0 keys after delete, got %d", len(arr))
	}
}

// ── User isolation ───────────────────────

func TestGitCreds_UserIsolation(t *testing.T) {
	h := setupGitCredHarness(t)

	// User 1 generates a key
	resp := h.request("POST", "/api/v1/git-credentials/generate", h.userToken, map[string]interface{}{
		"name": "User1 Key",
	})
	var cred map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&cred)
	id := cred["id"].(string)

	// User 2 should see empty list
	resp = h.request("GET", "/api/v1/git-credentials", h.user2Token, nil)
	var body map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&body)
	arr := body["data"].([]interface{})
	if len(arr) != 0 {
		t.Fatalf("user2 should see 0 keys, got %d", len(arr))
	}

	// User 2 should not be able to get user1's public key
	resp = h.request("GET", "/api/v1/git-credentials/"+id+"/public-key", h.user2Token, nil)
	if resp.Code != http.StatusNotFound {
		t.Fatalf("cross-user public-key: want 404, got %d", resp.Code)
	}

	// User 2 should not be able to delete user1's key
	resp = h.request("DELETE", "/api/v1/git-credentials/"+id, h.user2Token, nil)
	if resp.Code != http.StatusNotFound {
		t.Fatalf("cross-user delete: want 404, got %d", resp.Code)
	}
}

// ── Validation ───────────────────────────

func TestGitCreds_Generate_MissingName(t *testing.T) {
	h := setupGitCredHarness(t)

	resp := h.request("POST", "/api/v1/git-credentials/generate", h.userToken, map[string]interface{}{})
	if resp.Code != http.StatusBadRequest {
		t.Fatalf("missing name: want 400, got %d", resp.Code)
	}
}
