package handlers

// Authentication Provider handlers for the GoMFT application
import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/starfleetcptn/gomft/components"
	"github.com/starfleetcptn/gomft/internal/db"
)

// AuthProvidersPage renders the page listing all authentication providers
func (h *Handlers) AuthProvidersPage(c *gin.Context) {
	// Get user from context
	userID := c.GetUint("userID")
	if userID == 0 {
		h.HandleUnauthorized(c)
		return
	}

	// Get all auth providers
	providers, err := h.DB.GetAllAuthProviders(c.Request.Context())
	if err != nil {
		h.HandleServerError(c, err)
		return
	}

	components.AuthProviders(c.Request.Context(), providers).Render(c, c.Writer)

}

// NewAuthProviderPage renders the form to create a new authentication provider
func (h *Handlers) NewAuthProviderPage(c *gin.Context) {
	// Get user from context
	userID := c.GetUint("userID")
	if userID == 0 {
		h.HandleUnauthorized(c)
		return
	}

	// Create an empty auth provider for the form
	provider := &db.AuthProvider{}

	// Render the auth provider form for creating a new provider
	components.AuthProviderForm(c.Request.Context(), provider, true).Render(c, c.Writer)
}

// EditAuthProviderPage renders the form to edit an existing authentication provider
func (h *Handlers) EditAuthProviderPage(c *gin.Context) {
	// Get user from context
	userID := c.GetUint("userID")
	if userID == 0 {
		h.HandleUnauthorized(c)
		return
	}

	providerID, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil {
		h.HandleBadRequest(c, "Invalid Provider ID", "The provider ID is not valid")
		return
	}

	// Get the auth provider
	provider, err := h.DB.GetAuthProviderByID(c.Request.Context(), uint(providerID))
	if err != nil {
		h.HandleBadRequest(c, "Provider Not Found", "The authentication provider could not be found")
		return
	}

	// Render the auth provider form for editing
	components.AuthProviderForm(c.Request.Context(), provider, false).Render(c, c.Writer)
}

// HandleCreateAuthProvider handles the form submission to create a new authentication provider
func (h *Handlers) HandleCreateAuthProvider(c *gin.Context) {
	// Get user from context
	userID := c.GetUint("userID")
	if userID == 0 {
		h.HandleUnauthorized(c)
		return
	}

	if err := c.Request.ParseForm(); err != nil {
		h.HandleBadRequest(c, "Invalid Form Data", "Could not parse form data")
		return
	}

	provider := &db.AuthProvider{
		Name:         c.PostForm("name"),
		Type:         db.ProviderType(c.PostForm("type")),
		ProviderURL:  c.PostForm("provider_url"),
		ClientID:     c.PostForm("client_id"),
		ClientSecret: c.PostForm("client_secret"),
		RedirectURL:  c.PostForm("redirect_url"),
		Scopes:       c.PostForm("scopes"),
		Description:  c.PostForm("description"),
		IconURL:      c.PostForm("icon_url"),
		// Enabled will be set using the helper method below
	}
	provider.SetEnabled(c.PostForm("enabled") == "on") // Use helper method

	// Process config values based on provider type
	config := make(map[string]interface{})
	switch provider.Type {
	case db.ProviderTypeAuthentik:
		if tenant := c.PostForm("authentik_tenant"); tenant != "" {
			config["tenant_id"] = tenant
		}
	case db.ProviderTypeOIDC:
		if discoveryURL := c.PostForm("oidc_discovery_url"); discoveryURL != "" {
			config["discovery_url"] = discoveryURL
		}
	case db.ProviderTypeSAML:
		if metadataURL := c.PostForm("saml_metadata_url"); metadataURL != "" {
			config["metadata_url"] = metadataURL
		}
	}

	// Set the config
	if len(config) > 0 {
		configJSON, err := json.Marshal(config)
		if err != nil {
			h.HandleBadRequest(c, "Invalid Configuration", "Could not process provider configuration")
			return
		}
		provider.Config = string(configJSON)
	}

	// Process attribute mappings
	attrMapping := map[string]string{
		"username": c.PostForm("attr_username"),
		"email":    c.PostForm("attr_email"),
		"name":     c.PostForm("attr_name"),
		"groups":   c.PostForm("attr_groups"),
	}

	// Remove empty mappings
	for k, v := range attrMapping {
		if v == "" {
			delete(attrMapping, k)
		}
	}

	if len(attrMapping) > 0 {
		mappingJSON, err := json.Marshal(attrMapping)
		if err != nil {
			h.HandleBadRequest(c, "Invalid Attribute Mapping", "Could not process attribute mappings")
			return
		}
		provider.AttributeMapping = string(mappingJSON)
	}

	// Create the provider
	if err := h.DB.CreateAuthProvider(c.Request.Context(), provider); err != nil {
		h.HandleServerError(c, err)
		return
	}

	log.Printf("[Audit] User %d created auth provider %d of type %s with name '%s'",
		userID, provider.ID, provider.Type, provider.Name)

	// Set success flash and redirect
	c.SetCookie("flash_message", fmt.Sprintf("Authentication provider '%s' created successfully", provider.Name),
		3600, "/", "", false, true)
	c.SetCookie("flash_type", "success", 3600, "/", "", false, true)

	c.Redirect(http.StatusFound, "/admin/settings/auth-providers")
}

// HandleUpdateAuthProvider handles the form submission to update an existing authentication provider
func (h *Handlers) HandleUpdateAuthProvider(c *gin.Context) {
	// Get user from context
	userID := c.GetUint("userID")
	if userID == 0 {
		h.HandleUnauthorized(c)
		return
	}

	providerID, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil {
		h.HandleBadRequest(c, "Invalid Provider ID", "The provider ID is not valid")
		return
	}

	// Get the existing provider
	existingProvider, err := h.DB.GetAuthProviderByID(c.Request.Context(), uint(providerID))
	if err != nil {
		h.HandleBadRequest(c, "Provider Not Found", "The authentication provider could not be found")
		return
	}

	if err := c.Request.ParseForm(); err != nil {
		h.HandleBadRequest(c, "Invalid Form Data", "Could not parse form data")
		return
	}

	// Update the provider with form data
	existingProvider.Name = c.PostForm("name")
	existingProvider.ProviderURL = c.PostForm("provider_url")
	existingProvider.ClientID = c.PostForm("client_id")
	existingProvider.IconURL = c.PostForm("icon_url")

	// Only update client secret if provided
	if clientSecret := c.PostForm("client_secret"); clientSecret != "" {
		existingProvider.ClientSecret = clientSecret
	}

	existingProvider.RedirectURL = c.PostForm("redirect_url")
	existingProvider.Scopes = c.PostForm("scopes")
	existingProvider.Description = c.PostForm("description")
	existingProvider.SetEnabled(c.PostForm("enabled") == "on") // Use setter

	// Update the type if changed
	if providerType := c.PostForm("type"); providerType != "" {
		existingProvider.Type = db.ProviderType(providerType)
	}

	// Process config values based on provider type
	config := make(map[string]interface{})
	switch existingProvider.Type {
	case db.ProviderTypeAuthentik:
		if tenant := c.PostForm("authentik_tenant"); tenant != "" {
			config["tenant_id"] = tenant
		}
	case db.ProviderTypeOIDC:
		if discoveryURL := c.PostForm("oidc_discovery_url"); discoveryURL != "" {
			config["discovery_url"] = discoveryURL
		}
	case db.ProviderTypeSAML:
		if metadataURL := c.PostForm("saml_metadata_url"); metadataURL != "" {
			config["metadata_url"] = metadataURL
		}
	}

	// Set the config
	if len(config) > 0 {
		configJSON, err := json.Marshal(config)
		if err != nil {
			h.HandleBadRequest(c, "Invalid Configuration", "Could not process provider configuration")
			return
		}
		existingProvider.Config = string(configJSON)
	}

	// Process attribute mappings
	attrMapping := map[string]string{
		"username": c.PostForm("attr_username"),
		"email":    c.PostForm("attr_email"),
		"name":     c.PostForm("attr_name"),
		"groups":   c.PostForm("attr_groups"),
	}

	// Remove empty mappings
	for k, v := range attrMapping {
		if v == "" {
			delete(attrMapping, k)
		}
	}

	if len(attrMapping) > 0 {
		mappingJSON, err := json.Marshal(attrMapping)
		if err != nil {
			h.HandleBadRequest(c, "Invalid Attribute Mapping", "Could not process attribute mappings")
			return
		}
		existingProvider.AttributeMapping = string(mappingJSON)
	}

	// Update the provider
	if err := h.DB.UpdateAuthProvider(c.Request.Context(), existingProvider); err != nil {
		h.HandleServerError(c, err)
		return
	}

	log.Printf("[Audit] User %d updated auth provider %d of type %s with name '%s'",
		userID, existingProvider.ID, existingProvider.Type, existingProvider.Name)

	// Set success flash and redirect
	c.SetCookie("flash_message", fmt.Sprintf("Authentication provider '%s' updated successfully", existingProvider.Name),
		3600, "/", "", false, true)
	c.SetCookie("flash_type", "success", 3600, "/", "", false, true)

	c.Redirect(http.StatusFound, "/admin/settings/auth-providers")
}

// HandleDeleteAuthProvider handles the request to delete an authentication provider
func (h *Handlers) HandleDeleteAuthProvider(c *gin.Context) {
	// Get user from context
	userID := c.GetUint("userID")
	if userID == 0 {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error": "Unauthorized",
		})
		return
	}

	providerID, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Invalid provider ID",
		})
		return
	}

	// Get the provider to be deleted
	provider, err := h.DB.GetAuthProviderByID(c.Request.Context(), uint(providerID))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error": "Authentication provider not found",
		})
		return
	}

	// Check if there are any user identities associated with this provider
	count, err := h.DB.CountExternalUserIdentitiesByProviderID(c.Request.Context(), uint(providerID))
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "Failed to check if provider is in use",
		})
		return
	}

	if count > 0 {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": fmt.Sprintf("Cannot delete provider '%s' because it has %d associated user identities", provider.Name, count),
		})
		return
	}

	// Delete the provider
	if err := h.DB.DeleteAuthProvider(c.Request.Context(), uint(providerID)); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "Failed to delete authentication provider",
		})
		return
	}

	log.Printf("[Audit] User %d deleted auth provider %d of type %s with name '%s'",
		userID, provider.ID, provider.Type, provider.Name)

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": fmt.Sprintf("Authentication provider '%s' deleted successfully", provider.Name),
	})
}

// HandleTestAuthProviderConnection tests the connection to an authentication provider
func (h *Handlers) HandleTestAuthProviderConnection(c *gin.Context) {
	// Get user from context
	userID := c.GetUint("userID")
	if userID == 0 {
		c.JSON(http.StatusUnauthorized, gin.H{
			"error": "Unauthorized",
		})
		return
	}

	providerID, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Invalid provider ID",
		})
		return
	}

	// Get the provider
	provider, err := h.DB.GetAuthProviderByID(c.Request.Context(), uint(providerID))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{
			"error": "Authentication provider not found",
		})
		return
	}

	// Attempt to test the connection based on provider type
	var testResult error
	switch provider.Type {
	case db.ProviderTypeAuthentik:
		testResult = h.testAuthentikConnection(provider)
	case db.ProviderTypeOIDC:
		testResult = h.testOIDCConnection(provider)
	case db.ProviderTypeSAML:
		testResult = h.testSAMLConnection(provider)
	case db.ProviderTypeOAuth2:
		testResult = h.testOAuth2Connection(provider)
	default:
		testResult = fmt.Errorf("unsupported provider type: %s", provider.Type)
	}

	if testResult != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": fmt.Sprintf("Connection test failed: %s", testResult.Error()),
		})
		return
	}

	log.Printf("[Audit] User %d successfully tested connection to auth provider %d of type %s",
		userID, provider.ID, provider.Type)

	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "Connection test successful",
	})
}

// Test methods for different provider types
func (h *Handlers) testAuthentikConnection(provider *db.AuthProvider) error {
	// TODO: Implement actual test logic for Authentik
	return nil
}

func (h *Handlers) testOIDCConnection(provider *db.AuthProvider) error {
	// TODO: Implement actual test logic for OIDC
	return nil
}

func (h *Handlers) testSAMLConnection(provider *db.AuthProvider) error {
	// TODO: Implement actual test logic for SAML
	return nil
}

func (h *Handlers) testOAuth2Connection(provider *db.AuthProvider) error {
	// TODO: Implement actual test logic for OAuth2
	return nil
}
