package middleware

import (
	"netidhub-saas-be/app/database/entity"
	"netidhub-saas-be/utils/client"
	"strings"

	"github.com/gofiber/fiber/v2"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

const (
	ClientKeyHeader        = "X-Client-Key"
	ClientContextKey       = "client_id"
	UserIDContextKey       = "user_id"
	IsSuperAdminContextKey = "is_super_admin"
	AccessibleClientIDsKey = "accessible_client_ids"
	CurrentClientIDKey     = "current_client_id"
)

// excludedPaths contains paths that don't require client key validation
var excludedPaths = []string{
	"/swagger/*",
	"/docs/*",
	"/users/login",
	"/health/*",
	"/clients",
	"/clients/*",
	"*/viewer/*",
	"/bookmarks/test-table",
}

// isPathExcluded checks if the given path should be excluded from client key validation
func isPathExcluded(path string) bool {
	for _, excludedPath := range excludedPaths {
		if strings.HasPrefix(excludedPath, "*") && strings.HasSuffix(excludedPath, "*") {
			// Handle wildcard at both beginning and end (e.g., "*/viewer/*")
			pattern := excludedPath[1 : len(excludedPath)-1] // Remove * from both ends
			if strings.Contains(path, pattern) {
				return true
			}
		} else if strings.HasPrefix(excludedPath, "*") {
			// Handle wildcard at the beginning
			if strings.HasSuffix(path, excludedPath[1:]) {
				return true
			}
		} else if strings.HasSuffix(excludedPath, "*") {
			// Handle wildcard at the end
			prefix := excludedPath[:len(excludedPath)-1]
			if strings.HasPrefix(path, prefix) {
				return true
			}
		} else {
			// Exact match
			if path == excludedPath {
				return true
			}
		}
	}
	return false
}

// ClientMiddleware extracts and validates the Client Key from request headers
// Enhanced to support multi-client access and super admin
func ClientMiddleware(db *gorm.DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		// Check if path should be excluded from client key validation
		if isPathExcluded(c.Path()) {
			return c.Next()
		}

		// Check if user ID exists in context (set by auth middleware)
		userID := c.Locals(UserIDContextKey)
		if userID != nil {
			// User authenticated - use multi-client logic
			return handleAuthenticatedUser(c, db, userID)
		}

		// Fallback to X-Client-Key validation (backward compatibility)
		return handleClientKeyValidation(c, db)
	}
}

// handleAuthenticatedUser handles request from authenticated users
func handleAuthenticatedUser(c *fiber.Ctx, db *gorm.DB, userID interface{}) error {
	userId, ok := userID.(uint)
	if !ok {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
			"success":  false,
			"code":     401,
			"messages": []string{"Invalid user ID in context"},
		})
	}

	// Get user details
	var user entity.Users
	if err := db.Select("id, is_super_admin, client_id").
		Where("id = ?", userId).
		First(&user).Error; err != nil {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
			"success":  false,
			"code":     401,
			"messages": []string{"User not found"},
		})
	}

	// Store super admin status
	isSuperAdmin := user.IsSuperAdmin != nil && *user.IsSuperAdmin
	c.Locals(IsSuperAdminContextKey, isSuperAdmin)

	// Get accessible client IDs for this user
	accessibleClientIDs, err := client.GetAccessibleClientIDs(db, userId, isSuperAdmin)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success":  false,
			"code":     500,
			"messages": []string{"Error retrieving client access"},
		})
	}

	// Store accessible client IDs in context
	c.Locals(AccessibleClientIDsKey, accessibleClientIDs)

	// Determine current client ID (from header or user's primary client)
	var currentClientID *uuid.UUID
	clientKeyHeader := c.Get(ClientKeyHeader)
	
	if clientKeyHeader != "" {
		// User specified a client via header
		clientUUID, err := uuid.Parse(clientKeyHeader)
		if err != nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
				"success":  false,
				"code":     400,
				"messages": []string{"Invalid Client Key format"},
			})
		}

		// Verify user has access to this client
		hasAccess, err := client.HasAccessToClient(db, userId, clientUUID, isSuperAdmin)
		if err != nil || !hasAccess {
			return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
				"success":  false,
				"code":     403,
				"messages": []string{"Access denied to this client"},
			})
		}

		currentClientID = &clientUUID
	} else if user.ClientId != nil {
		// Use user's primary client
		currentClientID = user.ClientId
	}

	// Store current client ID
	if currentClientID != nil {
		c.Locals(CurrentClientIDKey, *currentClientID)
		c.Locals(ClientContextKey, *currentClientID) // Backward compatibility
	}

	return c.Next()
}

// handleClientKeyValidation validates X-Client-Key header (backward compatibility)
func handleClientKeyValidation(c *fiber.Ctx, db *gorm.DB) error {
	clientKey := c.Get(ClientKeyHeader)

	if clientKey == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success":  false,
			"code":     400,
			"messages": []string{"Client Key is required in header: " + ClientKeyHeader},
		})
	}

	clientUUID, err := uuid.Parse(clientKey)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success":  false,
			"code":     400,
			"messages": []string{"Invalid Client Key format"},
		})
	}

	var clientEntity entity.Clients
	if err := db.Where("id = ? AND is_active = ?", clientUUID, true).First(&clientEntity).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
				"success":  false,
				"code":     401,
				"messages": []string{"Invalid or inactive Client Key"},
			})
		}
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"success":  false,
			"code":     500,
			"messages": []string{"Error validating Client Key"},
		})
	}

	c.Locals(ClientContextKey, clientUUID)
	c.Locals(CurrentClientIDKey, clientUUID)
	
	return c.Next()
}

// GetClientID retrieves the client ID from the context (backward compatibility)
func GetClientID(c *fiber.Ctx) *uuid.UUID {
	if clientID, ok := c.Locals(ClientContextKey).(uuid.UUID); ok {
		return &clientID
	}
	return nil
}

// GetAccessibleClientIDs retrieves all accessible client IDs from context
func GetAccessibleClientIDs(c *fiber.Ctx) []uuid.UUID {
	if clientIDs, ok := c.Locals(AccessibleClientIDsKey).([]uuid.UUID); ok {
		return clientIDs
	}
	return nil // nil = super admin or no restriction
}

// GetCurrentClientID retrieves the current working client ID from context
func GetCurrentClientID(c *fiber.Ctx) *uuid.UUID {
	if clientID, ok := c.Locals(CurrentClientIDKey).(uuid.UUID); ok {
		return &clientID
	}
	return nil
}

// IsSuperAdmin checks if the current user is a super admin
func IsSuperAdmin(c *fiber.Ctx) bool {
	if isSuperAdmin, ok := c.Locals(IsSuperAdminContextKey).(bool); ok {
		return isSuperAdmin
	}
	return false
}

// AddExcludedPath adds a new path to the excluded paths list
func AddExcludedPath(path string) {
	excludedPaths = append(excludedPaths, path)
}

// GetExcludedPaths returns the current list of excluded paths
func GetExcludedPaths() []string {
	return excludedPaths
}
