package connectors

import (
	"context"
	"errors"
	"log"
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"

	"github.com/pingcap-inc/nexuscrm-connectors/features/connector/ingressreceipts"
	"github.com/pingcap-inc/nexuscrm-connectors/interfaces/rest/internal/hostctx"
)

type ConnectorIngressReceiptsAdminService interface {
	ListMetadata(ctx context.Context, perms connector_ingress_receipts.PermissionChecker, filter connector_ingress_receipts.ReceiptMetadataFilter) ([]connector_ingress_receipts.ReceiptMetadata, error)
	GetMetadata(ctx context.Context, perms connector_ingress_receipts.PermissionChecker, id string) (*connector_ingress_receipts.ReceiptMetadata, error)
	PurgeTerminal(ctx context.Context, perms connector_ingress_receipts.PermissionChecker, req connector_ingress_receipts.PurgeRequest) (connector_ingress_receipts.PurgeResult, error)
}

type connectorIngressReceiptAdminResponse struct {
	ID                     string     `json:"id"`
	Namespace              string     `json:"namespace"`
	InstalledPackID        string     `json:"installedPackId"`
	PackVersion            string     `json:"packVersion,omitempty"`
	SecretName             string     `json:"secretName"`
	SecretAlgorithm        string     `json:"secretAlgorithm"`
	VerifierCapabilityKind string     `json:"verifierCapabilityKind"`
	ParserCapabilityKind   string     `json:"parserCapabilityKind"`
	RequestMethod          string     `json:"requestMethod"`
	RequestTargetHash      string     `json:"requestTargetHash"`
	BodyHash               string     `json:"bodyHash"`
	BodySizeBytes          int64      `json:"bodySizeBytes"`
	Status                 string     `json:"status"`
	AttemptCount           int        `json:"attemptCount"`
	NextAttemptAt          *time.Time `json:"nextAttemptAt,omitempty"`
	ClaimedBy              string     `json:"claimedBy,omitempty"`
	ClaimedAt              *time.Time `json:"claimedAt,omitempty"`
	LeaseExpiresAt         *time.Time `json:"leaseExpiresAt,omitempty"`
	EnqueuedEventIDs       []string   `json:"enqueuedEventIds,omitempty"`
	LastErrorClass         string     `json:"lastErrorClass,omitempty"`
	LastErrorAt            *time.Time `json:"lastErrorAt,omitempty"`
	PurgeAfter             *time.Time `json:"purgeAfter,omitempty"`
	CreatedDate            time.Time  `json:"createdDate"`
	LastModifiedDate       time.Time  `json:"lastModifiedDate"`
}

type connectorIngressReceiptPurgeRequest struct {
	Before    string `json:"before"`
	Namespace string `json:"namespace"`
	Limit     int    `json:"limit"`
}

type ConnectorIngressReceiptsAdminHandler struct {
	svc ConnectorIngressReceiptsAdminService
}

type connectorIngressReceiptSystemPerms map[string]bool

func (p connectorIngressReceiptSystemPerms) Has(key string) bool {
	return p[key]
}

func NewConnectorIngressReceiptsAdminHandler(svc ConnectorIngressReceiptsAdminService) *ConnectorIngressReceiptsAdminHandler {
	return &ConnectorIngressReceiptsAdminHandler{svc: svc}
}

func (h *ConnectorIngressReceiptsAdminHandler) ListReceipts(c *gin.Context) {
	limit, ok := parsePositiveLimit(c, "limit", 50, 500)
	if !ok {
		return
	}
	offset, ok := parseOptionalNonNegativeInt(c, "offset", 0)
	if !ok {
		return
	}
	rows, err := h.svc.ListMetadata(c.Request.Context(), connectorIngressReceiptSystemPermissionsFromContext(c), connector_ingress_receipts.ReceiptMetadataFilter{
		Namespace: c.Query("namespace"),
		Status:    c.Query("status"),
		Page: connector_ingress_receipts.Page{
			Limit:  limit,
			Offset: offset,
		},
	})
	if err != nil {
		respondConnectorIngressReceiptAdminError(c, "ListReceipts", "", err)
		return
	}
	out := make([]connectorIngressReceiptAdminResponse, 0, len(rows))
	for _, row := range rows {
		out = append(out, connectorIngressReceiptToAdminResponse(row))
	}
	c.JSON(http.StatusOK, gin.H{
		"receipts": out,
		"count":    len(out),
		"limit":    limit,
		"offset":   offset,
	})
}

func (h *ConnectorIngressReceiptsAdminHandler) GetReceipt(c *gin.Context) {
	receipt, err := h.svc.GetMetadata(c.Request.Context(), connectorIngressReceiptSystemPermissionsFromContext(c), c.Param("id"))
	if err != nil {
		respondConnectorIngressReceiptAdminError(c, "GetReceipt", c.Param("id"), err)
		return
	}
	c.JSON(http.StatusOK, gin.H{"receipt": connectorIngressReceiptToAdminResponse(*receipt)})
}

func (h *ConnectorIngressReceiptsAdminHandler) PurgeReceipts(c *gin.Context) {
	var body connectorIngressReceiptPurgeRequest
	if c.Request.Body != nil && c.Request.ContentLength != 0 {
		if err := c.ShouldBindJSON(&body); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "invalid purge request"})
			return
		}
	}
	before := time.Time{}
	if strings.TrimSpace(body.Before) != "" {
		parsed, err := time.Parse(time.RFC3339Nano, strings.TrimSpace(body.Before))
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "invalid before"})
			return
		}
		before = parsed
	}
	result, err := h.svc.PurgeTerminal(c.Request.Context(), connectorIngressReceiptSystemPermissionsFromContext(c), connector_ingress_receipts.PurgeRequest{
		Before:    before,
		Namespace: body.Namespace,
		Limit:     body.Limit,
	})
	if err != nil {
		respondConnectorIngressReceiptAdminError(c, "PurgeReceipts", body.Namespace, err)
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"deleted": result.Deleted,
		"before":  result.Before,
	})
}

func (h *ConnectorIngressReceiptsAdminHandler) RegisterReadRoutes(group *gin.RouterGroup) {
	group.GET("/ingress-receipts", h.ListReceipts)
	group.GET("/ingress-receipts/:id", h.GetReceipt)
}

func (h *ConnectorIngressReceiptsAdminHandler) RegisterPurgeRoutes(group *gin.RouterGroup) {
	group.POST("/ingress-receipts/purge", h.PurgeReceipts)
}

func connectorIngressReceiptToAdminResponse(row connector_ingress_receipts.ReceiptMetadata) connectorIngressReceiptAdminResponse {
	return connectorIngressReceiptAdminResponse{
		ID:                     row.ID,
		Namespace:              row.Namespace,
		InstalledPackID:        row.InstalledPackID,
		PackVersion:            row.PackVersion,
		SecretName:             row.SecretName,
		SecretAlgorithm:        row.SecretAlgorithm,
		VerifierCapabilityKind: row.VerifierCapabilityKind,
		ParserCapabilityKind:   row.ParserCapabilityKind,
		RequestMethod:          row.RequestMethod,
		RequestTargetHash:      row.RequestTargetHash,
		BodyHash:               row.BodyHash,
		BodySizeBytes:          row.BodySizeBytes,
		Status:                 row.Status,
		AttemptCount:           row.AttemptCount,
		NextAttemptAt:          row.NextAttemptAt,
		ClaimedBy:              row.ClaimedBy,
		ClaimedAt:              row.ClaimedAt,
		LeaseExpiresAt:         row.LeaseExpiresAt,
		EnqueuedEventIDs:       row.EnqueuedEventIDs,
		LastErrorClass:         row.LastErrorClass,
		LastErrorAt:            row.LastErrorAt,
		PurgeAfter:             row.PurgeAfter,
		CreatedDate:            row.CreatedDate,
		LastModifiedDate:       row.LastModifiedDate,
	}
}

func connectorIngressReceiptSystemPermissionsFromContext(c *gin.Context) connector_ingress_receipts.PermissionChecker {
	value, ok := c.Get(hostctx.ContextKeySystemPermissions)
	if !ok {
		return connectorIngressReceiptSystemPerms{}
	}
	perms, ok := value.(map[string]bool)
	if !ok || perms == nil {
		return connectorIngressReceiptSystemPerms{}
	}
	return connectorIngressReceiptSystemPerms(perms)
}

func respondConnectorIngressReceiptAdminError(c *gin.Context, operation, target string, err error) bool {
	switch {
	case errors.Is(err, connector_ingress_receipts.ErrPermissionDenied):
		c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"})
		return true
	case errors.Is(err, connector_ingress_receipts.ErrReceiptNotFound):
		c.JSON(http.StatusNotFound, gin.H{"error": "connector ingress receipt not found"})
		return true
	case errors.Is(err, connector_ingress_receipts.ErrInvalidReceipt):
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid connector ingress receipt request"})
		return true
	default:
		log.Printf("connector ingress receipts admin: %s target=%q internal error: %v", operation, target, err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
		return true
	}
}
