// Package automation owns MeshX automation run ledger HTTP APIs.
package automation

import (
	"net/http"
	"strconv"
	"strings"

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

const maxPerPage = 100

// AutomationsHandler owns automation run ledger routes.
type AutomationsHandler struct {
	service          Service
	roles            map[string]struct{}
	authHeader       string
	jsonLimits       automationJSONLimits
	runnerJSONLimits runnerJSONLimits
}

type automationJSONLimits struct {
	control int64
	run     int64
	source  int64
	wait    int64
}

// RouteMiddleware wraps a route handler before it is registered.
type RouteMiddleware func(http.Handler) http.Handler

type routeRegistrar interface {
	Handle(pattern string, handler http.Handler)
}

// DisabledHandler makes the automations API fail closed when the service or role is unavailable.
type DisabledHandler struct {
	Message string
}

func (handler DisabledHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
	message := "Automations API is not configured"
	if handler.Message != "" {
		message = handler.Message
	}
	meshxapi.WriteError(w, meshxapi.NewHTTPError(http.StatusServiceUnavailable, message))
}

func NewAutomationsHandler(service Service, cfg config.AutomationConfig, authHeader ...string) *AutomationsHandler {
	roles := make(map[string]struct{}, len(cfg.Roles))
	for _, role := range cfg.Roles {
		if normalized := config.NormalizeAutomationRole(role); normalized != "" {
			roles[normalized] = struct{}{}
		}
	}
	header := config.DefaultAuthHeader
	if len(authHeader) > 0 && strings.TrimSpace(authHeader[0]) != "" {
		header = strings.TrimSpace(authHeader[0])
	}
	return &AutomationsHandler{service: service, roles: roles, authHeader: header, jsonLimits: newAutomationJSONLimits(cfg), runnerJSONLimits: newRunnerJSONLimits(cfg)}
}

// RegisterRoutes mounts MeshX Automation API routes on the supplied router.
func RegisterRoutes(router routeRegistrar, handler *AutomationsHandler, middleware RouteMiddleware) {
	if router == nil {
		return
	}
	if handler == nil {
		disabled := DisabledHandler{}
		registerAutomationRoute(router, "/api/v1/automations/repositories/", disabled, middleware)
		registerAutomationRoute(router, "/api/v1/automations/runners", disabled, middleware)
		registerAutomationRoute(router, "/api/v1/automations/runners/", disabled, middleware)
		registerAutomationRoute(router, "/api/v1/automations/runs", disabled, middleware)
		registerAutomationRoute(router, "/api/v1/automations/dashboard", disabled, middleware)
		registerAutomationRoute(router, "/api/v1/automations/runs/", disabled, middleware)
		return
	}
	registerAutomationRoleRoute(router, handler, config.AutomationRoleScheduler, "POST /api/v1/automations/repositories/{owner}/{repo}/runs", meshxapi.HandleAuthJSONLimit[dto.AutomationRepositoryRunRequest](handler.jsonLimits.run, handler.StartRepositoryAutomationRun, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleEventer, "POST /api/v1/automations/repositories/{owner}/{repo}/events/push", meshxapi.HandleAuthJSONLimit[dto.AutomationRepositoryPushEventRequest](handler.jsonLimits.run, handler.StartRepositoryAutomationRunsForPushEvent, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/repositories", meshxapi.HandleAuth(handler.ListAutomationRepositories, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/repositories/{owner}/{repo}/definitions", meshxapi.HandleAuth(handler.ListRepositoryAutomationFiles, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/repositories/{owner}/{repo}/definitions/{definitionPath}", meshxapi.HandleAuth(handler.GetRepositoryAutomationFile, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/repositories/{owner}/{repo}/definitions/{definitionPath}/runner-readiness", meshxapi.HandleAuth(handler.GetRepositoryAutomationRunnerReadiness, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "PUT /api/v1/automations/repositories/{owner}/{repo}/definitions/{definitionPath}", meshxapi.HandleAuthJSONLimit[dto.AutomationSourceSaveRequest](handler.jsonLimits.source, handler.SaveRepositoryAutomationFile, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "DELETE /api/v1/automations/repositories/{owner}/{repo}/definitions/{definitionPath}", meshxapi.HandleAuthJSONLimit[dto.AutomationSourceDeleteRequest](handler.jsonLimits.control, handler.DeleteRepositoryAutomationFile, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "POST /api/v1/automations/repositories/{owner}/{repo}/definitions/{definitionPath}/validate", meshxapi.HandleAuthJSONLimit[dto.AutomationSourceValidateRequest](handler.jsonLimits.source, handler.ValidateRepositoryAutomationFile, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "POST /api/v1/automations/repositories/{owner}/{repo}/definitions/{definitionPath}/preflight", meshxapi.HandleAuthJSONLimit[dto.AutomationSourcePreflightRequest](handler.jsonLimits.source, handler.PreflightRepositoryAutomationFile, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/repositories/{owner}/{repo}/secrets", meshxapi.HandleAuth(handler.ListAutomationSecrets, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "PUT /api/v1/automations/repositories/{owner}/{repo}/secrets/{name}", meshxapi.HandleAuthJSONLimit[dto.AutomationSecretPutRequest](handler.jsonLimits.control, handler.UpsertAutomationSecret, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "DELETE /api/v1/automations/repositories/{owner}/{repo}/secrets/{name}", meshxapi.HandleAuth(handler.DeleteAutomationSecret, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleRunnerDispatch, "POST /api/v1/automations/runners/register", meshxapi.HandleAuthJSONLimit[dto.AutomationRunnerRegisterRequest](handler.jsonLimits.control, handler.RegisterRunner, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleRunnerDispatch, "POST /api/v1/automations/runners/{runner_id}/sessions", handleRunnerJSON[dto.AutomationRunnerSessionRequest](handler.runnerJSONLimits.control, handler.CreateRunnerSession), nil)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleRunnerDispatch, "POST /api/v1/automations/runners/{runner_id}/sessions/{session_id}/heartbeat", handleRunnerJSON[dto.AutomationRunnerHeartbeatRequest](handler.runnerJSONLimits.control, handler.HeartbeatRunnerSession), nil)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleRunnerDispatch, "GET /api/v1/automations/runners/{runner_id}/jobs:acquire", handleRunner(handler.AcquireRunnerJobs), nil)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleRunnerDispatch, "POST /api/v1/automations/runners/{runner_id}/jobs/{job_id}/accepted", handleRunnerJSON[dto.AutomationRunnerJobAcceptedRequest](handler.runnerJSONLimits.control, handler.AcceptRunnerJob), nil)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleRunnerDispatch, "POST /api/v1/automations/runners/{runner_id}/jobs/{job_id}/completed", handleRunnerJSON[dto.AutomationRunnerJobCompletedRequest](handler.runnerJSONLimits.callback, handler.CompleteRunnerJob), nil)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleRunnerDispatch, "POST /api/v1/automations/runners/{runner_id}/jobs/{job_id}/failed", handleRunnerJSON[dto.AutomationRunnerJobFailedRequest](handler.runnerJSONLimits.callback, handler.FailRunnerJob), nil)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleRunnerDispatch, "POST /api/v1/automations/runners/{runner_id}/jobs/{job_id}/renew", handleRunnerJSON[dto.AutomationRunnerJobRenewRequest](handler.runnerJSONLimits.control, handler.RenewRunnerJob), nil)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleRunnerDispatch, "POST /api/v1/automations/runners/{runner_id}/jobs/{job_id}/logs", handleRunnerJSON[dto.AutomationRunnerJobLogRequest](handler.runnerJSONLimits.log, handler.AppendRunnerJobLog), nil)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleRunnerDispatch, "POST /api/v1/automations/runners/{runner_id}/jobs/{job_id}/artifacts", handleRunnerJSON[dto.AutomationRunnerJobArtifactRequest](handler.runnerJSONLimits.artifact, handler.AppendRunnerJobArtifact), nil)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/runs", meshxapi.HandleAuth(handler.ListRuns, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/dashboard", meshxapi.HandleAuth(handler.GetDashboard, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleEventer, "POST /api/v1/automations/waits:deliver-event", meshxapi.HandleAuthJSONLimit[dto.AutomationWaitSignalDeliverRequest](handler.jsonLimits.wait, handler.DeliverWaitSignal, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "PUT /api/v1/automations/pinned/{owner}/{repo}", meshxapi.HandleAuth(handler.PinAutomationRepo, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "DELETE /api/v1/automations/pinned/{owner}/{repo}", meshxapi.HandleAuth(handler.UnpinAutomationRepo, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/pinned", meshxapi.HandleAuth(handler.ListPinnedAutomationRepos, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/runs/{run_id}", meshxapi.HandleAuth(handler.GetRun, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/runs/{run_id}/overview", meshxapi.HandleAuth(handler.GetRunOverview, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/runs/{run_id}/jobs", meshxapi.HandleAuth(handler.ListRunJobRuns, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/runs/{run_id}/job-runs", meshxapi.HandleAuth(handler.ListRunJobRuns, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/runs/{run_id}/events", meshxapi.HandleAuth(handler.ListRunEvents, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/runs/{run_id}/logs", meshxapi.HandleAuth(handler.ListRunLogChunks, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/runs/{run_id}/jobs/{job_run_id}/logs/{stream}/{sequence}", meshxapi.HandleAuth(handler.ReadRunLogChunk, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/runs/{run_id}/artifacts", meshxapi.HandleAuth(handler.ListRunArtifacts, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/runs/{run_id}/artifacts/{artifact_id}", meshxapi.HandleAuth(handler.ReadRunArtifact, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleScheduler, "POST /api/v1/automations/runs/{run_id}/cancel", meshxapi.HandleAuthJSONLimit[dto.AutomationRunCancelRequest](handler.jsonLimits.control, handler.CancelRun, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleScheduler, "POST /api/v1/automations/runs/{run_id}/retry", meshxapi.HandleAuth(handler.RetryRun, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleAPI, "GET /api/v1/automations/runs/{run_id}/waits", meshxapi.HandleAuth(handler.ListRunWaits, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleEventer, "POST /api/v1/automations/runs/{run_id}/waits/{wait_id}/resume", meshxapi.HandleAuthJSONLimit[dto.AutomationWaitResumeRequest](handler.jsonLimits.wait, handler.ResumeRunWait, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleEventer, "POST /api/v1/automations/runs/{run_id}/waits/{wait_id}/complete-task", meshxapi.HandleAuthJSONLimit[dto.AutomationWaitTaskCompleteRequest](handler.jsonLimits.wait, handler.CompleteRunTaskWait, automationErrorHandling()), middleware)
	registerAutomationRoleRoute(router, handler, config.AutomationRoleEventer, "POST /api/v1/automations/runs/{run_id}/waits/{wait_id}/signal-event", meshxapi.HandleAuthJSONLimit[dto.AutomationWaitEventSignalRequest](handler.jsonLimits.wait, handler.SignalRunEventWait, automationErrorHandling()), middleware)
}

func newAutomationJSONLimits(cfg config.AutomationConfig) automationJSONLimits {
	maxSourceBytes := int64(cfg.MaxAutomationSourceBytes)
	if maxSourceBytes <= 0 {
		maxSourceBytes = 256 * 1024
	}
	maxRunInputBytes := int64(cfg.MaxRunInputBytes)
	if maxRunInputBytes <= 0 {
		maxRunInputBytes = 64 * 1024
	}
	maxJobInputBytes := int64(cfg.MaxJobInputBytes)
	if maxJobInputBytes <= 0 {
		maxJobInputBytes = 128 * 1024
	}
	return automationJSONLimits{
		control: runnerJSONControlBodyBytes,
		run:     saturatingAdd(maxRunInputBytes, runnerJSONMetadataBodyBytes),
		source:  saturatingAdd(maxSourceBytes, runnerJSONMetadataBodyBytes),
		wait:    saturatingAdd(maxJobInputBytes, runnerJSONMetadataBodyBytes),
	}
}

func registerAutomationRoute(router routeRegistrar, pattern string, handler http.Handler, middleware RouteMiddleware) {
	if middleware != nil {
		handler = middleware(handler)
	}
	router.Handle(pattern, handler)
}

func registerAutomationRoleRoute(router routeRegistrar, handler *AutomationsHandler, role string, pattern string, route http.Handler, middleware RouteMiddleware) {
	if handler == nil || !handler.hasRole(role) {
		route = DisabledHandler{Message: "Automation role " + role + " is not enabled"}
	}
	registerAutomationRoute(router, pattern, route, middleware)
}

func (h *AutomationsHandler) hasRole(role string) bool {
	if h == nil || h.service == nil {
		return false
	}
	_, ok := h.roles[config.NormalizeAutomationRole(role)]
	return ok
}

func paginationWithMax(r *http.Request, maxAllowedPerPage int) (int, int) {
	page := 1
	perPage := 30
	if r != nil {
		if raw := r.URL.Query().Get("page"); raw != "" {
			if parsed, err := strconv.Atoi(raw); err == nil && parsed > 0 {
				page = parsed
			}
		}
		if raw := r.URL.Query().Get("perPage"); raw != "" {
			if parsed, err := strconv.Atoi(raw); err == nil && parsed > 0 {
				perPage = parsed
			}
		}
	}
	if maxAllowedPerPage <= 0 {
		maxAllowedPerPage = maxPerPage
	}
	if perPage > maxAllowedPerPage {
		perPage = maxAllowedPerPage
	}
	return page, perPage
}

func (h *AutomationsHandler) ensureService() error {
	if h == nil || h.service == nil {
		return meshxapi.NewHTTPError(http.StatusServiceUnavailable, "Automations API is not configured")
	}
	return nil
}
