package handlers

import (
	"net/http"

	"github.com/gin-gonic/gin"

	"switchboard-core/models"
	"switchboard-core/store"
)

// ExtPermHandler serves extension permission management endpoints.
// v0.29.0 CS2: Admin reviews and grants/revokes declared permissions.
type ExtPermHandler struct {
	stores store.Stores
}

func NewExtPermHandler(stores store.Stores) *ExtPermHandler {
	return &ExtPermHandler{stores: stores}
}

// ListPackagePermissions returns all declared permissions for a package.
// GET /api/v1/admin/extensions/:id/permissions
func (h *ExtPermHandler) ListPackagePermissions(c *gin.Context) {
	pkgID := c.Param("id")

	perms, err := h.stores.ExtPermissions.ListForPackage(c.Request.Context(), pkgID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list permissions"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"data": perms})
}

// GrantPermission grants a single declared permission for a package.
// POST /api/v1/admin/extensions/:id/permissions/:perm/grant
func (h *ExtPermHandler) GrantPermission(c *gin.Context) {
	pkgID := c.Param("id")
	perm := c.Param("perm")
	userID := c.GetString("user_id")

	if !models.ValidExtensionPermissions[perm] {
		c.JSON(http.StatusBadRequest, gin.H{"error": "unrecognized permission: " + perm})
		return
	}

	if err := h.stores.ExtPermissions.Grant(c.Request.Context(), pkgID, perm, userID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to grant permission"})
		return
	}

	// Check if all permissions are now granted → activate package
	h.maybeActivate(c, pkgID)

	c.JSON(http.StatusOK, gin.H{"status": "granted", "package_id": pkgID, "permission": perm})
}

// RevokePermission revokes a single permission for a package.
// POST /api/v1/admin/extensions/:id/permissions/:perm/revoke
func (h *ExtPermHandler) RevokePermission(c *gin.Context) {
	pkgID := c.Param("id")
	perm := c.Param("perm")

	if err := h.stores.ExtPermissions.Revoke(c.Request.Context(), pkgID, perm); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to revoke permission"})
		return
	}

	// Suspend if a previously-active package loses a grant
	h.maybeSuspend(c, pkgID)

	c.JSON(http.StatusOK, gin.H{"status": "revoked", "package_id": pkgID, "permission": perm})
}

// GrantAllPermissions grants all declared permissions for a package.
// POST /api/v1/admin/extensions/:id/permissions/grant-all
func (h *ExtPermHandler) GrantAllPermissions(c *gin.Context) {
	pkgID := c.Param("id")
	userID := c.GetString("user_id")

	if err := h.stores.ExtPermissions.GrantAll(c.Request.Context(), pkgID, userID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to grant all permissions"})
		return
	}

	// Activate the package now that all permissions are granted
	h.maybeActivate(c, pkgID)

	c.JSON(http.StatusOK, gin.H{"status": "granted_all", "package_id": pkgID})
}

// ReviewPackage returns the package with its declared permissions.
// GET /api/v1/admin/extensions/:id/review
func (h *ExtPermHandler) ReviewPackage(c *gin.Context) {
	pkgID := c.Param("id")

	pkg, err := h.stores.Packages.Get(c.Request.Context(), pkgID)
	if err != nil || pkg == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "package not found"})
		return
	}

	perms, err := h.stores.ExtPermissions.ListForPackage(c.Request.Context(), pkgID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list permissions"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"package":     pkg,
		"permissions": perms,
	})
}

// ── Lifecycle helpers ────────────────────────

// maybeActivate transitions a pending_review package to active
// if all declared permissions have been granted.
func (h *ExtPermHandler) maybeActivate(c *gin.Context, pkgID string) {
	pkg, err := h.stores.Packages.Get(c.Request.Context(), pkgID)
	if err != nil || pkg == nil {
		return
	}
	if pkg.Status != models.PackageStatusPendingReview {
		return
	}

	perms, err := h.stores.ExtPermissions.ListForPackage(c.Request.Context(), pkgID)
	if err != nil {
		return
	}

	allGranted := true
	for _, p := range perms {
		if !p.Granted {
			allGranted = false
			break
		}
	}

	if allGranted {
		_ = h.stores.Packages.SetStatus(c.Request.Context(), pkgID, models.PackageStatusActive)
	}
}

// maybeSuspend transitions an active package to suspended
// if a required permission was revoked.
func (h *ExtPermHandler) maybeSuspend(c *gin.Context, pkgID string) {
	pkg, err := h.stores.Packages.Get(c.Request.Context(), pkgID)
	if err != nil || pkg == nil {
		return
	}
	if pkg.Status != models.PackageStatusActive {
		return
	}

	perms, err := h.stores.ExtPermissions.ListForPackage(c.Request.Context(), pkgID)
	if err != nil || len(perms) == 0 {
		return
	}

	for _, p := range perms {
		if !p.Granted {
			_ = h.stores.Packages.SetStatus(c.Request.Context(), pkgID, models.PackageStatusSuspended)
			return
		}
	}
}

// ── Manifest Permission Parsing ──────────────

// SyncManifestPermissions extracts the "permissions" array from a
// package manifest and declares them in the store. Returns the list
// of declared permissions. If the manifest declares permissions,
// the package status is set to pending_review.
//
// Called by AdminInstallExtension and InstallPackage handlers.
func SyncManifestPermissions(c *gin.Context, stores store.Stores, pkgID string, manifest map[string]any) []string {
	if stores.ExtPermissions == nil {
		return nil
	}

	raw, ok := manifest["permissions"]
	if !ok {
		return nil
	}

	arr, ok := raw.([]interface{})
	if !ok {
		return nil
	}

	var perms []string
	for _, v := range arr {
		if s, ok := v.(string); ok && models.ValidExtensionPermissions[s] {
			perms = append(perms, s)
		}
	}

	if len(perms) == 0 {
		return nil
	}

	if err := stores.ExtPermissions.DeclareForPackage(c.Request.Context(), pkgID, perms); err != nil {
		return nil
	}

	// Package needs review before activation
	_ = stores.Packages.SetStatus(c.Request.Context(), pkgID, models.PackageStatusPendingReview)

	return perms
}
