package app

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestPublishSingleTicketReadReplaySendsOnlyIssueAndReadRunID(t *testing.T) {
	t.Parallel()
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		require.Equal(t, http.MethodPost, req.Method)
		require.Equal(t, "/v1/tickets/publish-read", req.URL.Path)
		var body map[string]string
		require.NoError(t, json.NewDecoder(req.Body).Decode(&body))
		require.Equal(t, map[string]string{
			"issueKey":  "TCID-13132",
			"readRunId": "read-run-13132",
		}, body)
		writeJSON(w, http.StatusOK, map[string]any{"runId": "read-run-13132"})
	}))
	defer server.Close()

	runner := &CodexRunner{config: Config{AnalysisBaseURL: server.URL}}
	payload, status, err := runner.publishSingleTicketReadReplay(
		context.Background(), "TCID-13132", "read-run-13132",
	)
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, status)
	require.Contains(t, string(payload), "read-run-13132")
}

func TestMergedPullRequestSHARequiresMergedState(t *testing.T) {
	t.Parallel()
	sha, err := mergedPullRequestSHA([]byte(`{"state":"MERGED","mergeCommit":{"oid":"0123456789abcdef"}}`))
	require.NoError(t, err)
	require.Equal(t, "0123456789abcdef", sha)

	_, err = mergedPullRequestSHA([]byte(`{"state":"OPEN","mergeCommit":null}`))
	require.ErrorContains(t, err, "did not reach merged")
}

func TestPullRequestChecksArgsUseSupportedCLIFlags(t *testing.T) {
	t.Parallel()
	args := pullRequestChecksArgs("https://github.com/tidbcloud/aisupport/pull/1106", "tidbcloud/aisupport")
	require.Equal(t, []string{
		"pr", "checks", "https://github.com/tidbcloud/aisupport/pull/1106",
		"--repo", "tidbcloud/aisupport", "--watch",
	}, args)
	require.NotContains(t, args, "--fail-fast")
}

func TestDeploymentWorkflowRunsArgsUseSupportedCLIFlags(t *testing.T) {
	t.Parallel()
	args := deploymentWorkflowRunsArgs("tidbcloud/aisupport", "deploy-auto-oncall-services-agent9-staging.yml")
	require.Equal(t, []string{
		"run", "list", "--repo", "tidbcloud/aisupport",
		"--workflow", "deploy-auto-oncall-services-agent9-staging.yml",
		"--limit", "20", "--json", "databaseId,headSha,status,conclusion",
	}, args)
	require.NotContains(t, args, "--event")
}

func TestWaitForPullRequestChecksAllowsWorkflowRegistration(t *testing.T) {
	t.Parallel()
	attempts := 0
	err := waitForPullRequestChecks(context.Background(), time.Second, time.Millisecond, func() error {
		attempts++
		if attempts == 1 {
			return errors.New("no checks reported on the 'auto-oncall/ticket' branch")
		}
		return nil
	})
	require.NoError(t, err)
	require.Equal(t, 2, attempts)
}

func TestWaitForPullRequestChecksPreservesRealFailure(t *testing.T) {
	t.Parallel()
	attempts := 0
	expected := errors.New("build-test failed")
	err := waitForPullRequestChecks(context.Background(), time.Second, time.Millisecond, func() error {
		attempts++
		return expected
	})
	require.ErrorIs(t, err, expected)
	require.Equal(t, 1, attempts)
}

func TestWaitForPullRequestChecksFailsClosedWhenRegistrationNeverStarts(t *testing.T) {
	t.Parallel()
	attempts := 0
	err := waitForPullRequestChecks(context.Background(), 0, time.Second, func() error {
		attempts++
		return errors.New("no checks reported on the 'auto-oncall/ticket' branch")
	})
	require.ErrorContains(t, err, "checks did not register")
	require.Equal(t, 1, attempts)
}

func TestValidateSingleTicketReadPublicationRequiresSameRunJiraAndEvaluation(t *testing.T) {
	t.Parallel()
	payload := []byte(`{
  "runId":"run-after",
  "issueKey":"APID-29232",
  "mode":"publish_read",
  "evaluation":{"status":"evaluated","verdict":"equivalent"},
  "publication":{"runDbId":71,"database":"written","jira":"updated","evaluation":"written","errors":[]},
  "jiraInternalComment":{"status":"updated","commentId":"127741","commentUrl":"https://tidb.atlassian.net/comment/127741"}
}`)
	verified, err := validateSingleTicketReadPublication(payload, 200, "run-after", "APID-29232")
	require.NoError(t, err)
	require.Equal(t, "run-after", verified.RunID)
	require.Equal(t, int64(71), verified.RunDBID)
	require.Equal(t, "127741", verified.CommentID)

	humanBetter := []byte(`{
  "runId":"run-after","issueKey":"APID-29232","evaluation":{"status":"evaluated","verdict":"human_better"},
  "mode":"publish_read",
  "publication":{"runDbId":71,"database":"written","jira":"updated","evaluation":"written","errors":[]},
  "jiraInternalComment":{"status":"updated","commentId":"127741","commentUrl":"https://tidb.atlassian.net/comment/127741"}
}`)
	_, err = validateSingleTicketReadPublication(humanBetter, 200, "run-after", "APID-29232")
	require.ErrorContains(t, err, "did not complete")

	_, err = validateSingleTicketReadPublication(payload, 200, "different-read-run", "APID-29232")
	require.ErrorContains(t, err, "did not complete")

	_, err = validateSingleTicketReadPublication(payload, 200, "run-after", "OTHER-1")
	require.ErrorContains(t, err, "did not complete")
}

func TestReadReplayRunIDRequiresReadIdentity(t *testing.T) {
	t.Parallel()
	runID, err := readReplayRunID([]byte(`{"runId":"read-1","issueKey":"TCID-13132","mode":"read"}`), 200, "TCID-13132")
	require.NoError(t, err)
	require.Equal(t, "read-1", runID)

	_, err = readReplayRunID([]byte(`{"runId":"read-1","issueKey":"OTHER-1","mode":"read"}`), 200, "TCID-13132")
	require.ErrorContains(t, err, "identity mismatch")
}

func TestValidatePostReplayRepairPathsKeepsSameOwner(t *testing.T) {
	t.Parallel()
	original := []string{
		"auto-oncall-services/src/runner.ts",
		"auto-oncall-services/docs/opts/2026-08-26-timeout.md",
	}
	targets := []string{"auto-oncall-services/src/runner.ts"}
	require.NoError(t, validatePostReplayRepairPaths([]string{
		"auto-oncall-services/src/runner.ts",
		"auto-oncall-services/test/runner.test.ts",
	}, original, targets))
	require.ErrorContains(t, validatePostReplayRepairPaths([]string{
		"auto-oncall-services/src/diagnostic-scenarios.ts",
	}, original, targets), "new target")
	require.ErrorContains(t, validatePostReplayRepairPaths([]string{
		"auto-oncall-services/test/runner.test.ts",
	}, original, targets), "tests only")
}
