package app

import (
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/api/testkit/httputil"
	"github.com/tidbcloud/AgentTeam/internal/store/app"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
	"github.com/tidbcloud/AgentTeam/internal/workspace/owner"
)

func TestWebhookHandleGitHubUsesSharedAppWebhookHandler(t *testing.T) {
	t.Parallel()

	fake := newFakeStore()
	fake.handleResult = app.AppWebhookResult{
		WorkspaceID: "wsp_1",
		Deliveries:  []message.MessageDelivery{{ThreadID: "thr_1", MessageID: "msg_1"}},
	}
	followup := &fakeFollowupDeliverer{}
	router := newAppRouter(Dependencies{
		AppStore:          fake,
		FollowupDeliverer: followup,
	})

	resp := httputil.DoRaw(t, router, http.MethodPost, "/api/apps/github/webhook",
		http.Header{"Content-Type": {"application/json"}}, []byte(`{}`))
	require.Equal(t, http.StatusOK, resp.Code)
	require.True(t, fake.handleCalled)
	require.Equal(t, 1, followup.calls)
	require.Equal(t, "wsp_1", followup.lastWorkspaceID)
}

func TestWebhookHandleDirectRejectsOwnerMismatch(t *testing.T) {
	t.Parallel()

	fake := newFakeStore()
	fake.handleResult = app.AppWebhookResult{
		WorkspaceID: "wsp_other",
		Deliveries:  []message.MessageDelivery{{ThreadID: "thr_1", MessageID: "msg_1"}},
	}
	followup := &fakeFollowupDeliverer{}
	router := newAppRouter(Dependencies{
		AppStore:          fake,
		FollowupDeliverer: followup,
		ManagedWorkspaces: owner.NewStaticSet([]string{"wsp_1"}),
	})

	resp := httputil.DoRaw(t, router, http.MethodPost, "/api/apps/github/webhook",
		http.Header{"Content-Type": {"application/json"}}, []byte(`{}`))
	require.Equal(t, http.StatusMisdirectedRequest, resp.Code)
	require.Contains(t, resp.Body.String(), "workspace_owner_mismatch")
	require.Equal(t, 0, followup.calls)
}

func TestWebhookHandleGitHubNotFound(t *testing.T) {
	t.Parallel()

	fake := newFakeStore()
	fake.handleErr = storeerr.ErrNotFound
	router := newAppRouter(Dependencies{AppStore: fake})

	resp := httputil.DoRaw(t, router, http.MethodPost, "/api/apps/github/webhook",
		http.Header{"Content-Type": {"application/json"}}, []byte(`{}`))
	require.Equal(t, http.StatusNotFound, resp.Code)
}

func TestWebhookHandleRejectsOversizedBody(t *testing.T) {
	t.Parallel()

	fake := newFakeStore()
	router := newAppRouter(Dependencies{AppStore: fake})
	body := make([]byte, maxWebhookPayloadBytes+1)

	resp := httputil.DoRaw(t, router, http.MethodPost, "/api/apps/github/webhook",
		http.Header{"Content-Type": {"application/json"}}, body)
	require.Equal(t, http.StatusRequestEntityTooLarge, resp.Code)
}

func TestWebhookHandleGitHubUnauthorized(t *testing.T) {
	t.Parallel()

	fake := newFakeStore()
	fake.handleErr = storeerr.ErrUnauthorized
	router := newAppRouter(Dependencies{AppStore: fake})

	resp := httputil.DoRaw(t, router, http.MethodPost, "/api/apps/github/webhook",
		http.Header{"Content-Type": {"application/json"}}, []byte(`{}`))
	require.Equal(t, http.StatusUnauthorized, resp.Code)
}

func TestWebhookHandleGitHubBadRequestIsNonOK(t *testing.T) {
	t.Parallel()

	fake := newFakeStore()
	fake.handleErr = storeerr.ErrBadRequest
	router := newAppRouter(Dependencies{AppStore: fake})

	resp := httputil.DoRaw(t, router, http.MethodPost, "/api/apps/github/webhook",
		http.Header{"Content-Type": {"application/json"}}, []byte(`{`))
	require.Equal(t, http.StatusBadRequest, resp.Code)
}

func TestWebhookHandleGiteaUsesSharedAppWebhookHandler(t *testing.T) {
	t.Parallel()

	fake := newFakeStore()
	router := newAppRouter(Dependencies{AppStore: fake})

	resp := httputil.DoRaw(t, router, http.MethodPost, "/api/apps/gitea/webhook",
		http.Header{"Content-Type": {"application/json"}}, []byte(`{}`))
	require.Equal(t, http.StatusOK, resp.Code)
	require.True(t, fake.handleCalled)
}

func TestWebhookHandleDoesNotRegisterCustomAppRoute(t *testing.T) {
	t.Parallel()

	fake := newFakeStore()
	router := newAppRouter(Dependencies{AppStore: fake})

	resp := httputil.DoRaw(t, router, http.MethodPost, "/api/apps/demo/webhook",
		http.Header{"Content-Type": {"application/json"}}, []byte(`{}`))
	require.Equal(t, http.StatusNotFound, resp.Code)
	require.False(t, fake.handleCalled)
}
