package session

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

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/jackc/pgx/v5/pgxpool"
)

// SessionMiddleware extracts session from request and adds to context
func SessionMiddleware(pgPool *pgxpool.Pool) gin.HandlerFunc {
	store := GetSessionStore(pgPool)

	return func(c *gin.Context) {
		sessionID := extractSessionID(c)

		if sessionID != "" {
			session, err := store.GetSession(c.Request.Context(), sessionID)
			if err == nil && session != nil {
				// Add session to context
				c.Set("session", session)
				c.Set("session_id", session.SessionID)
				c.Set("user_id", session.UserID)
				c.Set("email", session.Email)
				c.Set("user_type", string(session.UserType))
				c.Set("roles", session.Roles)
				c.Set("permissions", session.Permissions)
				if session.TenantID != nil {
					c.Set("tenant_id", *session.TenantID)
				}
			}
		}

		c.Next()
	}
}

// extractSessionID extracts session ID from request
func extractSessionID(c *gin.Context) string {
	// Try Authorization header first
	authHeader := c.GetHeader("Authorization")
	if strings.HasPrefix(authHeader, "Bearer ") {
		return strings.TrimPrefix(authHeader, "Bearer ")
	}

	// Try X-Session-ID header
	if sessionID := c.GetHeader("X-Session-ID"); sessionID != "" {
		return sessionID
	}

	// Try cookie
	if cookie, err := c.Cookie("session_id"); err == nil {
		return cookie
	}

	return ""
}

// RequireSession requires a valid session
func RequireSession() gin.HandlerFunc {
	return func(c *gin.Context) {
		session := GetSession(c)

		if session == nil {
			// Check development bypass
			if os.Getenv("ENVIRONMENT") == "development" {
				// Set demo session
				demoSession := getDemoSession()
				c.Set("session", demoSession)
				c.Set("session_id", demoSession.SessionID)
				c.Set("user_id", demoSession.UserID)
				c.Set("email", demoSession.Email)
				c.Set("user_type", string(demoSession.UserType))
				c.Set("roles", demoSession.Roles)
				c.Set("permissions", demoSession.Permissions)
				c.Next()
				return
			}

			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error":   "unauthorized",
				"message": "Authentication required",
			})
			return
		}

		c.Next()
	}
}

// GetSession retrieves session from context
func GetSession(c *gin.Context) *Session {
	if session, exists := c.Get("session"); exists {
		if s, ok := session.(*Session); ok {
			return s
		}
	}
	return nil
}

// GetSessionUserID retrieves user ID from session context
func GetSessionUserID(c *gin.Context) (uuid.UUID, error) {
	userIDStr, exists := c.Get("user_id")
	if !exists {
		return uuid.Nil, nil
	}

	return uuid.Parse(userIDStr.(string))
}

// GetSessionEmail retrieves email from session context
func GetSessionEmail(c *gin.Context) string {
	email, exists := c.Get("email")
	if !exists {
		return ""
	}
	return email.(string)
}

// GetSessionUserType retrieves user type from session context
func GetSessionUserType(c *gin.Context) UserType {
	userType, exists := c.Get("user_type")
	if !exists {
		return UserTypeCustomer
	}
	return UserType(userType.(string))
}

// GetSessionRoles retrieves roles from session context
func GetSessionRoles(c *gin.Context) []string {
	roles, exists := c.Get("roles")
	if !exists {
		return nil
	}
	if r, ok := roles.([]string); ok {
		return r
	}
	return nil
}

// GetSessionPermissions retrieves permissions from session context
func GetSessionPermissions(c *gin.Context) []string {
	perms, exists := c.Get("permissions")
	if !exists {
		return nil
	}
	if p, ok := perms.([]string); ok {
		return p
	}
	return nil
}

// GetSessionTenantID retrieves tenant ID from session context
func GetSessionTenantID(c *gin.Context) *string {
	tenantID, exists := c.Get("tenant_id")
	if !exists {
		return nil
	}
	if t, ok := tenantID.(string); ok {
		return &t
	}
	return nil
}

// getDemoSession returns a demo session for development
func getDemoSession() *Session {
	tenantID := "a0000000-0000-0000-0000-000000000001"
	ip := "127.0.0.1"
	ua := "Development"

	return &Session{
		SessionID: "demo-session-id",
		UserID:    "10000000-0000-0000-0000-000000000024",
		Email:     "demo@breakpilot.app",
		UserType:  UserTypeEmployee,
		Roles: []string{
			"admin", "schul_admin", "teacher",
		},
		Permissions: []string{
			"grades:read", "grades:write",
			"attendance:read", "attendance:write",
			"students:read", "students:write",
			"reports:generate", "consent:admin",
			"own_data:read", "users:manage",
		},
		TenantID:       &tenantID,
		IPAddress:      &ip,
		UserAgent:      &ua,
		CreatedAt:      time.Now().UTC(),
		LastActivityAt: time.Now().UTC(),
	}
}
