package oauth

import (
	"github.com/tingly-dev/tingly-box/ai"
)

// DefaultRegistry returns a registry with default provider configurations
// Note: Client ID and Secret must be set from environment variables or config
func DefaultRegistry() *Registry {
	registry := NewRegistry()

	// Anthropic (Claude) OAuth - uses PKCE
	// TokenURL verified: https://api.anthropic.com/v1/oauth/token (not console.anthropic.com)
	// PKCE is required: code_verifier must be included in token request
	registry.Register(&ProviderConfig{
		Type:               ai.IssuerClaudeCode,
		DisplayName:        "Anthropic Claude Code",
		ClientID:           "9d1c250a-e61b-44d9-88ed-5944d1962f5e", // Public client ID for Claude Code
		ClientSecret:       "",                                     // No secret required for public client
		AuthURL:            "https://claude.ai/oauth/authorize",
		TokenURL:           "https://api.anthropic.com/v1/oauth/token", // API endpoint (verified working)
		RedirectURL:        "",                                         // Dynamic: set to server.BaseURL + "/callback"
		Scopes:             []string{"user:profile", "user:inference", "user:sessions:claude_code", "user:mcp_servers", "user:file_upload"},
		AuthStyle:          AuthStyleInNone,        // Public client, no auth in token request
		OAuthMethod:        OAuthMethodPKCE,        // Uses PKCE for security (REQUIRED)
		TokenRequestFormat: TokenRequestFormatJSON, // Anthropic requires JSON format
		ConsoleURL:         "https://console.anthropic.com/",
		Hook:               &AnthropicHook{},
	})

	// OpenAI OAuth
	registry.Register(&ProviderConfig{
		Type:         ai.IssuerOpenAI,
		DisplayName:  "OpenAI",
		ClientID:     "", // Must be configured
		ClientSecret: "",
		AuthURL:      "https://platform.openai.com/oauth/authorize",
		TokenURL:     "https://api.openai.com/v1/oauth/token",
		Scopes:       []string{"api", "offline_access"},
		AuthStyle:    AuthStyleInHeader,
		ConsoleURL:   "https://platform.openai.com/",
		Hook:         &NoopHook{},
	})

	// TODO: Google OAuth (for Gemini/Vertex AI)
	registry.Register(&ProviderConfig{
		Type:         ai.IssuerGoogle,
		DisplayName:  "Google",
		ClientID:     "", // Must be configured
		ClientSecret: "",
		AuthURL:      "https://accounts.google.com/o/oauth2/v2/auth",
		TokenURL:     "https://oauth2.googleapis.com/token",
		Scopes:       []string{"https://www.googleapis.com/auth/cloud-platform"},
		AuthStyle:    AuthStyleInHeader,
		ConsoleURL:   "https://console.cloud.google.com/",
		Hook:         &NoopHook{},
	})

	// Gemini CLI OAuth (Google OAuth with Gemini CLI's built-in credentials)
	// Based on: https://github.com/google-gemini/gemini-cli
	registry.Register(&ProviderConfig{
		Type:         ai.IssuerGemini,
		DisplayName:  "Gemini CLI",
		ClientID:     "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com",
		ClientSecret: "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",
		AuthURL:      "https://accounts.google.com/o/oauth2/v2/auth",
		TokenURL:     "https://oauth2.googleapis.com/token",
		Scopes: []string{
			"https://www.googleapis.com/auth/cloud-platform",
			"https://www.googleapis.com/auth/userinfo.email",
			"https://www.googleapis.com/auth/userinfo.profile",
		},
		AuthStyle:   AuthStyleInHeader,
		OAuthMethod: OAuthMethodPKCE, // Uses PKCE for security
		ConsoleURL:  "https://console.cloud.google.com/",
		Hook:        &GeminiHook{},
	})

	// GitHub OAuth (for GitHub Copilot)
	// Note: You need to create your own OAuth app at https://github.com/settings/developers
	// This is a demo configuration for testing the authorize URL
	registry.Register(&ProviderConfig{
		Type:         ai.IssuerGitHub,
		DisplayName:  "GitHub",
		ClientID:     "demo-github-client-id", // Replace with your own OAuth app's Client ID
		ClientSecret: "",                      // No secret required for demo
		AuthURL:      "https://github.com/login/oauth/authorize",
		TokenURL:     "https://github.com/login/oauth/access_token",
		Scopes:       []string{"read:user", "user:email"},
		AuthStyle:    AuthStyleInParams, // GitHub uses params for auth
		ConsoleURL:   "https://github.com/settings/developers",
		Hook:         &NoopHook{},
	})

	// Antigravity OAuth (Google OAuth with Antigravity credentials)
	// Scopes include cloud-platform, userinfo, and additional Google services
	registry.Register(&ProviderConfig{
		Type:         ai.IssuerAntigravity,
		DisplayName:  "Antigravity",
		ClientID:     "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com",
		ClientSecret: "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",
		AuthURL:      "https://accounts.google.com/o/oauth2/v2/auth",
		TokenURL:     "https://oauth2.googleapis.com/token",
		Scopes: []string{
			"https://www.googleapis.com/auth/cloud-platform",
			"https://www.googleapis.com/auth/userinfo.email",
			"https://www.googleapis.com/auth/userinfo.profile",
			"https://www.googleapis.com/auth/cclog",
			"https://www.googleapis.com/auth/experimentsandconfigs",
		},
		AuthStyle:   AuthStyleInHeader,
		OAuthMethod: OAuthMethodPKCE,
		ConsoleURL:  "https://console.cloud.google.com/",
		Hook:        &AntigravityHook{},
	})

	// Mock OAuth provider for testing
	// Uses https://oauth-mock.mock.beeceptor.com for testing OAuth flow
	registry.Register(&ProviderConfig{
		Type:         ai.IssuerMock,
		DisplayName:  "Mock OAuth (Testing)",
		ClientID:     "mock-client-id",
		ClientSecret: "mock-client-secret",
		AuthURL:      "https://oauth-mock.mock.beeceptor.com/oauth/authorize",
		TokenURL:     "https://oauth-mock.mock.beeceptor.com/oauth/token/google",
		Scopes:       []string{"test", "read", "write"},
		AuthStyle:    AuthStyleInParams,
		ConsoleURL:   "",
		Hook:         &NoopHook{},
	})

	// Qwen OAuth (Device Code Flow with PKCE)
	// https://chat.qwen.ai/
	// Uses device code flow with PKCE for authentication (RFC 8628 + RFC 7636)
	registry.Register(&ProviderConfig{
		Type:               ai.IssuerQwenCode,
		GrantType:          "urn:ietf:params:oauth:grant-type:device_code",
		DisplayName:        "Qwen",
		ClientID:           "f0304373b74a44d2b584a3fb70ca9e56",
		ClientSecret:       "", // No secret required for device code flow
		DeviceCodeURL:      "https://chat.qwen.ai/api/v1/oauth2/device/code",
		TokenURL:           "https://chat.qwen.ai/api/v1/oauth2/token",
		Scopes:             []string{"openid", "profile", "email", "model.completion"},
		AuthStyle:          AuthStyleInNone,
		OAuthMethod:        OAuthMethodDeviceCodePKCE,
		TokenRequestFormat: TokenRequestFormatForm,
		ConsoleURL:         "https://chat.qwen.ai/",
		Hook:               &QwenHook{},
	})

	// iFlow OAuth (Chinese AI platform)
	// https://iflow.cn/
	// Uses custom OAuth with phone-based login and Basic Auth for token requests
	// Reference: https://github.com/router-for-me/CLIProxyAPI
	registry.Register(&ProviderConfig{
		Type:         ai.IssuerIFlow,
		DisplayName:  "iFlow",
		ClientID:     "10009311001",
		ClientSecret: "4Z3YjXycVsQvyGF1etiNlIBB4RsqSDtW",
		AuthURL:      "https://iflow.cn/oauth",
		TokenURL:     "https://iflow.cn/oauth/token",
		Scopes:       []string{},
		AuthStyle:    AuthStyleInHeader, // Uses Basic Auth
		ConsoleURL:   "https://platform.iflow.cn/",
		Hook: &IFlowHook{
			ClientID:     "10009311001",
			ClientSecret: "4Z3YjXycVsQvyGF1etiNlIBB4RsqSDtW",
		},
	})

	// Codex OAuth (OpenAI Codex CLI)
	// https://platform.openai.com/
	// Uses PKCE for secure authentication with OpenAI's OAuth
	// Reference: https://github.com/openai/openai-cli
	// Emulates Codex CLI to use their client ID - requires callback on port 1455
	registry.Register(&ProviderConfig{
		Type:               ai.IssuerCodex,
		DisplayName:        "Codex",
		ClientID:           "app_EMoamEEZ73f0CkXaXp7hrann", // OpenAI Codex CLI client ID
		ClientSecret:       "",                             // Public client, no secret required
		AuthURL:            "https://auth.openai.com/oauth/authorize",
		TokenURL:           "https://auth.openai.com/oauth/token",
		Scopes:             []string{"openid", "profile", "email", "offline_access"},
		AuthStyle:          AuthStyleInParams, // Client credentials in POST body
		OAuthMethod:        OAuthMethodPKCE,
		TokenRequestFormat: TokenRequestFormatForm,   // application/x-www-form-urlencoded
		StateEncoding:      StateEncodingBase64URL32, // Match OpenAI Codex CLI state format (32 bytes)
		ConsoleURL:         "https://platform.openai.com/",
		Callback:           "/auth/callback", // Codex requires specific callback path
		CallbackPorts:      []int{1455},      // Codex requires port 1455
		Hook:               &CodexHook{},
	})

	registry.Register(&ProviderConfig{
		Type:               ai.IssuerKimiCode,
		DisplayName:        "Kimi CLI",
		ClientID:           "17e5f671-d194-4dfb-9706-5516cb48c098",
		ClientSecret:       "", // No ClientSecret for Device Authorization Flow
		DeviceCodeURL:      "https://auth.kimi.com/api/oauth/device_authorization",
		TokenURL:           "https://auth.kimi.com/api/oauth/token",
		Scopes:             []string{"kimi-code"},
		AuthStyle:          AuthStyleInNone,
		OAuthMethod:        OAuthMethodDeviceCode,
		TokenRequestFormat: TokenRequestFormatForm,
		ConsoleURL:         "https://www.kimi.com/code",
		Hook:               &KimiHook{},
	})

	return registry
}

// Registry manages OAuth provider configurations
type Registry struct {
	providers map[ai.Issuer]*ProviderConfig
}

// NewRegistry creates a new OAuth provider registry
func NewRegistry() *Registry {
	return &Registry{
		providers: make(map[ai.Issuer]*ProviderConfig),
	}
}

// Register adds or updates a provider configuration
func (r *Registry) Register(config *ProviderConfig) {
	r.providers[config.Type] = config
}

// Unregister removes a provider configuration
func (r *Registry) Unregister(providerType ai.Issuer) {
	delete(r.providers, providerType)
}

// Get returns a provider configuration
func (r *Registry) Get(providerType ai.Issuer) (*ProviderConfig, bool) {
	config, ok := r.providers[providerType]
	return config, ok
}

// List returns all registered provider types
func (r *Registry) List() []ai.Issuer {
	types := make([]ai.Issuer, 0, len(r.providers))
	for t := range r.providers {
		types = append(types, t)
	}
	return types
}

// IsRegistered checks if a provider is registered
func (r *Registry) IsRegistered(providerType ai.Issuer) bool {
	_, ok := r.providers[providerType]
	return ok
}

// ProviderInfo returns information about a provider
type ProviderInfo struct {
	Type        ai.Issuer `json:"type"`
	DisplayName string    `json:"display_name"`
	AuthURL     string    `json:"auth_url,omitempty"`
	Scopes      []string  `json:"scopes,omitempty"`
	Configured  bool      `json:"configured"` // Has client credentials
}

// GetProviderInfo returns info about all registered providers
func (r *Registry) GetProviderInfo() []ProviderInfo {
	info := make([]ProviderInfo, 0, len(r.providers))
	for _, config := range r.providers {
		// A provider is considered configured if:
		// - Public client (AuthStyleInNone): only needs ClientID
		// - PKCE/DeviceCode flows: only need ClientID
		// - Standard flows: need both ClientID and ClientSecret
		configured := config.ClientID != ""
		if config.AuthStyle != AuthStyleInNone &&
			config.OAuthMethod != OAuthMethodPKCE &&
			config.OAuthMethod != OAuthMethodDeviceCode &&
			config.OAuthMethod != OAuthMethodDeviceCodePKCE {
			configured = configured && config.ClientSecret != ""
		}
		info = append(info, ProviderInfo{
			Type:        config.Type,
			DisplayName: config.DisplayName,
			AuthURL:     config.AuthURL,
			Scopes:      config.Scopes,
			Configured:  configured,
		})
	}
	return info
}
