// Package provider contains provider integration (GitHub, Gitea) API routes.
package provider

import (
	"log/slog"
	"net/http"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
)

func handleProviderWorkspace(handler api.WorkspaceScopedHandler) gin.HandlerFunc {
	return api.HandleWorkspaceScoped(handler)
}

func handleProviderWorkspaceJSON[T any](
	handler func(api.WorkspaceScopedJSON[T]) (api.Response, error),
) gin.HandlerFunc {
	return api.HandleWorkspaceScopedJSON[T](handler)
}

// Dependencies contains collaborators and config for provider routes.
type Dependencies struct {
	SessionAuth                     api.SessionAuthenticator
	WorkspaceStore                  api.WorkspaceResolver
	Store                           Store
	GitHubAdminStore                GitHubAdminStore
	GiteaAdminStore                 GiteaAdminStore
	InstallationRequestProjector    AppInstallationRequestProjector
	ServiceEventSubscriptionBlocker ServiceEventSubscriptionBlocker
	GitHubConfig                    GitHubConfig
	HTTPClient                      *http.Client
	PublicBaseURL                   string
	EmailVerificationRequired       bool
	Logger                          *slog.Logger
}

// RegisterRoutes mounts provider integration (GitHub, Gitea) routes.
func RegisterRoutes(router gin.IRouter, dependencies Dependencies) {
	RegisterControlRoutes(router, dependencies)
	RegisterWorkspaceRoutes(router, dependencies)
}

// RegisterControlRoutes mounts external provider callback routes.
func RegisterControlRoutes(router gin.IRouter, dependencies Dependencies) {
	if router == nil {
		return
	}
	dependencies = normalizeDependencies(dependencies)
	registerGitHubCallbackRoutes(router, dependencies)
}

// RegisterWorkspaceRoutes mounts workspace provider configuration routes.
func RegisterWorkspaceRoutes(router gin.IRouter, dependencies Dependencies) {
	if router == nil {
		return
	}
	dependencies = normalizeDependencies(dependencies)
	registerGitHubWorkspaceRoutes(router, dependencies)
	registerGiteaWorkspaceRoutes(router, dependencies)
}

func normalizeDependencies(dependencies Dependencies) Dependencies {
	if dependencies.SessionAuth == nil {
		if auth, ok := dependencies.Store.(api.SessionAuthenticator); ok {
			dependencies.SessionAuth = auth
		}
	}
	if dependencies.WorkspaceStore == nil {
		if resolver, ok := dependencies.Store.(api.WorkspaceResolver); ok {
			dependencies.WorkspaceStore = resolver
		}
	}
	if dependencies.GiteaAdminStore == nil {
		if adminStore, ok := dependencies.Store.(GiteaAdminStore); ok {
			dependencies.GiteaAdminStore = adminStore
		}
	}
	if dependencies.GitHubAdminStore == nil {
		if adminStore, ok := dependencies.Store.(GitHubAdminStore); ok {
			dependencies.GitHubAdminStore = adminStore
		}
	}
	return dependencies
}

func registerGitHubCallbackRoutes(router gin.IRouter, dependencies Dependencies) {
	gh := &GitHubHandler{
		store:                           dependencies.Store,
		adminStore:                      dependencies.GitHubAdminStore,
		installationRequestProjector:    dependencies.InstallationRequestProjector,
		serviceEventSubscriptionBlocker: dependencies.ServiceEventSubscriptionBlocker,
		githubConfig:                    dependencies.GitHubConfig,
		httpClient:                      dependencies.HTTPClient,
		publicBaseURL:                   dependencies.PublicBaseURL,
		logger:                          dependencies.Logger,
	}

	apiGroup := router.Group("/api")
	apiGroup.GET("/github/callback", gh.RegisterCallback)
	apiGroup.GET("/github/setup", gh.Setup)
	apiGroup.GET("/github/setup/start", handleGitHubRegistrationSetupStart(gh))
}

func registerGitHubWorkspaceRoutes(router gin.IRouter, dependencies Dependencies) {
	gh := &GitHubHandler{
		store:                           dependencies.Store,
		adminStore:                      dependencies.GitHubAdminStore,
		installationRequestProjector:    dependencies.InstallationRequestProjector,
		serviceEventSubscriptionBlocker: dependencies.ServiceEventSubscriptionBlocker,
		githubConfig:                    dependencies.GitHubConfig,
		httpClient:                      dependencies.HTTPClient,
		publicBaseURL:                   dependencies.PublicBaseURL,
		logger:                          dependencies.Logger,
	}

	workspaceScoped := router.Group(
		"/api/v1/workspaces/:workspaceId/providers",
		api.RequireAuthenticatedUser(dependencies.SessionAuth),
		api.RequireVerifiedIdentity(dependencies.EmailVerificationRequired),
		api.RequireRouteWorkspace(dependencies.WorkspaceStore),
	)

	githubRoutes := workspaceScoped.Group("/github")
	githubRoutes.GET("/registration", handleProviderWorkspace(gh.Register))
	githubRoutes.POST("/registration/start", handleProviderWorkspaceJSON[dto.GitHubRegistrationStartRequest](gh.RegistrationStart))
	githubRoutes.GET("/installation", handleProviderWorkspace(gh.Install))
	githubRoutes.GET("/setup-proposal", handleProviderWorkspace(gh.SetupProposalGet))
	githubRoutes.PUT("/setup-proposal", handleProviderWorkspaceJSON[dto.GitHubConfigUpdateRequest](gh.SetupProposalUpdate))
	githubRoutes.GET("/setup-proposal/:proposalId/installations", handleProviderWorkspace(gh.SetupProposalInstallationsList))
	githubRoutes.GET("/setup-proposal/:proposalId/installation", handleProviderWorkspace(gh.SetupProposalInstall))
	githubRoutes.PUT("/setup-draft", handleProviderWorkspaceJSON[dto.GitHubConfigUpdateRequest](gh.SetupDraftUpdate))
	githubRoutes.GET("/setup-draft/:draftId/installations", handleProviderWorkspace(gh.SetupDraftInstallationsList))
	githubRoutes.POST("/setup-draft/complete", handleProviderWorkspaceJSON[dto.GitHubSetupCompletionRequest](gh.SetupDraftComplete))
	githubRoutes.PUT("/config", handleProviderWorkspaceJSON[dto.GitHubConfigUpdateRequest](gh.ConfigUpdate))
	githubRoutes.GET("/config", handleProviderWorkspace(gh.ConfigGet))
	githubRoutes.DELETE("/config", handleProviderWorkspace(gh.ConfigDelete))
	githubRoutes.GET("/installations", handleProviderWorkspace(gh.InstallationsList))
	githubRoutes.POST("/connections", handleProviderWorkspaceJSON[dto.GitHubConnectionCreateRequest](gh.ConnectionCreate))
	githubRoutes.GET("/connections", handleProviderWorkspace(gh.ConnectionsList))
	githubRoutes.DELETE("/connections/:connectionId", handleProviderWorkspace(gh.Disconnect))
}

func registerGiteaWorkspaceRoutes(router gin.IRouter, dependencies Dependencies) {
	ge := &GiteaHandler{
		store:      dependencies.Store,
		adminStore: dependencies.GiteaAdminStore,
	}

	workspaceScoped := router.Group(
		"/api/v1/workspaces/:workspaceId/providers",
		api.RequireAuthenticatedUser(dependencies.SessionAuth),
		api.RequireVerifiedIdentity(dependencies.EmailVerificationRequired),
		api.RequireRouteWorkspace(dependencies.WorkspaceStore),
	)

	giteaRoutes := workspaceScoped.Group("/gitea")
	giteaRoutes.PUT("/config", handleProviderWorkspaceJSON[dto.GiteaConfigUpdateRequest](ge.ConfigUpdate))
	giteaRoutes.GET("/config", handleProviderWorkspace(ge.ConfigGet))
	giteaRoutes.POST("/connections", handleProviderWorkspaceJSON[dto.GiteaConnectionCreateRequest](ge.ConnectionCreate))
	giteaRoutes.GET("/connections", handleProviderWorkspace(ge.ConnectionsList))
	giteaRoutes.DELETE("/connections/:connectionId", handleProviderWorkspace(ge.Disconnect))
}
