package service

import (
	"errors"
	"testing"

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

func TestParseIssueCommentBatchItems(t *testing.T) {
	number := 42
	items := []db.ImportStagingItem{{
		SourceID:     "101",
		SourceNodeID: "IC_kwDOAAA",
		SourceNumber: &number,
		SourceURL:    "https://github.test/source/repo/issues/42#issuecomment-101",
		PayloadJSON:  `{"id":101,"node_id":"fallback-node","html_url":"fallback-url","body":"hello","user":{"id":7,"login":"octocat","html_url":"https://github.com/octocat"},"created_at":"2026-01-02T03:04:05Z","updated_at":"2026-01-02T03:04:05Z"}`,
	}}

	parsed, err := parseIssueCommentBatchItems(items)
	if err != nil {
		t.Fatalf("parseIssueCommentBatchItems: %v", err)
	}
	if len(parsed) != 1 {
		t.Fatalf("parsed length = %d, want 1", len(parsed))
	}
	got := parsed[0]
	if got.number != number || got.sourceID != "101" || got.sourceNodeID != "IC_kwDOAAA" || got.sourceURL != "https://github.test/source/repo/issues/42#issuecomment-101" {
		t.Fatalf("parsed metadata = %#v", got)
	}
	if got.source.Body != "hello" || got.source.User == nil || got.source.User.Login != "octocat" {
		t.Fatalf("parsed source = %#v", got.source)
	}
}

func TestParseIssueCommentBatchItemsRejectsDuplicateSourceID(t *testing.T) {
	number := 42
	items := []db.ImportStagingItem{
		{SourceID: "101", SourceNumber: &number, PayloadJSON: `{"id":101,"body":"first"}`},
		{SourceID: "101", SourceNumber: &number, PayloadJSON: `{"id":101,"body":"duplicate"}`},
	}

	_, err := parseIssueCommentBatchItems(items)
	if !errors.Is(err, ErrValidation) {
		t.Fatalf("parseIssueCommentBatchItems err = %v, want ErrValidation", err)
	}
}

func TestParseIssueBatchItemsSkipsPullRequests(t *testing.T) {
	items := []db.ImportStagingItem{
		{SourceID: "issue-1", PayloadJSON: `{"id":101,"number":1,"title":"issue"}`},
		{SourceID: "pr-2", PayloadJSON: `{"id":202,"number":2,"title":"pr","pull_request":{"url":"https://github.test/pulls/2"}}`},
	}

	parsed, err := parseIssueBatchItems(items)
	if err != nil {
		t.Fatalf("parseIssueBatchItems: %v", err)
	}
	if len(parsed) != 1 {
		t.Fatalf("parsed length = %d, want 1", len(parsed))
	}
	if parsed[0].number != 1 || parsed[0].sourceID != "issue-1" {
		t.Fatalf("parsed issue = %#v", parsed[0])
	}
}

func TestParsePullRequestBatchItemsRejectsDuplicateNumber(t *testing.T) {
	items := []db.ImportStagingItem{
		{SourceID: "101", PayloadJSON: `{"id":101,"number":7,"title":"first"}`},
		{SourceID: "102", PayloadJSON: `{"id":102,"number":7,"title":"duplicate"}`},
	}

	_, err := parsePullRequestBatchItems(items)
	if !errors.Is(err, ErrValidation) {
		t.Fatalf("parsePullRequestBatchItems err = %v, want ErrValidation", err)
	}
}

func TestParsePullRequestReviewCommentBatchItems(t *testing.T) {
	number := 8
	items := []db.ImportStagingItem{{
		SourceID:     "501",
		SourceNodeID: "PRRC_kwDOAAA",
		SourceNumber: &number,
		SourceURL:    "https://github.test/source/repo/pull/8#discussion_r501",
		PayloadJSON:  `{"id":501,"node_id":"fallback-node","pull_request_review_id":400,"in_reply_to_id":499,"html_url":"fallback-url","body":"nit","user":{"id":7,"login":"octocat"},"created_at":"2026-01-02T03:04:05Z","updated_at":"2026-01-02T03:04:05Z"}`,
	}}

	parsed, err := parsePullRequestReviewCommentBatchItems(items)
	if err != nil {
		t.Fatalf("parsePullRequestReviewCommentBatchItems: %v", err)
	}
	if len(parsed) != 1 {
		t.Fatalf("parsed length = %d, want 1", len(parsed))
	}
	got := parsed[0]
	if got.number != number || got.sourceID != "501" || got.sourceNodeID != "PRRC_kwDOAAA" || got.sourceURL != "https://github.test/source/repo/pull/8#discussion_r501" {
		t.Fatalf("parsed metadata = %#v", got)
	}
	if got.source.PullRequestReviewID != 400 || got.source.InReplyToID != 499 {
		t.Fatalf("parsed source = %#v", got.source)
	}
}

func TestParseReleaseBatchItems(t *testing.T) {
	items := []db.ImportStagingItem{{
		SourceID:     "601",
		SourceNodeID: "REL_kwDOAAA",
		SourceURL:    "https://github.test/source/repo/releases/tag/v1.0.0",
		PayloadJSON:  `{"id":601,"node_id":"fallback-node","tag_name":"v1.0.0","name":"v1","author":{"id":7,"login":"octocat"}}`,
	}}

	parsed, err := parseReleaseBatchItems(items)
	if err != nil {
		t.Fatalf("parseReleaseBatchItems: %v", err)
	}
	if len(parsed) != 1 {
		t.Fatalf("parsed length = %d, want 1", len(parsed))
	}
	got := parsed[0]
	if got.tagName != "v1.0.0" || got.sourceID != "601" || got.sourceNodeID != "REL_kwDOAAA" || got.sourceURL == "" {
		t.Fatalf("parsed release = %#v", got)
	}
}

func TestParseReleaseBatchItemsRejectsDuplicateTag(t *testing.T) {
	items := []db.ImportStagingItem{
		{SourceID: "601", PayloadJSON: `{"id":601,"tag_name":"v1.0.0"}`},
		{SourceID: "602", PayloadJSON: `{"id":602,"tag_name":"v1.0.0"}`},
	}

	_, err := parseReleaseBatchItems(items)
	if !errors.Is(err, ErrValidation) {
		t.Fatalf("parseReleaseBatchItems err = %v, want ErrValidation", err)
	}
}

func TestParseReleaseAssetBatchItemsRejectsDuplicateSourceID(t *testing.T) {
	items := []db.ImportStagingItem{
		{SourceID: "701", PayloadJSON: `{"id":701,"release_tag_name":"v1.0.0","name":"asset.txt","content_size":1,"content_sha256":"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}`},
		{SourceID: "701", PayloadJSON: `{"id":701,"release_tag_name":"v1.0.0","name":"asset-copy.txt","content_size":1,"content_sha256":"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}`},
	}

	_, err := parseReleaseAssetBatchItems(items)
	if !errors.Is(err, ErrValidation) {
		t.Fatalf("parseReleaseAssetBatchItems err = %v, want ErrValidation", err)
	}
}

func TestParseReleaseAssetBatchItemsRejectsLegacyBase64(t *testing.T) {
	_, err := parseReleaseAssetBatchItems([]db.ImportStagingItem{{
		SourceID:    "701",
		PayloadJSON: `{"id":701,"name":"asset.txt","content_base64":"Ynl0ZXM="}`,
	}})
	if !errors.Is(err, ErrValidation) {
		t.Fatalf("parseReleaseAssetBatchItems err = %v, want ErrValidation", err)
	}
}

func TestParseBranchProtectionBatchItems(t *testing.T) {
	items := []db.ImportStagingItem{{
		SourceID:    "main",
		SourceURL:   "https://github.test/source/repo/branches/main/protection",
		PayloadJSON: `{"branch_name":"main","protection":{"enforce_admins":{"enabled":true}}}`,
	}}

	parsed, err := parseBranchProtectionBatchItems(items)
	if err != nil {
		t.Fatalf("parseBranchProtectionBatchItems: %v", err)
	}
	if len(parsed) != 1 {
		t.Fatalf("parsed length = %d, want 1", len(parsed))
	}
	if parsed[0].branchName != "main" || parsed[0].sourceURL == "" || !protectionEnabled(parsed[0].protection["enforce_admins"]) {
		t.Fatalf("parsed branch protection = %#v", parsed[0])
	}
}

func TestParseWorkflowBatchItemsRejectsDuplicatePath(t *testing.T) {
	items := []db.ImportStagingItem{
		{SourceID: "801", PayloadJSON: `{"id":801,"path":".github/workflows/ci.yml"}`},
		{SourceID: "802", PayloadJSON: `{"id":802,"path":".github/workflows/ci.yml"}`},
	}

	_, err := parseWorkflowBatchItems(items)
	if !errors.Is(err, ErrValidation) {
		t.Fatalf("parseWorkflowBatchItems err = %v, want ErrValidation", err)
	}
}
