package authorized

import (
	"github.com/gin-gonic/gin"
	"github.com/yaoapp/gou/process"
	"github.com/yaoapp/yao/openapi/oauth/types"
)

// ProcessAuthInfo extracts authorized information from the process
func ProcessAuthInfo(p *process.Process) *types.AuthorizedInfo {
	// TODO: Implement this function
	// Get authorized information from the process context
	info := &types.AuthorizedInfo{}
	return info
}

// GetInfo extracts authorized information from the gin context
// This function reads authorization data that was set by the OAuth guard middleware
func GetInfo(c *gin.Context) *types.AuthorizedInfo {
	info := &types.AuthorizedInfo{}

	if subject, ok := c.Get("__subject"); ok {
		info.Subject = subject.(string)
	}

	if clientID, ok := c.Get("__client_id"); ok {
		info.ClientID = clientID.(string)
	}

	if userID, ok := c.Get("__user_id"); ok {
		info.UserID = userID.(string)
	}

	if scope, ok := c.Get("__scope"); ok {
		info.Scope = scope.(string)
	}

	if teamID, ok := c.Get("__team_id"); ok {
		info.TeamID = teamID.(string)
	}

	if tenantID, ok := c.Get("__tenant_id"); ok {
		info.TenantID = tenantID.(string)
	}

	if sessionID, ok := c.Get("__sid"); ok {
		info.SessionID = sessionID.(string)
	}

	if rememberMe, ok := c.Get("__remember_me"); ok {
		if rmBool, ok := rememberMe.(bool); ok {
			info.RememberMe = rmBool
		}
	}

	// Get data access constraints (set by ACL enforcement)
	info.Constraints = GetConstraints(c)

	return info
}

// IsTeamMember checks if the user is a team member
func IsTeamMember(c *gin.Context) bool {
	authInfo := GetInfo(c)
	return authInfo != nil && authInfo.TeamID != "" && authInfo.UserID != ""
}

// GetConstraints extracts data access constraints from the gin context
// Returns a DataConstraints struct with all constraint flags
func GetConstraints(c *gin.Context) types.DataConstraints {
	constraints := types.DataConstraints{}

	// Built-in constraints
	if ownerOnly, ok := c.Get("__owner_only"); ok {
		if ownerOnlyBool, ok := ownerOnly.(bool); ok {
			constraints.OwnerOnly = ownerOnlyBool
		}
	}

	if creatorOnly, ok := c.Get("__creator_only"); ok {
		if creatorOnlyBool, ok := creatorOnly.(bool); ok {
			constraints.CreatorOnly = creatorOnlyBool
		}
	}

	if editorOnly, ok := c.Get("__editor_only"); ok {
		if editorOnlyBool, ok := editorOnly.(bool); ok {
			constraints.EditorOnly = editorOnlyBool
		}
	}

	if teamOnly, ok := c.Get("__team_only"); ok {
		if teamOnlyBool, ok := teamOnly.(bool); ok {
			constraints.TeamOnly = teamOnlyBool
		}
	}

	// Extra constraints
	if extraConstraints, ok := c.Get("__extra_constraints"); ok {
		if extra, ok := extraConstraints.(map[string]interface{}); ok {
			constraints.Extra = extra
		}
	}

	return constraints
}

// UpdateConstraints updates data access constraints in the gin context
// This should be called by ACL enforcement after successful permission check
// Accepts a map of constraints for flexible extension
func UpdateConstraints(c *gin.Context, constraints map[string]interface{}) {
	// Set each constraint in the context
	for key, value := range constraints {
		c.Set("__"+key, value)
	}
}

// SetInfo sets authorized information in the gin context
// This function should be called by the OAuth guard middleware after token validation
// userIDGetter is a function that resolves the user_id from clientID and subject
func SetInfo(c *gin.Context, claims *types.TokenClaims, sessionID string, userIDGetter func(clientID, subject string) (string, error)) {
	// Set session ID in context
	if sessionID != "" {
		c.Set("__sid", sessionID)
	}

	// Set user_id in context (resolve from claims)
	if userIDGetter != nil {
		userID, err := userIDGetter(claims.ClientID, claims.Subject)
		if err == nil && userID != "" {
			c.Set("__user_id", userID)
		}
	}

	// Set subject, scope, client_id in context
	c.Set("__subject", claims.Subject)
	c.Set("__scope", claims.Scope)
	c.Set("__client_id", claims.ClientID)

	// Set team_id and tenant_id in context if available
	if claims.TeamID != "" {
		c.Set("__team_id", claims.TeamID)
	}
	if claims.TenantID != "" {
		c.Set("__tenant_id", claims.TenantID)
	}

	// Set custom claims from Extra field into context
	if claims.Extra != nil {
		for key, value := range claims.Extra {
			c.Set("__"+key, value)
		}
	}
}
