// Copyright 2026 MarketAlly. All rights reserved.
// SPDX-License-Identifier: MIT

package v2

import (
	"net/http"
	"time"

	actions_model "code.gitea.io/gitea/models/actions"
	"code.gitea.io/gitea/modules/json"
	api "code.gitea.io/gitea/modules/structs"
	"code.gitea.io/gitea/services/context"
)

// RunnerStatusResponse represents the runner status for API/polling
type RunnerStatusResponse struct {
	ID           int64                 `json:"id"`
	Name         string                `json:"name"`
	IsOnline     bool                  `json:"is_online"`
	Status       string                `json:"status"`
	Version      string                `json:"version"`
	Labels       []string              `json:"labels"`
	LastOnline   *time.Time            `json:"last_online,omitempty"`
	Capabilities *api.RunnerCapability `json:"capabilities,omitempty"`
}

// GetRunnerStatus returns the current status of a runner
// @Summary Get runner status
// @Description Returns current runner status including online state, capabilities, disk, and bandwidth
// @Tags actions
// @Accept json
// @Produce json
// @Param owner path string true "owner of the repo"
// @Param repo path string true "name of the repo"
// @Param runner_id path int64 true "runner ID"
// @Success 200 {object} RunnerStatusResponse
// @Router /repos/{owner}/{repo}/actions/runners/{runner_id}/status [get]
func GetRunnerStatus(ctx *context.APIContext) {
	runnerID := ctx.PathParamInt64("runner_id")

	runner, err := actions_model.GetRunnerByID(ctx, runnerID)
	if err != nil {
		ctx.APIErrorNotFound(err)
		return
	}

	// Check access - runner must belong to this repo or be global
	repo := ctx.Repo.Repository
	if runner.RepoID != 0 && runner.RepoID != repo.ID {
		ctx.APIErrorNotFound(nil)
		return
	}

	response := buildRunnerStatusResponse(runner)
	ctx.JSON(http.StatusOK, response)
}

// GetAdminRunnerStatus returns the current status of a runner (admin endpoint)
// @Summary Get runner status (admin)
// @Description Returns current runner status for admin panel AJAX polling
// @Tags admin
// @Accept json
// @Produce json
// @Param runner_id path int64 true "runner ID"
// @Success 200 {object} RunnerStatusResponse
// @Router /admin/actions/runners/{runner_id}/status [get]
func GetAdminRunnerStatus(ctx *context.APIContext) {
	runnerID := ctx.PathParamInt64("runner_id")

	runner, err := actions_model.GetRunnerByID(ctx, runnerID)
	if err != nil {
		ctx.APIErrorNotFound(err)
		return
	}

	response := buildRunnerStatusResponse(runner)
	ctx.JSON(http.StatusOK, response)
}

// buildRunnerStatusResponse creates a status response from a runner
func buildRunnerStatusResponse(runner *actions_model.ActionRunner) *RunnerStatusResponse {
	response := &RunnerStatusResponse{
		ID:       runner.ID,
		Name:     runner.Name,
		IsOnline: runner.IsOnline(),
		Status:   runner.Status().String(),
		Version:  runner.Version,
		Labels:   runner.AgentLabels,
	}

	// Add last online time if available
	if runner.LastOnline.AsTime().Unix() > 0 {
		lastOnline := runner.LastOnline.AsTime()
		response.LastOnline = &lastOnline
	}

	// Parse capabilities JSON if available
	if runner.CapabilitiesJSON != "" {
		var caps api.RunnerCapability
		if err := json.Unmarshal([]byte(runner.CapabilitiesJSON), &caps); err == nil {
			response.Capabilities = &caps
		}
	}

	return response
}

// ListRunnersStatus returns status for all runners accessible to the repo
// @Summary List runner statuses
// @Description Returns status for all runners available to the repository
// @Tags actions
// @Accept json
// @Produce json
// @Param owner path string true "owner of the repo"
// @Param repo path string true "name of the repo"
// @Success 200 {array} RunnerStatusResponse
// @Router /repos/{owner}/{repo}/actions/runners/status [get]
func ListRunnersStatus(ctx *context.APIContext) {
	repo := ctx.Repo.Repository

	runners, err := actions_model.GetRunnersOfRepo(ctx, repo.ID)
	if err != nil {
		ctx.APIErrorInternal(err)
		return
	}

	responses := make([]*RunnerStatusResponse, 0, len(runners))
	for _, runner := range runners {
		responses = append(responses, buildRunnerStatusResponse(runner))
	}

	ctx.JSON(http.StatusOK, responses)
}
