package api

import (
	"net/http"
	"strings"

	"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
}

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

// 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"
	CreatedAt        int64  `json:"created_at" example:"1703505600"`     // Unix timestamp
	UpdatedAt        int64  `json:"updated_at" example:"1703505600"`     // Unix 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"`

	// 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"`
	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"`
}

// 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",
		})
		return
	}

	// 2. Try master key
	master, err := h.masterService.ValidateMasterKey(token)
	if err == nil && master != nil {
		c.JSON(http.StatusOK, 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(),
		})
		return
	}

	// 3. Try child key (API key) - lookup by token hash
	tokenHash := tokenhash.HashToken(token)

	// First try Redis cache
	if h.rdb != nil {
		keyData, err := h.rdb.HGetAll(c.Request.Context(), "auth:token:"+tokenHash).Result()
		if err == nil && len(keyData) > 0 && keyData["status"] == "active" {
			// Found in Redis, now get full data from DB
			var key model.Key
			if err := h.db.Where("token_hash = ?", tokenHash).First(&key).Error; err == nil {
				if key.Status == "active" {
					c.JSON(http.StatusOK, WhoamiResponse{
						Type:             "key",
						ID:               key.ID,
						MasterID:         key.MasterID,
						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),
						CreatedAt:        key.CreatedAt.UTC().Unix(),
						UpdatedAt:        key.UpdatedAt.UTC().Unix(),
					})
					return
				}
			}
		}
	}

	// Fallback: direct database lookup
	var key model.Key
	if err := h.db.Where("token_hash = ?", tokenHash).First(&key).Error; err == nil {
		if key.Status == "active" {
			c.JSON(http.StatusOK, WhoamiResponse{
				Type:             "key",
				ID:               key.ID,
				MasterID:         key.MasterID,
				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),
				CreatedAt:        key.CreatedAt.UTC().Unix(),
				UpdatedAt:        key.UpdatedAt.UTC().Unix(),
			})
			return
		}
	}

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