package canonical

import (
	"testing"

	reposync "github.com/pingcap/agent-git-service/internal/sync"
)

func TestDirectionMappersPreserveIdentityAndClassifyRepresentation(t *testing.T) {
	record := NewRecord(TypePullRequestReview, "review-node", "review:1", map[string]any{"body": "LGTM"})
	object, err := record.Inventory("r1", "native")
	if err != nil {
		t.Fatal(err)
	}
	mapped, err := MapAGSToGitHub([]reposync.InventoryObject{object})
	if err != nil {
		t.Fatal(err)
	}
	if len(mapped) != 1 || mapped[0].Identity != object.Identity || mapped[0].ObjectKey != object.ObjectKey {
		t.Fatalf("mapped identity changed: %#v", mapped)
	}
	if mapped[0].Representation != "attributed" {
		t.Fatalf("representation=%q, want attributed", mapped[0].Representation)
	}
	decoded, err := Decode(mapped[0].Payload)
	if err != nil {
		t.Fatal(err)
	}
	if decoded.SourceProvider != "ags" {
		t.Fatalf("source_provider=%q, want ags", decoded.SourceProvider)
	}
}

func TestGitHubPullRequestUsesMirroredShadowHeadInAGS(t *testing.T) {
	record := NewRecord(TypePullRequest, "PR_node", "pull_requests:17", map[string]any{
		"_number":    17,
		"head_ref":   "feature-from-fork",
		"_head_repo": "someone/fork",
		"_base_repo": "owner/repo",
	})
	object, err := record.Inventory("r1", "native")
	if err != nil {
		t.Fatal(err)
	}
	mapped, err := MapGitHubToAGS([]reposync.InventoryObject{object})
	if err != nil {
		t.Fatal(err)
	}
	decoded, err := Decode(mapped[0].Payload)
	if err != nil {
		t.Fatal(err)
	}
	if got := StringField(decoded.Fields, "head_ref"); got != "ags-sync/pr/17/head" {
		t.Fatalf("head_ref=%q", got)
	}
	if got := StringField(decoded.Fields, "_source_head_ref"); got != "feature-from-fork" {
		t.Fatalf("_source_head_ref=%q", got)
	}
	if got := StringField(decoded.Fields, "_head_repo"); got != "" {
		t.Fatalf("_head_repo=%q, want empty", got)
	}
}

func TestGitHubSameRepositoryPullRequestKeepsWritableHead(t *testing.T) {
	record := NewRecord(TypePullRequest, "PR_node", "pull-request:PR_node", map[string]any{
		"_number": 2, "head_ref": "feature", "_head_repo": "owner/repo", "_base_repo": "owner/repo",
	})
	object, err := record.Inventory("r", "native")
	if err != nil {
		t.Fatal(err)
	}
	mapped, err := MapGitHubToAGS([]reposync.InventoryObject{object})
	if err != nil {
		t.Fatal(err)
	}
	decoded, _ := Decode(mapped[0].Payload)
	if got := StringField(decoded.Fields, "head_ref"); got != "feature" {
		t.Fatalf("head_ref=%q", got)
	}
}

func TestAGSShadowHeadMapsBackToOriginalGitHubHead(t *testing.T) {
	record := NewRecord(TypePullRequest, "17", "pull-request:17", map[string]any{
		"head_ref": "ags-sync/pr/17/head", "_source_head_ref": "feature", "_source_head_repo": "someone/fork",
	})
	object, err := record.Inventory("r", "native")
	if err != nil {
		t.Fatal(err)
	}
	mapped, err := MapAGSToGitHub([]reposync.InventoryObject{object})
	if err != nil {
		t.Fatal(err)
	}
	decoded, _ := Decode(mapped[0].Payload)
	if StringField(decoded.Fields, "head_ref") != "feature" || StringField(decoded.Fields, "_head_repo") != "someone/fork" {
		t.Fatalf("mapped=%#v", decoded.Fields)
	}
}

func TestReviewRequestProjectionIsReadBackComparableOnlyOnGitHub(t *testing.T) {
	record := NewRecord(TypeReviewRequest, "pull:9", "review-request:pull:9", map[string]any{
		"users": []string{"zoe", "alice"}, "teams": []string{"database"},
	})
	object, err := record.Inventory("r1", "native")
	if err != nil {
		t.Fatal(err)
	}
	githubObjects, err := MapAGSToGitHub([]reposync.InventoryObject{object})
	if err != nil {
		t.Fatal(err)
	}
	githubRecord, err := Decode(githubObjects[0].Payload)
	if err != nil {
		t.Fatal(err)
	}
	if githubObjects[0].Representation != "projected" || StringField(githubRecord.Fields, "body") != "Requested reviewers: alice, database, zoe" {
		t.Fatalf("GitHub projection=%#v record=%#v", githubObjects[0], githubRecord)
	}

	agsObjects, err := MapGitHubToAGS(githubObjects)
	if err != nil {
		t.Fatal(err)
	}
	agsRecord, err := Decode(agsObjects[0].Payload)
	if err != nil {
		t.Fatal(err)
	}
	if agsObjects[0].Representation != "native" || StringField(agsRecord.Fields, "body") != "" {
		t.Fatalf("AGS projection=%#v record=%#v", agsObjects[0], agsRecord)
	}
}
