package server

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	agsconfig "github.com/pingcap/agent-git-service/config"
	agsserver "github.com/pingcap/agent-git-service/server"
	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/config"
	"github.com/tidbcloud/AgentTeam/internal/service/automation"
)

func TestAutomationPushEventAPIStartsMatchingRuns(t *testing.T) {
	after := "abababababababababababababababababababab"
	automationYAML := "name: Push build\non:\n  push:\n    branches: [main]\n    paths: [cmd/**]\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo build\n"
	var listRef string
	var contentRef string
	var sourceAuth string
	var listRequests int
	var contentRequests int
	srv, err := New(config.ResourceRuntimeConfig{
		HTTPAddress: ":0",
		Auth:        config.ResourceRuntimeAuthConfig{Enabled: true, Header: "Authorization"},
		Database:    config.ResourceDatabaseConfig{DSN: meshxDatabaseDSN(t)},
		AGS:         config.AGSConfig{DatabaseDSN: prepareResolverDB(t), GitRepoDir: t.TempDir()},
	}, Options{AutomationAuthorizer: automation.AllowAutomationAuthorizer{}, NewAGS: func(agsconfig.Config, ...agsserver.Option) (agsServer, error) {
		agsHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			sourceAuth = r.Header.Get("Authorization")
			switch {
			case r.URL.Path == "/api/v3/repos/pingcap/agentteam/contents/.agentteam":
				listRequests++
				listRef = r.URL.Query().Get("ref")
				require.NoError(t, json.NewEncoder(w).Encode([]map[string]any{{
					"type": "file",
					"name": "push.yml",
					"path": ".agentteam/push.yml",
					"sha":  "content-sha",
					"size": len([]byte(automationYAML)),
				}}))
			case r.URL.Path == "/api/v3/repos/pingcap/agentteam/commits/"+after:
				require.NoError(t, json.NewEncoder(w).Encode(map[string]any{"sha": after}))
			case r.URL.Path == "/api/v3/repos/pingcap/agentteam/contents/.agentteam/push.yml":
				contentRequests++
				contentRef = r.URL.Query().Get("ref")
				require.NoError(t, json.NewEncoder(w).Encode(map[string]any{
					"type":     "file",
					"name":     "push.yml",
					"path":     ".agentteam/push.yml",
					"size":     len([]byte(automationYAML)),
					"encoding": "base64",
					"content":  base64.StdEncoding.EncodeToString([]byte(automationYAML)),
				}))
			default:
				http.NotFound(w, r)
			}
		})
		return fakeAGS{handler: agsHandler}, nil
	}})
	require.NoError(t, err)
	installLegacyAutomationSourceForTest(srv)
	t.Cleanup(func() { require.NoError(t, srv.Shutdown(context.Background())) })

	auth := "Bearer " + jwt(map[string]any{"sub": "automation-eventer"})
	body := bytes.NewBufferString(`{"eventId":"ags-local-merge-1","deliveryKey":"github:installation:delivery-api","ref":"refs/heads/main","before":"oldsha","after":"` + after + `","changedPaths":["cmd/server/main.go"],"actorRef":"user:octocat","actorDisplay":"Octocat","eventPayload":{"sender":"octocat"}}`)
	req := httptest.NewRequest(http.MethodPost, "/api/v1/automations/repositories/pingcap/agentteam/events/push", body)
	req.Header.Set("Authorization", auth)
	req.Header.Set("X-Workspace-Id", "ws-push-api")
	rec := httptest.NewRecorder()
	srv.handler.ServeHTTP(rec, req)
	require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
	require.Equal(t, after, listRef)
	require.Equal(t, after, contentRef)
	require.Equal(t, auth, sourceAuth)

	var response struct {
		Runs []struct {
			ID             string `json:"id"`
			DefinitionPath string `json:"definitionPath"`
			GitRef         string `json:"gitRef"`
			CommitSHA      string `json:"commitSha"`
			InputJSON      string `json:"inputJson"`
			TriggerContext struct {
				RepositoryFullName string `json:"repositoryFullName"`
				Ref                string `json:"ref"`
				Branch             string `json:"branch"`
				BeforeSHA          string `json:"beforeSha"`
				AfterSHA           string `json:"afterSha"`
				EventID            string `json:"eventId"`
				ActorRef           string `json:"actorRef"`
				ActorDisplay       string `json:"actorDisplay"`
				TriggerKind        string `json:"triggerKind"`
			} `json:"triggerContext"`
			TriggerKind          string `json:"triggerKind"`
			TriggerRef           string `json:"triggerRef"`
			TargetKind           string `json:"targetKind"`
			TargetRef            string `json:"targetRef"`
			OwnerPrincipalID     string `json:"ownerPrincipalId"`
			CreatedByPrincipalID string `json:"createdByPrincipalId"`
		} `json:"runs"`
	}
	require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &response))
	require.Len(t, response.Runs, 1)
	run := response.Runs[0]
	require.NotEmpty(t, run.ID)
	require.Equal(t, ".agentteam/push.yml", run.DefinitionPath)
	require.Equal(t, after, run.GitRef)
	require.Equal(t, after, run.CommitSHA)
	require.JSONEq(t, `{"sender":"octocat"}`, run.InputJSON)
	require.Equal(t, "pingcap/agentteam", run.TriggerContext.RepositoryFullName)
	require.Equal(t, "refs/heads/main", run.TriggerContext.Ref)
	require.Equal(t, "main", run.TriggerContext.Branch)
	require.Equal(t, "oldsha", run.TriggerContext.BeforeSHA)
	require.Equal(t, after, run.TriggerContext.AfterSHA)
	require.Equal(t, "ags-local-merge-1", run.TriggerContext.EventID)
	require.Equal(t, "user:octocat", run.TriggerContext.ActorRef)
	require.Equal(t, "Octocat", run.TriggerContext.ActorDisplay)
	require.Equal(t, "push", run.TriggerContext.TriggerKind)
	require.Equal(t, "push", run.TriggerKind)
	require.Equal(t, "ags-local-merge-1", run.TriggerRef)
	require.Equal(t, "repository_ref", run.TargetKind)
	require.Equal(t, "pingcap/agentteam@refs/heads/main#"+after, run.TargetRef)
	require.Equal(t, "agentteam-automation-eventer", run.OwnerPrincipalID)
	require.Equal(t, "agentteam-automation-eventer", run.CreatedByPrincipalID)

	duplicateReq := httptest.NewRequest(http.MethodPost, "/api/v1/automations/repositories/pingcap/agentteam/events/push", bytes.NewBufferString(`{"deliveryKey":"github:installation:delivery-api","ref":"refs/heads/main","before":"oldsha","after":"`+after+`","changedPaths":["cmd/server/main.go"],"sender":"octocat","eventPayload":{"sender":"octocat"}}`))
	duplicateReq.Header.Set("Authorization", auth)
	duplicateReq.Header.Set("X-Workspace-Id", "ws-push-api")
	duplicateRec := httptest.NewRecorder()
	srv.handler.ServeHTTP(duplicateRec, duplicateReq)
	require.Equal(t, http.StatusCreated, duplicateRec.Code, duplicateRec.Body.String())
	var duplicateResponse struct {
		Runs []struct {
			ID string `json:"id"`
		} `json:"runs"`
	}
	require.NoError(t, json.Unmarshal(duplicateRec.Body.Bytes(), &duplicateResponse))
	require.Len(t, duplicateResponse.Runs, 1)
	require.Equal(t, run.ID, duplicateResponse.Runs[0].ID)
	require.Equal(t, 2, listRequests, "duplicate delivery still lists candidate automation files")
	require.Equal(t, 1, contentRequests, "duplicate delivery should return the idempotent run without re-reading automation content")

	var runCount int64
	require.NoError(t, srv.meshxDB.Table("automation_runs").Where("workspace_id = ?", "ws-push-api").Count(&runCount).Error)
	require.EqualValues(t, 1, runCount)
	var queueJobCount int64
	require.NoError(t, srv.meshxDB.Table("automation_jobs").Where("workspace_id = ?", "ws-push-api").Count(&queueJobCount).Error)
	require.EqualValues(t, 1, queueJobCount)
}
