package output

import (
	"bytes"
	"encoding/json"
	"strings"
	"testing"
	"time"

	"github.com/salvacybersec/keyhunter/pkg/engine"
)

func TestJSONFormatter_EmptyFindings(t *testing.T) {
	var buf bytes.Buffer
	if err := (JSONFormatter{}).Format(nil, &buf, Options{}); err != nil {
		t.Fatalf("Format returned error: %v", err)
	}
	got := buf.String()
	if got != "[]\n" {
		t.Fatalf("empty findings: want %q, got %q", "[]\n", got)
	}
}

func TestJSONFormatter_RegisteredUnderJSON(t *testing.T) {
	f, err := Get("json")
	if err != nil {
		t.Fatalf("Get(\"json\") error: %v", err)
	}
	if _, ok := f.(JSONFormatter); !ok {
		t.Fatalf("Get(\"json\") returned %T, want JSONFormatter", f)
	}
}

func sampleFinding() engine.Finding {
	return engine.Finding{
		ProviderName: "openai",
		KeyValue:     "sk-proj-abcdef1234567890XYZW",
		KeyMasked:    "sk-proj-...XYZW",
		Confidence:   "high",
		Source:       "src/config.go",
		SourceType:   "file",
		LineNumber:   42,
		Offset:       1024,
		DetectedAt:   time.Date(2026, 4, 5, 12, 30, 0, 0, time.UTC),
	}
}

func TestJSONFormatter_MaskedByDefault(t *testing.T) {
	f := sampleFinding()
	var buf bytes.Buffer
	if err := (JSONFormatter{}).Format([]engine.Finding{f}, &buf, Options{}); err != nil {
		t.Fatalf("Format returned error: %v", err)
	}
	var out []map[string]any
	if err := json.Unmarshal(buf.Bytes(), &out); err != nil {
		t.Fatalf("invalid JSON: %v\n%s", err, buf.String())
	}
	if len(out) != 1 {
		t.Fatalf("want 1 element, got %d", len(out))
	}
	if out[0]["key"] != f.KeyMasked {
		t.Errorf("key: want %q (masked), got %v", f.KeyMasked, out[0]["key"])
	}
	if out[0]["key_masked"] != f.KeyMasked {
		t.Errorf("key_masked: want %q, got %v", f.KeyMasked, out[0]["key_masked"])
	}
	if out[0]["provider"] != "openai" {
		t.Errorf("provider: got %v", out[0]["provider"])
	}
	if out[0]["confidence"] != "high" {
		t.Errorf("confidence: got %v", out[0]["confidence"])
	}
	if out[0]["line"].(float64) != 42 {
		t.Errorf("line: got %v", out[0]["line"])
	}
	if out[0]["detected_at"] != "2026-04-05T12:30:00Z" {
		t.Errorf("detected_at: got %v", out[0]["detected_at"])
	}
	if out[0]["verified"] != false {
		t.Errorf("verified: got %v", out[0]["verified"])
	}
	// Unverified: omitempty fields should not appear.
	if _, ok := out[0]["verify_status"]; ok {
		t.Errorf("verify_status should be omitted when empty")
	}
}

func TestJSONFormatter_UnmaskRevealsKey(t *testing.T) {
	f := sampleFinding()
	var buf bytes.Buffer
	if err := (JSONFormatter{}).Format([]engine.Finding{f}, &buf, Options{Unmask: true}); err != nil {
		t.Fatalf("Format returned error: %v", err)
	}
	var out []map[string]any
	if err := json.Unmarshal(buf.Bytes(), &out); err != nil {
		t.Fatalf("invalid JSON: %v", err)
	}
	if out[0]["key"] != f.KeyValue {
		t.Errorf("key: want %q (unmasked), got %v", f.KeyValue, out[0]["key"])
	}
	if out[0]["key_masked"] != f.KeyMasked {
		t.Errorf("key_masked should remain masked, got %v", out[0]["key_masked"])
	}
}

func TestJSONFormatter_VerifyFieldsPresent(t *testing.T) {
	f := sampleFinding()
	f.Verified = true
	f.VerifyStatus = "live"
	f.VerifyHTTPCode = 200
	f.VerifyMetadata = map[string]string{"org": "acme"}

	var buf bytes.Buffer
	if err := (JSONFormatter{}).Format([]engine.Finding{f}, &buf, Options{}); err != nil {
		t.Fatalf("Format returned error: %v", err)
	}
	var out []map[string]any
	if err := json.Unmarshal(buf.Bytes(), &out); err != nil {
		t.Fatalf("invalid JSON: %v", err)
	}
	if out[0]["verified"] != true {
		t.Errorf("verified: got %v", out[0]["verified"])
	}
	if out[0]["verify_status"] != "live" {
		t.Errorf("verify_status: got %v", out[0]["verify_status"])
	}
	if out[0]["verify_http_code"].(float64) != 200 {
		t.Errorf("verify_http_code: got %v", out[0]["verify_http_code"])
	}
	meta, ok := out[0]["verify_metadata"].(map[string]any)
	if !ok {
		t.Fatalf("verify_metadata missing or wrong type: %v", out[0]["verify_metadata"])
	}
	if meta["org"] != "acme" {
		t.Errorf("verify_metadata.org: got %v", meta["org"])
	}
}

func TestJSONFormatter_Indented(t *testing.T) {
	f := sampleFinding()
	var buf bytes.Buffer
	if err := (JSONFormatter{}).Format([]engine.Finding{f}, &buf, Options{}); err != nil {
		t.Fatalf("Format returned error: %v", err)
	}
	// Expect 2-space indent: array items at 2 spaces, nested fields at 4 spaces.
	s := buf.String()
	if !strings.Contains(s, "\n  {") {
		t.Errorf("array item not indented with 2 spaces:\n%s", s)
	}
	if !strings.Contains(s, "\n    \"provider\"") {
		t.Errorf("nested field not indented with 4 spaces:\n%s", s)
	}
}
