package httpapi

import (
	"context"
	"net/url"
	"strings"

	"github.com/easyai/easyai-ai-gateway/apps/api/internal/auth"
	"github.com/easyai/easyai-ai-gateway/apps/api/internal/identity"
	"github.com/easyai/easyai-ai-gateway/apps/api/internal/oidcsession"
	ssfreceiver "github.com/easyai/easyai-ai-gateway/apps/api/internal/securityevents"
)

type oidcTokenVerifier interface {
	Verify(context.Context, string) (*auth.User, error)
}

type tenantContextReader interface {
	Get(context.Context, string, string) (identity.TenantContext, bool, error)
}

// identityRequestRuntime is an immutable request-level snapshot. A handler that
// starts with one runtime keeps using it even when an administrator activates a
// new revision while that request is in flight.
type identityRequestRuntime struct {
	Revision       identity.Revision
	Verifier       oidcTokenVerifier
	PublicClient   oidcPublicClient
	Sessions       oidcSessionManager
	SessionCipher  *oidcsession.Cipher
	SecurityEvents *ssfreceiver.ConnectionManager
	TenantContext  tenantContextReader
	CookieSecure   bool
	BrowserEnabled bool
}

func (s *Server) currentIdentityRuntime() *identityRequestRuntime {
	if s.identityRuntime != nil {
		runtime := s.identityRuntime.Current()
		if runtime == nil {
			return nil
		}
		return &identityRequestRuntime{
			Revision: runtime.Revision, Verifier: runtime.Verifier, PublicClient: runtime.PublicClient,
			Sessions: runtime.Sessions, SessionCipher: runtime.SessionCipher, SecurityEvents: runtime.SecurityEvents,
			TenantContext: runtime.TenantContext,
			CookieSecure:  runtime.CookieSecure, BrowserEnabled: runtime.PublicClient != nil,
		}
	}

	// Compatibility path for focused HTTP tests. NewServer never uses these
	// static fields after identity revisions are enabled.
	if s.identityTestRevision.ID == "" && s.identityTestRevision.Issuer == "" && s.oidcClient == nil && s.oidcSessions == nil && s.oidcSessionCipher == nil && s.securityEventManager == nil && !s.identityTestBrowserEnabled {
		return nil
	}
	revision := s.identityTestRevision
	if revision.State == "" {
		revision.State = identity.RevisionActive
	}
	webBaseURL := revision.WebBaseURL
	if webBaseURL == "" {
		webBaseURL = s.cfg.WebBaseURL
	}
	if webBaseURL == "" {
		webBaseURL = s.cfg.CORSAllowedOrigin
	}
	revision.WebBaseURL = webBaseURL
	if revision.PublicBaseURL == "" {
		revision.PublicBaseURL = s.cfg.PublicBaseURL
	}
	if revision.SessionAbsoluteSeconds <= 0 {
		revision.SessionAbsoluteSeconds = 28800
	}
	var verifier oidcTokenVerifier
	if s.auth != nil {
		verifier = s.auth.OIDCVerifier
	}
	return &identityRequestRuntime{
		Revision: revision,
		Verifier: verifier, PublicClient: s.oidcClient, Sessions: s.oidcSessions,
		SessionCipher: s.oidcSessionCipher, SecurityEvents: s.securityEventManager,
		CookieSecure:   s.identityTestCookieSecure || strings.HasPrefix(strings.ToLower(revision.PublicBaseURL), "https://"),
		BrowserEnabled: s.identityTestBrowserEnabled || s.oidcClient != nil && s.oidcSessions != nil && s.oidcSessionCipher != nil,
	}
}

func (s *Server) currentSecurityEventManager() *ssfreceiver.ConnectionManager {
	if s.identityRuntime != nil {
		return s.identityRuntime.SecurityEventManager()
	}
	if runtime := s.currentIdentityRuntime(); runtime != nil {
		return runtime.SecurityEvents
	}
	return nil
}

func (s *Server) oidcCookieSecure() bool {
	if runtime := s.currentIdentityRuntime(); runtime != nil {
		return runtime.CookieSecure
	}
	return false
}

func originMatchesBaseURL(origin, baseURL string) bool {
	originURL, err := url.Parse(strings.TrimSpace(origin))
	if err != nil || originURL.User != nil || originURL.Path != "" || originURL.RawQuery != "" || originURL.Fragment != "" {
		return false
	}
	base, err := url.Parse(strings.TrimSpace(baseURL))
	if err != nil {
		return false
	}
	return strings.EqualFold(originURL.Scheme, base.Scheme) && strings.EqualFold(originURL.Host, base.Host)
}
