package handlers

import (
	"log"
	"net/http"
	"strconv"

	"github.com/flatrender/render-svc/internal/db"
	"github.com/flatrender/render-svc/internal/identityclient"
	"github.com/flatrender/render-svc/internal/middleware"
	"github.com/flatrender/render-svc/internal/models"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

type RenderHandler struct {
	store    *db.Store
	identity *identityclient.Client
}

func NewRenderHandler(store *db.Store, identity *identityclient.Client) *RenderHandler {
	return &RenderHandler{store: store, identity: identity}
}

// GET /v1/renders
func (h *RenderHandler) List(c *gin.Context) {
	userID := middleware.GetUserID(c)
	status := c.Query("status")
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
	if page < 1 {
		page = 1
	}
	if pageSize < 1 || pageSize > 100 {
		pageSize = 20
	}

	jobs, total, err := h.store.ListJobs(c.Request.Context(), userID, status, page, pageSize)
	if err != nil {
		c.JSON(http.StatusInternalServerError, models.APIError{Code: "internal_error", Message: err.Error()})
		return
	}
	if jobs == nil {
		jobs = []*models.RenderJob{}
	}
	c.JSON(http.StatusOK, models.PagedResponse[*models.RenderJob]{
		Data: jobs,
		Meta: models.PaginationMeta{
			Page:     page,
			PageSize: pageSize,
			Total:    total,
			HasMore:  int64(page*pageSize) < total,
		},
	})
}

// GET /v1/renders/active
// Lightweight list of the user's in-flight renders + their ceiling — powers the
// app-wide mini progress widget and the "can I start another render?" check.
func (h *RenderHandler) Active(c *gin.Context) {
	userID := middleware.GetUserID(c)
	jobs, err := h.store.ListActiveJobs(c.Request.Context(), userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, models.APIError{Code: "internal_error", Message: err.Error()})
		return
	}
	if jobs == nil {
		jobs = []*models.RenderJob{}
	}
	out := make([]gin.H, 0, len(jobs))
	for _, j := range jobs {
		out = append(out, gin.H{
			"id":               j.ID,
			"saved_project_id": j.SavedProjectID,
			"name":             j.Name,
			"step":             j.Step,
			"render_progress":  j.RenderProgress,
			"preview_b64":      j.ImagePreviewB64,
			"created_at":       j.CreatedAt,
		})
	}
	maxRenders := middleware.GetMaxRenders(c)
	c.JSON(http.StatusOK, gin.H{
		"active":       out,
		"max_renders":  maxRenders,
		"can_start_new": len(out) < maxRenders,
	})
}

// POST /v1/renders
func (h *RenderHandler) Create(c *gin.Context) {
	userID := middleware.GetUserID(c)
	tenantID := middleware.GetTenantID(c)

	var req models.RenderJobCreateRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, models.APIError{Code: "bad_request", Message: err.Error()})
		return
	}

	// Concurrent-render ceiling: a user may run only `max_renders` renders at once
	// (default 1; raised by gamification level or an admin grant via the JWT claim).
	maxRenders := middleware.GetMaxRenders(c)
	active, err := h.store.CountActiveJobs(c.Request.Context(), userID)
	if err != nil {
		log.Printf("count active jobs failed (allowing render): %v", err)
	} else if active >= maxRenders {
		c.JSON(http.StatusConflict, models.APIError{
			Code:    "active_render_limit",
			Message: "شما یک رندر در حال انجام دارید. برای شروع رندر جدید صبر کنید تا رندر فعلی کامل شود.",
		})
		return
	}

	// Daily render-limit: consume one render charge (0 max = unlimited).
	allowed, err := h.identity.Consume(c.Request.Context(), userID)
	if err != nil {
		log.Printf("render-charge consume failed (allowing render): %v", err)
	}
	if !allowed {
		c.JSON(http.StatusTooManyRequests, models.APIError{
			Code: "daily_render_limit", Message: "سقف رندر روزانهٔ شما به پایان رسیده است.",
		})
		return
	}

	job, err := h.store.CreateJob(c.Request.Context(), userID, tenantID, &req)
	if err != nil {
		// Creation failed after consuming — return the charge.
		_ = h.identity.Refund(c.Request.Context(), userID)
		c.JSON(http.StatusInternalServerError, models.APIError{Code: "internal_error", Message: err.Error()})
		return
	}
	c.JSON(http.StatusCreated, job)
}

// GET /v1/renders/:job_id
func (h *RenderHandler) Get(c *gin.Context) {
	userID := middleware.GetUserID(c)
	jobID, err := uuid.Parse(c.Param("job_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, models.APIError{Code: "bad_request", Message: "invalid job_id"})
		return
	}
	job, err := h.store.GetJobByID(c.Request.Context(), jobID, userID)
	if err != nil {
		c.JSON(http.StatusNotFound, models.APIError{Code: "not_found", Message: err.Error()})
		return
	}
	frames, _ := h.store.ListFrameJobs(c.Request.Context(), jobID)
	if frames == nil {
		frames = []*models.FrameJob{}
	}
	c.JSON(http.StatusOK, gin.H{
		"id":               job.ID,
		"saved_project_id": job.SavedProjectID,
		"name":             job.Name,
		"step":             job.Step,
		"render_progress":  job.RenderProgress,
		"priority_queue":   job.PriorityQueue,
		"price_type":       job.PriceType,
		"paid_price_minor": job.PaidPriceMinor,
		"quality":          job.Quality,
		"resolution":       job.Resolution,
		"frame_rate":       job.FrameRate,
		"duration_sec":     job.DurationSec,
		"has_voiceover":    job.HasVoiceover,
		"image_preview_b64": job.ImagePreviewB64,
		"failed_message":   job.FailedMessage,
		"export_id":        job.ExportID,
		"queued_at":        job.QueuedAt,
		"started_at":       job.StartedAt,
		"completed_at":     job.CompletedAt,
		"retry_count":      job.RetryCount,
		"repair_attempts":  job.RepairAttempts,
		"frame_jobs":       frames,
	})
}

// POST /v1/renders/:job_id/cancel
func (h *RenderHandler) Cancel(c *gin.Context) {
	userID := middleware.GetUserID(c)
	jobID, err := uuid.Parse(c.Param("job_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, models.APIError{Code: "bad_request", Message: "invalid job_id"})
		return
	}
	cancelled, err := h.store.CancelJob(c.Request.Context(), jobID, userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, models.APIError{Code: "internal_error", Message: err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"cancelled":           cancelled,
		"refund_amount_minor": 0,
	})
}

// POST /v1/renders/:job_id/stop — admin: stop any user's in-progress job
func (h *RenderHandler) Stop(c *gin.Context) {
	jobID, err := uuid.Parse(c.Param("job_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, models.APIError{Code: "bad_request", Message: "invalid job_id"})
		return
	}
	stopped, ownerID, err := h.store.StopJob(c.Request.Context(), jobID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, models.APIError{Code: "internal_error", Message: err.Error()})
		return
	}
	// Admin stop → refund the user's render charge (not their fault).
	if stopped && ownerID != uuid.Nil {
		if err := h.identity.Refund(c.Request.Context(), ownerID); err != nil {
			log.Printf("render-charge refund failed for %s: %v", ownerID, err)
		}
	}
	c.JSON(http.StatusOK, gin.H{"stopped": stopped})
}

// POST /v1/renders/:job_id/retry
func (h *RenderHandler) Retry(c *gin.Context) {
	userID := middleware.GetUserID(c)
	tenantID := middleware.GetTenantID(c)
	jobID, err := uuid.Parse(c.Param("job_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, models.APIError{Code: "bad_request", Message: "invalid job_id"})
		return
	}
	original, err := h.store.GetJobByID(c.Request.Context(), jobID, userID)
	if err != nil {
		c.JSON(http.StatusNotFound, models.APIError{Code: "not_found", Message: err.Error()})
		return
	}
	// Create a new job cloning the original config
	req := &models.RenderJobCreateRequest{
		SavedProjectID: original.SavedProjectID,
		Quality:        original.Quality,
		Resolution:     original.Resolution,
		FrameRate:      &original.FrameRate,
	}
	newJob, err := h.store.CreateJob(c.Request.Context(), userID, tenantID, req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, models.APIError{Code: "internal_error", Message: err.Error()})
		return
	}
	c.JSON(http.StatusCreated, newJob)
}

// GET /v1/renders/:job_id/progress
func (h *RenderHandler) Progress(c *gin.Context) {
	userID := middleware.GetUserID(c)
	jobID, err := uuid.Parse(c.Param("job_id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, models.APIError{Code: "bad_request", Message: "invalid job_id"})
		return
	}
	job, err := h.store.GetJobByID(c.Request.Context(), jobID, userID)
	if err != nil {
		c.JSON(http.StatusNotFound, models.APIError{Code: "not_found", Message: err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"job_id":         job.ID,
		"step":           job.Step,
		"progress":       job.RenderProgress,
		"current_frame":  nil,
		"total_frames":   nil,
		"eta_seconds":    nil,
		"preview_b64":    job.ImagePreviewB64,
		"active_nodes":   job.CurrentActiveNodes,
		"message":        job.FailedMessage,
	})
}

// GET /v1/renders/:job_id/logs
func (h *RenderHandler) Logs(c *gin.Context) {
	// Logs are stored externally (MinIO/ELK). Return empty for now — node agents push logs elsewhere.
	c.JSON(http.StatusOK, gin.H{"logs": []any{}})
}
