package api

import (
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/ez-api/ez-api/internal/model"
	"github.com/ez-api/ez-api/internal/service"
	"github.com/ez-api/foundation/tokenhash"
	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
	"gorm.io/gorm"
)

// AuthHandler handles authentication identity endpoints.
type AuthHandler struct {
	db            *gorm.DB
	rdb           *redis.Client
	adminService  *service.AdminService
	masterService *service.MasterService
	statsService  *service.StatsService
}

// NewAuthHandler creates a new AuthHandler.
func NewAuthHandler(db *gorm.DB, rdb *redis.Client, adminService *service.AdminService, masterService *service.MasterService, statsService *service.StatsService) *AuthHandler {
	return &AuthHandler{
		db:            db,
		rdb:           rdb,
		adminService:  adminService,
		masterService: masterService,
		statsService:  statsService,
	}
}

// WhoamiAdminResponse represents admin identity response.
type WhoamiAdminResponse struct {
	Type string `json:"type" example:"admin"` // Always "admin"
	Role string `json:"role" example:"admin"` // Admin role
}

// WhoamiMasterResponse represents master identity response.
type WhoamiMasterResponse struct {
	Type             string `json:"type" example:"master"`               // Always "master"
	ID               uint   `json:"id" example:"1"`                      // Master ID
	Name             string `json:"name" example:"tenant-a"`             // Master name
	Group            string `json:"group" example:"default"`             // Routing group
	DefaultNamespace string `json:"default_namespace" example:"default"` // Default namespace
	Namespaces       string `json:"namespaces" example:"default,ns1"`    // Comma-separated namespaces
	Status           string `json:"status" example:"active"`             // Status: active/suspended
	Epoch            int64  `json:"epoch" example:"1"`                   // Epoch for key revocation
	MaxChildKeys     int    `json:"max_child_keys" example:"5"`          // Max child keys allowed
	GlobalQPS        int    `json:"global_qps" example:"100"`            // Global QPS limit
	CreatedAt        int64  `json:"created_at" example:"1703505600"`     // Unix timestamp
	UpdatedAt        int64  `json:"updated_at" example:"1703505600"`     // Unix timestamp
}

// WhoamiKeyResponse represents child key identity response.
type WhoamiKeyResponse struct {
	Type             string `json:"type" example:"key"`                  // Always "key"
	ID               uint   `json:"id" example:"5"`                      // Key ID
	MasterID         uint   `json:"master_id" example:"1"`               // Parent master ID
	Group            string `json:"group" example:"default"`             // Routing group
	Scopes           string `json:"scopes" example:"chat:write"`         // Comma-separated scopes
	DefaultNamespace string `json:"default_namespace" example:"default"` // Default namespace
	Namespaces       string `json:"namespaces" example:"default"`        // Comma-separated namespaces
	Status           string `json:"status" example:"active"`             // Status: active/suspended
	IssuedAtEpoch    int64  `json:"issued_at_epoch" example:"1"`         // Master epoch when issued
	IssuedBy         string `json:"issued_by" example:"master"`          // "master" or "admin"
	AllowIPs         string `json:"allow_ips,omitempty" example:""`      // IP whitelist (for diagnostics)
	DenyIPs          string `json:"deny_ips,omitempty" example:""`       // IP blacklist (for diagnostics)
	CreatedAt        int64  `json:"created_at" example:"1703505600"`     // Unix timestamp
	UpdatedAt        int64  `json:"updated_at" example:"1703505600"`     // Unix timestamp
}

// WhoamiRealtimeView represents realtime statistics for the authenticated identity.
type WhoamiRealtimeView struct {
	Requests    int64  `json:"requests" example:"100"`                    // Total requests
	Tokens      int64  `json:"tokens" example:"50000"`                    // Total tokens used
	QPS         int64  `json:"qps" example:"5"`                           // Current QPS
	QPSLimit    int64  `json:"qps_limit" example:"100"`                   // QPS limit
	RateLimited bool   `json:"rate_limited" example:"false"`              // Whether currently rate limited
	UpdatedAt   *int64 `json:"updated_at,omitempty" example:"1703505600"` // Last updated timestamp
}

// WhoamiResponse is a union type for all identity responses.
// swagger:model WhoamiResponse
type WhoamiResponse struct {
	// Type of the authenticated identity: "admin", "master", or "key"
	Type string `json:"type" example:"master"`

	// Admin fields (only present when type is "admin")
	Role        string   `json:"role,omitempty" example:"admin"`
	Permissions []string `json:"permissions,omitempty"` // Admin permissions (always ["*"])

	// Master fields (only present when type is "master")
	ID               uint   `json:"id,omitempty" example:"1"`
	Name             string `json:"name,omitempty" example:"tenant-a"`
	Group            string `json:"group,omitempty" example:"default"`
	DefaultNamespace string `json:"default_namespace,omitempty" example:"default"`
	Namespaces       string `json:"namespaces,omitempty" example:"default,ns1"`
	Status           string `json:"status,omitempty" example:"active"`
	Epoch            int64  `json:"epoch,omitempty" example:"1"`
	MaxChildKeys     int    `json:"max_child_keys,omitempty" example:"5"`
	GlobalQPS        int    `json:"global_qps,omitempty" example:"100"`
	CreatedAt        int64  `json:"created_at,omitempty" example:"1703505600"`
	UpdatedAt        int64  `json:"updated_at,omitempty" example:"1703505600"`

	// Key fields (only present when type is "key")
	MasterID           uint   `json:"master_id,omitempty" example:"1"`
	MasterName         string `json:"master_name,omitempty" example:"tenant-a"` // Parent master name (for display)
	Scopes             string `json:"scopes,omitempty" example:"chat:write"`
	IssuedAtEpoch      int64  `json:"issued_at_epoch,omitempty" example:"1"`
	IssuedBy           string `json:"issued_by,omitempty" example:"master"`
	AllowIPs           string `json:"allow_ips,omitempty" example:""`                 // IP whitelist (for diagnostics)
	DenyIPs            string `json:"deny_ips,omitempty" example:""`                  // IP blacklist (for diagnostics)
	ExpiresAt          int64  `json:"expires_at,omitempty" example:"0"`               // Expiration timestamp (0 = never)
	ModelLimits        string `json:"model_limits,omitempty" example:"gpt-4,claude"`  // Model restrictions
	ModelLimitsEnabled bool   `json:"model_limits_enabled,omitempty" example:"false"` // Whether model limits are active
	QuotaLimit         int64  `json:"quota_limit,omitempty" example:"-1"`             // Token quota limit (-1 = unlimited)
	QuotaUsed          int64  `json:"quota_used,omitempty" example:"0"`               // Token quota used
	QuotaResetAt       int64  `json:"quota_reset_at,omitempty" example:"0"`           // Quota reset timestamp
	QuotaResetType     string `json:"quota_reset_type,omitempty" example:"monthly"`   // Quota reset type
	RequestCount       int64  `json:"request_count,omitempty" example:"0"`            // Total request count (from DB)
	UsedTokens         int64  `json:"used_tokens,omitempty" example:"0"`              // Total tokens used (from DB)
	LastAccessedAt     int64  `json:"last_accessed_at,omitempty" example:"0"`         // Last access timestamp

	// Realtime stats (for master and key types)
	Realtime *WhoamiRealtimeView `json:"realtime,omitempty"`
}

// Whoami godoc
// @Summary      Get current identity
// @Description  Returns the identity of the authenticated user based on the Authorization header.
// @Description  Supports Admin Token, Master Key, and Child Key (API Key) authentication.
// @Description
// @Description  Response varies by token type:
// @Description  - Admin Token: {"type": "admin", "role": "admin"}
// @Description  - Master Key: {"type": "master", "id": 1, "name": "...", ...}
// @Description  - Child Key: {"type": "key", "id": 5, "master_id": 1, "issued_by": "master", ...}
// @Tags         auth
// @Produce      json
// @Security     AdminAuth
// @Security     MasterAuth
// @Success      200  {object}  WhoamiResponse
// @Failure      401  {object}  gin.H "Invalid or missing token"
// @Router       /auth/whoami [get]
func (h *AuthHandler) Whoami(c *gin.Context) {
	authHeader := c.GetHeader("Authorization")
	if authHeader == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "authorization header required"})
		return
	}

	parts := strings.Split(authHeader, " ")
	if len(parts) != 2 || parts[0] != "Bearer" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid authorization header format"})
		return
	}

	token := parts[1]

	// 1. Try admin token first
	if h.adminService.ValidateToken(token) {
		c.JSON(http.StatusOK, WhoamiResponse{
			Type:        "admin",
			Role:        "admin",
			Permissions: []string{"*"}, // Admin has all permissions
		})
		return
	}

	// 2. Try master key
	master, err := h.masterService.ValidateMasterKey(token)
	if err == nil && master != nil {
		resp := WhoamiResponse{
			Type:             "master",
			ID:               master.ID,
			Name:             master.Name,
			Group:            master.Group,
			DefaultNamespace: strings.TrimSpace(master.DefaultNamespace),
			Namespaces:       strings.TrimSpace(master.Namespaces),
			Status:           master.Status,
			Epoch:            master.Epoch,
			MaxChildKeys:     master.MaxChildKeys,
			GlobalQPS:        master.GlobalQPS,
			CreatedAt:        master.CreatedAt.UTC().Unix(),
			UpdatedAt:        master.UpdatedAt.UTC().Unix(),
		}

		// Get realtime stats for master
		if h.statsService != nil {
			if stats, err := h.statsService.GetMasterRealtimeSnapshot(c.Request.Context(), master.ID); err == nil {
				if stats.QPSLimit == 0 && master.GlobalQPS > 0 {
					stats.QPSLimit = int64(master.GlobalQPS)
				}
				var updatedAt *int64
				if stats.UpdatedAt != nil {
					sec := stats.UpdatedAt.Unix()
					updatedAt = &sec
				}
				resp.Realtime = &WhoamiRealtimeView{
					Requests:    stats.Requests,
					Tokens:      stats.Tokens,
					QPS:         stats.QPS,
					QPSLimit:    stats.QPSLimit,
					RateLimited: stats.RateLimited,
					UpdatedAt:   updatedAt,
				}
			}
		}

		c.JSON(http.StatusOK, resp)
		return
	}

	// 3. Try child key (API key) - validate using Redis first (consistent with balancer)
	tokenHash := tokenhash.HashToken(token)

	// Validate token using Redis (same logic as balancer)
	if h.rdb != nil {
		ctx := c.Request.Context()

		// Get token metadata from Redis
		keyData, err := h.rdb.HGetAll(ctx, "auth:token:"+tokenHash).Result()
		if err != nil || len(keyData) == 0 {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
			return
		}

		// Check token status
		tokenStatus := strings.ToLower(keyData["status"])
		if tokenStatus != "" && tokenStatus != "active" {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "token is not active"})
			return
		}

		// Get master ID and issued_at_epoch
		masterIDStr := strings.TrimSpace(keyData["master_id"])
		masterID, err := strconv.ParseUint(masterIDStr, 10, 64)
		if err != nil {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token metadata"})
			return
		}

		issuedAtStr := strings.TrimSpace(keyData["issued_at_epoch"])
		issuedAtEpoch, err := strconv.ParseInt(issuedAtStr, 10, 64)
		if err != nil {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token metadata"})
			return
		}

		// Get master metadata from Redis
		masterData, err := h.rdb.HGetAll(ctx, "auth:master:"+masterIDStr).Result()
		if err != nil || len(masterData) == 0 {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "master not found"})
			return
		}

		// Check master status
		masterStatus := strings.ToLower(masterData["status"])
		if masterStatus != "" && masterStatus != "active" {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "master is not active"})
			return
		}

		// Check epoch (key revocation)
		masterEpochStr := strings.TrimSpace(masterData["epoch"])
		masterEpoch, err := strconv.ParseInt(masterEpochStr, 10, 64)
		if err != nil {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid master metadata"})
			return
		}
		if issuedAtEpoch < masterEpoch {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "token has been revoked"})
			return
		}

		// Check expiration
		expiresAt := int64(0)
		expiresAtStr := strings.TrimSpace(keyData["expires_at"])
		if expiresAtStr != "" {
			expiresAt, err = strconv.ParseInt(expiresAtStr, 10, 64)
			if err != nil {
				c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token metadata"})
				return
			}
		}
		if expiresAt > 0 && time.Now().Unix() >= expiresAt {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "token has expired"})
			return
		}

		// Token is valid, now get full data from DB for response
		var key model.Key
		if err := h.db.Where("token_hash = ?", tokenHash).First(&key).Error; err == nil {
			// Get master name for display
			var master model.Master
			masterName := ""
			if err := h.db.First(&master, masterID).Error; err == nil {
				masterName = master.Name
			}

			// Convert nullable time fields
			var quotaResetAt int64
			if key.QuotaResetAt != nil {
				quotaResetAt = key.QuotaResetAt.UTC().Unix()
			}
			var lastAccessedAt int64
			if key.LastAccessedAt != nil {
				lastAccessedAt = key.LastAccessedAt.UTC().Unix()
			}

			resp := WhoamiResponse{
				Type:               "key",
				ID:                 key.ID,
				MasterID:           uint(masterID),
				MasterName:         masterName,
				Group:              key.Group,
				Scopes:             strings.TrimSpace(key.Scopes),
				DefaultNamespace:   strings.TrimSpace(key.DefaultNamespace),
				Namespaces:         strings.TrimSpace(key.Namespaces),
				Status:             key.Status,
				IssuedAtEpoch:      key.IssuedAtEpoch,
				IssuedBy:           strings.TrimSpace(key.IssuedBy),
				AllowIPs:           strings.TrimSpace(key.AllowIPs),
				DenyIPs:            strings.TrimSpace(key.DenyIPs),
				ExpiresAt:          expiresAt,
				ModelLimits:        strings.TrimSpace(key.ModelLimits),
				ModelLimitsEnabled: key.ModelLimitsEnabled,
				QuotaLimit:         key.QuotaLimit,
				QuotaUsed:          key.QuotaUsed,
				QuotaResetAt:       quotaResetAt,
				QuotaResetType:     strings.TrimSpace(key.QuotaResetType),
				RequestCount:       key.RequestCount,
				UsedTokens:         key.UsedTokens,
				LastAccessedAt:     lastAccessedAt,
				CreatedAt:          key.CreatedAt.UTC().Unix(),
				UpdatedAt:          key.UpdatedAt.UTC().Unix(),
			}

			// Get realtime stats for the key's master
			if h.statsService != nil {
				if stats, err := h.statsService.GetMasterRealtimeSnapshot(c.Request.Context(), uint(masterID)); err == nil {
					if stats.QPSLimit == 0 && master.GlobalQPS > 0 {
						stats.QPSLimit = int64(master.GlobalQPS)
					}
					var updatedAt *int64
					if stats.UpdatedAt != nil {
						sec := stats.UpdatedAt.Unix()
						updatedAt = &sec
					}
					resp.Realtime = &WhoamiRealtimeView{
						Requests:    stats.Requests,
						Tokens:      stats.Tokens,
						QPS:         stats.QPS,
						QPSLimit:    stats.QPSLimit,
						RateLimited: stats.RateLimited,
						UpdatedAt:   updatedAt,
					}
				}
			}

			c.JSON(http.StatusOK, resp)
			return
		}
	}

	c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
}
