package handlers

import (
	"context"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/go-chi/chi/v5"
	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/pkg/api"
)

// PipelineErrorResponse is the JSON representation of a pipeline error.
type PipelineErrorResponse struct {
	Type      string `json:"type"`
	Message   string `json:"message"`
	IsWarning bool   `json:"is_warning"`
}

// PipelineResponse is the JSON representation of a CI pipeline.
type PipelineResponse struct {
	ID       int64                   `json:"id"`
	Number   int64                   `json:"number"`
	Status   string                  `json:"status"`
	Event    string                  `json:"event"`
	Branch   string                  `json:"branch"`
	Commit   string                  `json:"commit"`
	Message  string                  `json:"message"`
	Author   string                  `json:"author"`
	Started  string                  `json:"started,omitempty"`
	Finished string                  `json:"finished,omitempty"`
	Errors   []PipelineErrorResponse `json:"errors,omitempty"`
}

// ListPipelines returns recent CI pipeline executions for a project.
// GET /projects/{id}/pipelines
func (h *InfrastructureHandler) ListPipelines(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "id")
	ctx, cancel := context.WithTimeout(r.Context(), TimeoutLookup)
	defer cancel()

	if err := validateProjectID(projectID); err != nil {
		api.WriteBadRequest(w, r, err.Error())
		return
	}

	if h.ciProvider == nil {
		api.WriteInternalError(w, r, "CI provider not configured")
		return
	}

	pipelines, err := h.ciProvider.ListPipelines(ctx, h.defaultGitOwner, projectID)
	if err != nil {
		api.WriteNotFound(w, r, fmt.Sprintf("pipelines not found: %v", err))
		return
	}

	resp := make([]PipelineResponse, len(pipelines))
	for i, p := range pipelines {
		resp[i] = PipelineResponse{
			ID:       p.ID,
			Number:   p.Number,
			Status:   p.Status,
			Event:    p.Event,
			Branch:   p.Branch,
			Commit:   p.Commit,
			Message:  p.Message,
			Author:   p.Author,
			Started:  formatTime(p.Started),
			Finished: formatTime(p.Finished),
			Errors:   mapPipelineErrors(p.Errors),
		}
	}

	api.WriteSuccess(w, r, resp)
}

// GetPipeline returns a specific CI pipeline execution for a project.
// GET /projects/{id}/pipelines/{number}
func (h *InfrastructureHandler) GetPipeline(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "id")
	numberStr := chi.URLParam(r, "number")
	ctx, cancel := context.WithTimeout(r.Context(), TimeoutLookup)
	defer cancel()

	if err := validateProjectID(projectID); err != nil {
		api.WriteBadRequest(w, r, err.Error())
		return
	}

	number, err := strconv.ParseInt(numberStr, 10, 64)
	if err != nil {
		api.WriteBadRequest(w, r, "invalid pipeline number")
		return
	}

	if h.ciProvider == nil {
		api.WriteInternalError(w, r, "CI provider not configured")
		return
	}

	p, err := h.ciProvider.GetPipeline(ctx, h.defaultGitOwner, projectID, number)
	if err != nil {
		api.WriteNotFound(w, r, fmt.Sprintf("pipeline not found: %v", err))
		return
	}

	api.WriteSuccess(w, r, PipelineResponse{
		ID:       p.ID,
		Number:   p.Number,
		Status:   p.Status,
		Event:    p.Event,
		Branch:   p.Branch,
		Commit:   p.Commit,
		Message:  p.Message,
		Author:   p.Author,
		Started:  formatTime(p.Started),
		Finished: formatTime(p.Finished),
		Errors:   mapPipelineErrors(p.Errors),
	})
}

// formatTime formats a time.Time as RFC3339, returning empty string for zero time.
func formatTime(t time.Time) string {
	if t.IsZero() {
		return ""
	}
	return t.Format(time.RFC3339)
}

// mapPipelineErrors converts domain pipeline errors to response format.
func mapPipelineErrors(errors []domain.CIPipelineError) []PipelineErrorResponse {
	if len(errors) == 0 {
		return nil
	}
	resp := make([]PipelineErrorResponse, len(errors))
	for i, e := range errors {
		resp[i] = PipelineErrorResponse{
			Type:      e.Type,
			Message:   e.Message,
			IsWarning: e.IsWarning,
		}
	}
	return resp
}

// PipelineStepResponse is the JSON representation of a pipeline step.
type PipelineStepResponse struct {
	ID       int64  `json:"id"`
	Name     string `json:"name"`
	Status   string `json:"status"`
	ExitCode *int   `json:"exit_code,omitempty"`
	Duration int    `json:"duration_seconds"`
	Error    string `json:"error,omitempty"`
	Log      string `json:"log,omitempty"`
}

// PipelineStepsResponse is the JSON representation of pipeline steps.
type PipelineStepsResponse struct {
	PipelineNumber int64                  `json:"pipeline_number"`
	URL            string                 `json:"url"`
	Steps          []PipelineStepResponse `json:"steps"`
}

// GetPipelineSteps returns detailed step information for a pipeline.
// GET /projects/{id}/pipelines/{number}/steps
func (h *InfrastructureHandler) GetPipelineSteps(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "id")
	numberStr := chi.URLParam(r, "number")
	ctx, cancel := context.WithTimeout(r.Context(), TimeoutLookup)
	defer cancel()

	if err := validateProjectID(projectID); err != nil {
		api.WriteBadRequest(w, r, err.Error())
		return
	}

	number, err := strconv.ParseInt(numberStr, 10, 64)
	if err != nil {
		api.WriteBadRequest(w, r, "invalid pipeline number")
		return
	}

	if h.ciProvider == nil {
		api.WriteInternalError(w, r, "CI provider not configured")
		return
	}

	steps, err := h.ciProvider.GetPipelineSteps(ctx, h.defaultGitOwner, projectID, number)
	if err != nil {
		api.WriteNotFound(w, r, fmt.Sprintf("pipeline steps not found: %v", err))
		return
	}

	// Map to response
	respSteps := make([]PipelineStepResponse, len(steps.Steps))
	for i, s := range steps.Steps {
		respSteps[i] = PipelineStepResponse{
			ID:       s.ID,
			Name:     s.Name,
			Status:   s.Status,
			ExitCode: s.ExitCode,
			Duration: s.Duration,
			Error:    s.Error,
			Log:      s.Log,
		}
	}

	api.WriteSuccess(w, r, PipelineStepsResponse{
		PipelineNumber: steps.PipelineNumber,
		URL:            steps.URL,
		Steps:          respSteps,
	})
}

// RetryPipeline restarts a failed or stopped pipeline.
// POST /projects/{id}/pipelines/{number}/retry
func (h *InfrastructureHandler) RetryPipeline(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "id")
	numberStr := chi.URLParam(r, "number")
	ctx, cancel := context.WithTimeout(r.Context(), TimeoutStandard)
	defer cancel()

	if err := validateProjectID(projectID); err != nil {
		api.WriteBadRequest(w, r, err.Error())
		return
	}

	number, err := strconv.ParseInt(numberStr, 10, 64)
	if err != nil {
		api.WriteBadRequest(w, r, "invalid pipeline number")
		return
	}

	if h.ciProvider == nil {
		api.WriteInternalError(w, r, "CI provider not configured")
		return
	}

	p, err := h.ciProvider.RetryPipeline(ctx, h.defaultGitOwner, projectID, number)
	if err != nil {
		api.WriteInternalError(w, r, fmt.Sprintf("failed to retry pipeline: %v", err))
		return
	}

	api.WriteSuccess(w, r, PipelineResponse{
		ID:       p.ID,
		Number:   p.Number,
		Status:   p.Status,
		Event:    p.Event,
		Branch:   p.Branch,
		Commit:   p.Commit,
		Message:  p.Message,
		Author:   p.Author,
		Started:  formatTime(p.Started),
		Finished: formatTime(p.Finished),
		Errors:   mapPipelineErrors(p.Errors),
	})
}
