package api

import (
	"encoding/json"
	"io"
	"log"
	"net/http"

	"gitea.hostxtra.co.uk/mrhid6/vantage/admin/internal/billing"
	"gitea.hostxtra.co.uk/mrhid6/vantage/admin/internal/config"
	"gitea.hostxtra.co.uk/mrhid6/vantage/admin/internal/models"
	"gitea.hostxtra.co.uk/mrhid6/vantage/admin/internal/paddle"
	"github.com/gin-gonic/gin"
)

// paddleWebhook is the ingress for every Paddle event.
//
// Order is load-bearing: read the RAW body first (the signature is over the
// exact bytes), verify, THEN claim the event ID, THEN dispatch. A bad signature
// is 401 and processes nothing; a duplicate of a handled event is 200 and does
// nothing; a handler error is 500 so Paddle retries, and is recorded for staff.
func paddleWebhook(cfg config.Config) gin.HandlerFunc {
	return func(c *gin.Context) {
		body, err := io.ReadAll(c.Request.Body)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "unreadable body"})
			return
		}
		if !paddle.VerifySignature(cfg.PaddleWebhookSecret,
			c.GetHeader("Paddle-Signature"), body) {
			log.Printf("paddle webhook: bad signature from %s", c.ClientIP())
			c.JSON(http.StatusUnauthorized, gin.H{"error": "bad signature"})
			return
		}

		var ev billing.Event
		if err := json.Unmarshal(body, &ev); err != nil || ev.EventID == "" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "malformed event"})
			return
		}

		ctx := c.Request.Context()
		claimed, err := models.ClaimEvent(ctx, ev.EventID, ev.EventType)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "claim failed"})
			return
		}
		if !claimed {
			// Already handled (or in flight). 200 so Paddle stops retrying.
			c.JSON(http.StatusOK, gin.H{"duplicate": true})
			return
		}

		if err := billing.Dispatch(ctx, ev); err != nil {
			log.Printf("paddle webhook: handler %s failed for %s: %v",
				ev.EventType, ev.EventID, err)
			_ = models.MarkEventProcessed(ctx, ev.EventID, err)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "handler failed"})
			return
		}
		_ = models.MarkEventProcessed(ctx, ev.EventID, nil)
		c.JSON(http.StatusOK, gin.H{"ok": true})
	}
}
