package service

import (
	"baron-sso-backend/internal/repository"
	"context"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"time"

	"github.com/coreos/go-oidc/v3/oidc"
	"golang.org/x/oauth2"
)

type FederationService struct {
	repo     repository.FederationRepository
	hydraSvc *HydraAdminService
	redisSvc *RedisService
}

func NewFederationService(repo repository.FederationRepository, hydraSvc *HydraAdminService, redisSvc *RedisService) *FederationService {
	return &FederationService{repo: repo, hydraSvc: hydraSvc, redisSvc: redisSvc}
}

func (s *FederationService) InitiateOIDCLogin(ctx context.Context, providerID, loginChallenge string) (string, error) {
	provider, err := s.repo.FindProviderByID(ctx, providerID)
	if err != nil {
		return "", fmt.Errorf("failed to find provider: %w", err)
	}

	if provider == nil || provider.IssuerURL == nil || provider.OIDCClientID == nil || provider.OIDCClientSecret == nil || provider.Scopes == nil {
		return "", fmt.Errorf("OIDC configuration for provider %s is incomplete", providerID)
	}

	oidcProvider, err := oidc.NewProvider(ctx, *provider.IssuerURL)
	if err != nil {
		return "", fmt.Errorf("failed to create OIDC provider: %w", err)
	}

	config := oauth2.Config{
		ClientID:     *provider.OIDCClientID,
		ClientSecret: *provider.OIDCClientSecret,
		Endpoint:     oidcProvider.Endpoint(),
		RedirectURL:  "http://localhost:8080/api/v1/federation/oidc/callback", // This should be configurable
		Scopes:       []string{*provider.Scopes},
	}

	state, err := generateState()
	if err != nil {
		return "", fmt.Errorf("failed to generate state: %w", err)
	}

	// Store state and login_challenge in Redis
	redisKey := fmt.Sprintf("oidc_state:%s", state)
	if err := s.redisSvc.Set(redisKey, loginChallenge, 10*time.Minute); err != nil {
		return "", fmt.Errorf("failed to save state to Redis: %w", err)
	}

	return config.AuthCodeURL(state), nil
}

func (s *FederationService) HandleOIDCCallback(ctx context.Context, code, state string) (string, error) {
	// 1. Retrieve login_challenge from Redis
	redisKey := fmt.Sprintf("oidc_state:%s", state)
	loginChallenge, err := s.redisSvc.Get(redisKey)
	if err != nil {
		return "", fmt.Errorf("failed to get state from Redis or state expired: %w", err)
	}
	// Delete the state from Redis now that it's been used
	s.redisSvc.Delete(redisKey)

	// TODO: Finish the rest of the callback logic
	// 2. Exchange code for token
	// 3. Verify ID token
	// 4. JIT Provisioning
	// 5. Accept Hydra Login Request

	fmt.Println("Login challenge found:", loginChallenge)

	return "http://localhost:3000/login?login_successful=true", nil // Placeholder
}

func generateState() (string, error) {
	b := make([]byte, 32)
	_, err := rand.Read(b)
	if err != nil {
		return "", err
	}
	return base64.RawURLEncoding.EncodeToString(b), nil
}
