package session

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

// Employee permissions
var EmployeePermissions = []string{
	"grades:read", "grades:write",
	"attendance:read", "attendance:write",
	"students:read", "students:write",
	"reports:generate", "consent:admin",
	"corrections:read", "corrections:write",
	"classes:read", "classes:write",
	"timetable:read", "timetable:write",
	"substitutions:read", "substitutions:write",
	"parent_communication:read", "parent_communication:write",
}

// Customer permissions
var CustomerPermissions = []string{
	"own_data:read", "own_grades:read", "own_attendance:read",
	"consent:manage",
	"meetings:join",
	"messages:read", "messages:write",
	"children:read", "children:grades:read", "children:attendance:read",
}

// Admin permissions
var AdminPermissions = []string{
	"users:read", "users:write", "users:manage",
	"rbac:read", "rbac:write",
	"audit:read",
	"settings:read", "settings:write",
	"dsr:read", "dsr:process",
}

// Employee roles
var EmployeeRoles = map[string]bool{
	"admin":                   true,
	"schul_admin":             true,
	"schulleitung":            true,
	"pruefungsvorsitz":        true,
	"klassenlehrer":           true,
	"fachlehrer":              true,
	"sekretariat":             true,
	"erstkorrektor":           true,
	"zweitkorrektor":          true,
	"drittkorrektor":          true,
	"teacher_assistant":       true,
	"teacher":                 true,
	"lehrer":                  true,
	"data_protection_officer": true,
}

// Customer roles
var CustomerRoles = map[string]bool{
	"parent":   true,
	"student":  true,
	"user":     true,
	"guardian": true,
}

// RequireEmployee requires the user to be an employee
func RequireEmployee() gin.HandlerFunc {
	return func(c *gin.Context) {
		session := GetSession(c)
		if session == nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error":   "unauthorized",
				"message": "Authentication required",
			})
			return
		}

		if !session.IsEmployee() {
			c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
				"error":   "forbidden",
				"message": "Employee access required",
			})
			return
		}

		c.Next()
	}
}

// RequireCustomer requires the user to be a customer
func RequireCustomer() gin.HandlerFunc {
	return func(c *gin.Context) {
		session := GetSession(c)
		if session == nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error":   "unauthorized",
				"message": "Authentication required",
			})
			return
		}

		if !session.IsCustomer() {
			c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
				"error":   "forbidden",
				"message": "Customer access required",
			})
			return
		}

		c.Next()
	}
}

// RequireUserType requires a specific user type
func RequireUserType(userType UserType) gin.HandlerFunc {
	return func(c *gin.Context) {
		session := GetSession(c)
		if session == nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error":   "unauthorized",
				"message": "Authentication required",
			})
			return
		}

		if session.UserType != userType {
			c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
				"error":   "forbidden",
				"message": "User type '" + string(userType) + "' required",
			})
			return
		}

		c.Next()
	}
}

// RequirePermission requires a specific permission
func RequirePermission(permission string) gin.HandlerFunc {
	return func(c *gin.Context) {
		session := GetSession(c)
		if session == nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error":   "unauthorized",
				"message": "Authentication required",
			})
			return
		}

		if !session.HasPermission(permission) {
			c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
				"error":   "forbidden",
				"message": "Permission '" + permission + "' required",
			})
			return
		}

		c.Next()
	}
}

// RequireAnyPermission requires at least one of the permissions
func RequireAnyPermission(permissions ...string) gin.HandlerFunc {
	return func(c *gin.Context) {
		session := GetSession(c)
		if session == nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error":   "unauthorized",
				"message": "Authentication required",
			})
			return
		}

		if !session.HasAnyPermission(permissions) {
			c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
				"error":   "forbidden",
				"message": "One of the required permissions is missing",
			})
			return
		}

		c.Next()
	}
}

// RequireAllPermissions requires all specified permissions
func RequireAllPermissions(permissions ...string) gin.HandlerFunc {
	return func(c *gin.Context) {
		session := GetSession(c)
		if session == nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error":   "unauthorized",
				"message": "Authentication required",
			})
			return
		}

		if !session.HasAllPermissions(permissions) {
			c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
				"error":   "forbidden",
				"message": "Missing required permissions",
			})
			return
		}

		c.Next()
	}
}

// RequireRole requires a specific role
func RequireRole(role string) gin.HandlerFunc {
	return func(c *gin.Context) {
		session := GetSession(c)
		if session == nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error":   "unauthorized",
				"message": "Authentication required",
			})
			return
		}

		if !session.HasRole(role) {
			c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
				"error":   "forbidden",
				"message": "Role '" + role + "' required",
			})
			return
		}

		c.Next()
	}
}

// RequireAnyRole requires at least one of the roles
func RequireAnyRole(roles ...string) gin.HandlerFunc {
	return func(c *gin.Context) {
		session := GetSession(c)
		if session == nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error":   "unauthorized",
				"message": "Authentication required",
			})
			return
		}

		for _, role := range roles {
			if session.HasRole(role) {
				c.Next()
				return
			}
		}

		c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
			"error":   "forbidden",
			"message": "One of the required roles is missing",
		})
	}
}

// RequireSameTenant ensures user can only access their tenant's data
func RequireSameTenant(tenantIDParam string) gin.HandlerFunc {
	return func(c *gin.Context) {
		session := GetSession(c)
		if session == nil {
			c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
				"error":   "unauthorized",
				"message": "Authentication required",
			})
			return
		}

		requestTenantID := c.Param(tenantIDParam)
		if requestTenantID == "" {
			requestTenantID = c.Query(tenantIDParam)
		}

		if requestTenantID != "" && session.TenantID != nil && *session.TenantID != requestTenantID {
			// Check if user is super admin
			if !session.HasRole("super_admin") {
				c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
					"error":   "forbidden",
					"message": "Access denied to this tenant",
				})
				return
			}
		}

		c.Next()
	}
}

// DetermineUserType determines user type based on roles
func DetermineUserType(roles []string) UserType {
	for _, role := range roles {
		if EmployeeRoles[role] {
			return UserTypeEmployee
		}
	}

	for _, role := range roles {
		if CustomerRoles[role] {
			return UserTypeCustomer
		}
	}

	return UserTypeCustomer
}

// GetPermissionsForRoles returns permissions based on roles and user type
func GetPermissionsForRoles(roles []string, userType UserType) []string {
	permSet := make(map[string]bool)

	// Base permissions by user type
	if userType == UserTypeEmployee {
		for _, p := range EmployeePermissions {
			permSet[p] = true
		}
	} else {
		for _, p := range CustomerPermissions {
			permSet[p] = true
		}
	}

	// Admin permissions
	adminRoles := map[string]bool{
		"admin":                   true,
		"schul_admin":             true,
		"super_admin":             true,
		"data_protection_officer": true,
	}

	for _, role := range roles {
		if adminRoles[role] {
			for _, p := range AdminPermissions {
				permSet[p] = true
			}
			break
		}
	}

	// Convert to slice
	permissions := make([]string, 0, len(permSet))
	for p := range permSet {
		permissions = append(permissions, p)
	}

	return permissions
}

// CheckResourceOwnership checks if user owns a resource or is admin
func CheckResourceOwnership(session *Session, resourceUserID string, allowAdmin bool) bool {
	if session == nil {
		return false
	}

	// User owns the resource
	if session.UserID == resourceUserID {
		return true
	}

	// Admin can access all
	if allowAdmin && (session.HasRole("admin") || session.HasRole("super_admin")) {
		return true
	}

	return false
}

// IsSessionEmployee checks if current session belongs to an employee
func IsSessionEmployee(c *gin.Context) bool {
	session := GetSession(c)
	if session == nil {
		return false
	}
	return session.IsEmployee()
}

// IsSessionCustomer checks if current session belongs to a customer
func IsSessionCustomer(c *gin.Context) bool {
	session := GetSession(c)
	if session == nil {
		return false
	}
	return session.IsCustomer()
}

// HasSessionPermission checks if session has a permission
func HasSessionPermission(c *gin.Context, permission string) bool {
	session := GetSession(c)
	if session == nil {
		return false
	}
	return session.HasPermission(permission)
}

// HasSessionRole checks if session has a role
func HasSessionRole(c *gin.Context, role string) bool {
	session := GetSession(c)
	if session == nil {
		return false
	}
	return session.HasRole(role)
}
