package provider

import (
	"encoding/json"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/api/testkit/httputil"
	"github.com/tidbcloud/AgentTeam/internal/store/provider"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

func newGiteaTestDeps(fake *fakeStore, blocker ServiceEventSubscriptionBlocker) Dependencies {
	return Dependencies{
		Store:                           fake,
		ServiceEventSubscriptionBlocker: blocker,
		HTTPClient:                      http.DefaultClient,
		PublicBaseURL:                   "http://agentteam.example.com",
	}
}

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

	fake := newFakeStore()
	fake.giteaConfigView = provider.GiteaAppConfigView{Registered: true, InstanceURL: "https://gitea.example.com"}
	router := newProviderRouter(newGiteaTestDeps(fake, nil))

	resp := httputil.DoJSON(t, router, http.MethodGet, "/api/v1/workspaces/wsp_1/providers/gitea/config", nil)
	require.Equal(t, http.StatusOK, resp.Code)
	require.Contains(t, resp.Body.String(), "gitea.example.com")
}

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

	fake := newFakeStore()
	fake.giteaConfigErr = storeerr.ErrForbidden
	router := newProviderRouter(newGiteaTestDeps(fake, nil))

	resp := httputil.DoJSON(t, router, http.MethodGet, "/api/v1/workspaces/wsp_1/providers/gitea/config", nil)
	require.Equal(t, http.StatusForbidden, resp.Code)
}

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

	fake := newFakeStore()
	fake.giteaConfigView = provider.GiteaAppConfigView{
		Registered:  true,
		InstanceURL: "https://gitea.example.com",
		DisplayName: "Team Gitea",
	}
	fake.giteaSaveResult = provider.GiteaAppConfigView{
		Registered:  true,
		InstanceURL: "https://replacement.example.com",
		DisplayName: "Replacement",
	}
	blocker := &httputil.FakeBlocker{}
	router := newProviderRouter(newGiteaTestDeps(fake, blocker))
	path := "/api/v1/workspaces/wsp_1/providers/gitea/config"

	get := httputil.DoJSON(t, router, http.MethodGet, path, nil)
	require.Equal(t, http.StatusOK, get.Code)
	require.Contains(t, get.Body.String(), "gitea.example.com")

	response := httputil.DoJSON(t, router, http.MethodPut, path, map[string]any{
		"instance_url":   "https://replacement.example.com",
		"api_token":      "",
		"webhook_secret": "",
		"display_name":   "Replacement",
	})
	require.Equal(t, http.StatusOK, response.Code)
	require.Equal(t, "https://replacement.example.com", fake.lastGiteaSave.InstanceURL)
	require.Empty(t, fake.lastGiteaSave.APIToken)
	require.Empty(t, fake.lastGiteaSave.WebhookSecret)

	deleteResponse := httputil.DoJSON(t, router, http.MethodDelete, path, nil)
	require.Equal(t, http.StatusNotFound, deleteResponse.Code, "Gitea config delete is intentionally not exposed")
	require.Empty(t, blocker.BlockCalls)
	require.Empty(t, blocker.UnblockCalls)
}

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

	fake := newFakeStore()
	fake.giteaConnections = []provider.AppConnectionView{{ID: "cxn_1", DisplayLabel: "acme/agentteam"}}
	router := newProviderRouter(newGiteaTestDeps(fake, nil))

	resp := httputil.DoJSON(t, router, http.MethodGet, "/api/v1/workspaces/wsp_1/providers/gitea/connections", nil)
	require.Equal(t, http.StatusOK, resp.Code)

	var connections []provider.AppConnectionView
	require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &connections))
	require.Len(t, connections, 1)
}

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

	fake := newFakeStore()
	fake.giteaListErr = storeerr.ErrForbidden
	router := newProviderRouter(newGiteaTestDeps(fake, nil))

	resp := httputil.DoJSON(t, router, http.MethodGet, "/api/v1/workspaces/wsp_1/providers/gitea/connections", nil)
	require.Equal(t, http.StatusForbidden, resp.Code)
}

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

	fake := newFakeStore()
	fake.giteaConnections = []provider.AppConnectionView{{ID: "cxn_1", DisplayLabel: "acme/agentteam"}}
	router := newProviderRouter(newGiteaTestDeps(fake, nil))
	path := "/api/v1/workspaces/wsp_1/providers/gitea/connections"

	get := httputil.DoJSON(t, router, http.MethodGet, path, nil)
	require.Equal(t, http.StatusOK, get.Code)

	fake.giteaCreateResult = provider.AppInstallationView{ID: "inst_1", AppID: "app_gitea", Enabled: true}
	post := httputil.DoJSON(t, router, http.MethodPost, path, map[string]any{"target": "acme/agentteam"})
	require.Equal(t, http.StatusOK, post.Code)
	require.Equal(t, "acme/agentteam", fake.lastGiteaTarget)

	deleteResponse := httputil.DoJSON(t, router, http.MethodDelete, path+"/cxn_1", nil)
	require.Equal(t, http.StatusNoContent, deleteResponse.Code)
	require.Equal(t, "cxn_1", fake.lastGiteaDisconnect)
}

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

	fake := newFakeStore()
	fake.giteaSaveErr = storeerr.ErrForbidden
	fake.giteaCreateErr = storeerr.ErrConflict
	fake.giteaDisconnectErr = storeerr.ErrForbidden
	router := newProviderRouter(newGiteaTestDeps(fake, nil))
	base := "/api/v1/workspaces/wsp_1/providers/gitea"

	put := httputil.DoJSON(t, router, http.MethodPut, base+"/config", map[string]any{
		"instance_url": "https://gitea.example.com",
		"display_name": "Gitea",
	})
	require.Equal(t, http.StatusForbidden, put.Code)
	post := httputil.DoJSON(t, router, http.MethodPost, base+"/connections", map[string]any{"target": "acme/agentteam"})
	require.Equal(t, http.StatusConflict, post.Code)
	deleteResponse := httputil.DoJSON(t, router, http.MethodDelete, base+"/connections/cxn_1", nil)
	require.Equal(t, http.StatusForbidden, deleteResponse.Code)
}
