package handler

import (
	"baron-sso-backend/internal/domain"
	"baron-sso-backend/internal/repository"
	"baron-sso-backend/internal/service"
	"errors"

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

// FederationHandler handles API requests for IdP federation.
type FederationHandler struct {
	fedSvc *service.FederationService
	repo   repository.FederationRepository // For IdP Config CRUD
	db     *gorm.DB                        // For tenant existence checks, etc. in CRUD
}

// NewFederationHandler creates a new FederationHandler.
func NewFederationHandler(fedSvc *service.FederationService, repo repository.FederationRepository, db *gorm.DB) *FederationHandler {
	return &FederationHandler{
		fedSvc: fedSvc,
		repo:   repo,
		db:     db,
	}
}

// InitiateOIDCLogin handles the start of the OIDC login flow.
// It expects `provider_id` and `login_challenge` as query parameters.
func (h *FederationHandler) InitiateOIDCLogin(c *fiber.Ctx) error {
	providerID := c.Query("provider_id")
	loginChallenge := c.Query("login_challenge")

	if providerID == "" || loginChallenge == "" {
		return errorJSON(c, fiber.StatusBadRequest, "provider_id and login_challenge are required")
	}

	redirectURL, err := h.fedSvc.InitiateOIDCLogin(c.Context(), providerID, loginChallenge)
	if err != nil {
		// Log the error properly in a real application
		return errorJSON(c, fiber.StatusInternalServerError, "failed to initiate OIDC login")
	}

	return c.Redirect(redirectURL, fiber.StatusFound)
}

// HandleOIDCCallback handles the OIDC callback from the IdP.
func (h *FederationHandler) HandleOIDCCallback(c *fiber.Ctx) error {
	code := c.Query("code")
	state := c.Query("state")

	if code == "" || state == "" {
		return errorJSON(c, fiber.StatusBadRequest, "code and state are required")
	}

	redirectURL, err := h.fedSvc.HandleOIDCCallback(c.Context(), code, state)
	if err != nil {
		return errorJSON(c, fiber.StatusInternalServerError, "failed to handle OIDC callback")
	}

	return c.Redirect(redirectURL, fiber.StatusFound)
}

// --- New Client-based IdP Config Methods ---

// ListIdpConfigsForClient handles listing all IdP configurations for a client.
func (h *FederationHandler) ListIdpConfigsForClient(c *fiber.Ctx) error {
	clientID := c.Params("clientId")
	if clientID == "" {
		return errorJSON(c, fiber.StatusBadRequest, "clientId is required")
	}

	var configs []domain.IdentityProviderConfig
	if err := h.db.Where("client_id = ?", clientID).Find(&configs).Error; err != nil {
		return errorJSON(c, fiber.StatusInternalServerError, err.Error())
	}

	return c.JSON(configs)
}

// CreateIdpConfigForClient handles the creation of a new IdP configuration for a client.
func (h *FederationHandler) CreateIdpConfigForClient(c *fiber.Ctx) error {
	clientID := c.Params("clientId")
	if clientID == "" {
		return errorJSON(c, fiber.StatusBadRequest, "clientId is required in path")
	}

	var req domain.IdentityProviderConfig
	if err := c.BodyParser(&req); err != nil {
		return errorJSON(c, fiber.StatusBadRequest, "invalid request body")
	}

	// Assign clientID from path parameter
	req.ClientID = clientID

	// Basic validation
	if req.DisplayName == "" || req.ProviderType == "" {
		return errorJSON(c, fiber.StatusBadRequest, "display_name and provider_type are required")
	}

	// TODO: Optionally, validate if the clientID exists in Hydra

	// Create in DB
	if err := h.db.Create(&req).Error; err != nil {
		return errorJSON(c, fiber.StatusInternalServerError, err.Error())
	}

	return c.Status(fiber.StatusCreated).JSON(req)
}

// --- Deprecated Tenant-based IdP Config Methods ---

// ListIdpConfigsForTenant handles listing all IdP configurations for a tenant.
func (h *FederationHandler) ListIdpConfigsForTenant(c *fiber.Ctx) error {
	tenantID := c.Params("tenantId")
	if tenantID == "" {
		return errorJSON(c, fiber.StatusBadRequest, "tenantId is required")
	}

	// This is a temporary solution. We should create a proper method in the repository.
	var configs []domain.IdentityProviderConfig
	// Note: This now queries client_id, which is incorrect for tenants.
	// This method is deprecated.
	if err := h.db.Where("tenant_id = ?", tenantID).Find(&configs).Error; err != nil {
		return errorJSON(c, fiber.StatusInternalServerError, err.Error())
	}

	return c.JSON(configs)
}

// CreateIdpConfig handles the creation of a new IdP configuration.
func (h *FederationHandler) CreateIdpConfig(c *fiber.Ctx) error {
	var req domain.IdentityProviderConfig
	if err := c.BodyParser(&req); err != nil {
		return errorJSON(c, fiber.StatusBadRequest, "invalid request body")
	}

	// Basic validation - This is the old validation logic
	if req.ClientID == "" || req.DisplayName == "" || req.ProviderType == "" {
		return errorJSON(c, fiber.StatusBadRequest, "client_id, display_name, and provider_type are required")
	}

	// This check is now incorrect and deprecated.
	var tenant domain.Tenant
	if err := h.db.First(&tenant, "id = ?", req.ClientID).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errorJSON(c, fiber.StatusBadRequest, "tenant not found")
		}
		return errorJSON(c, fiber.StatusInternalServerError, err.Error())
	}

	// Create in DB
	if err := h.db.Create(&req).Error; err != nil {
		return errorJSON(c, fiber.StatusInternalServerError, err.Error())
	}

	return c.Status(fiber.StatusCreated).JSON(req)
}

// TODO: Re-implement Update, Delete handlers for IdP Configs for Clients
