//
//  Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

package server

import (
	"errors"
	"fmt"
	"ragflow/internal/server/config"
	"strings"
	"time"

	"github.com/spf13/viper"
	"go.uber.org/zap"
)

// DefaultConnectTimeout default connection timeout for external services
const DefaultConnectTimeout = 5 * time.Second

// OAuthConfig OAuth configuration for a channel.
// Mirrors api/apps/auth/__init__.py's OAUTH_CONFIG entries: a Type that
// selects the auth client flavor (oauth2 / oidc / GitHub), plus the
// transport URLs and client credentials. For OIDC the URLs are derived
// from Issuer via the .well-known/openid-configuration document, so they
// may be left blank.
type OAuthConfig struct {
	DisplayName      string `mapstructure:"display_name"`
	Icon             string `mapstructure:"icon"`
	Type             string `mapstructure:"type"`
	ClientID         string `mapstructure:"client_id"`
	ClientSecret     string `mapstructure:"client_secret"`
	AuthorizationURL string `mapstructure:"authorization_url"`
	TokenURL         string `mapstructure:"token_url"`
	UserinfoURL      string `mapstructure:"userinfo_url"`
	RedirectURI      string `mapstructure:"redirect_uri"`
	Scope            string `mapstructure:"scope"`
	Issuer           string `mapstructure:"issuer"`
}

var (
	globalConfig *config.Config
	globalViper  *viper.Viper
	zapLogger    *zap.Logger
)

// Init initialize configuration
func Init(configPath string) error {

	v := viper.New()

	// Set configuration file path
	if configPath != "" {
		v.SetConfigFile(configPath)
	} else {
		// Try to load service_conf.yaml from conf directory first
		v.SetConfigName("service_conf")
		v.SetConfigType("yaml")
		v.AddConfigPath("./conf")
		v.AddConfigPath(".")
		v.AddConfigPath("/etc/ragflow/")
	}

	// Read environment variables
	v.SetEnvPrefix("RAGFLOW")
	v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	v.AutomaticEnv()

	// Read configuration file
	if err := v.ReadInConfig(); err != nil {
		var configFileNotFoundError viper.ConfigFileNotFoundError
		if !errors.As(err, &configFileNotFoundError) {
			return fmt.Errorf("read config file error: %w", err)
		}
		zapLogger.Info("Config file not found, using environment variables only")
	}

	// Save viper instance
	globalViper = v

	globalConfig = &config.Config{}
	err := globalConfig.ParseGeneralConfig(v)
	if err != nil {
		return fmt.Errorf("parse general config error: %w", err)
	}

	err = globalConfig.ParseDatabaseConfig(v)
	if err != nil {
		return fmt.Errorf("parse database config error: %w", err)
	}

	err = globalConfig.ParseDocEngineConfig(v)
	if err != nil {
		return fmt.Errorf("parse doc engine config error: %w", err)
	}

	err = globalConfig.ParseStorageEngineConfig(v)
	if err != nil {
		return fmt.Errorf("parse storage engine config error: %w", err)
	}

	err = globalConfig.ParseCacheEngineConfig(v)
	if err != nil {
		return fmt.Errorf("parse cache engine config error: %w", err)
	}

	err = globalConfig.ParseQueueEngineConfig(v)
	if err != nil {
		return fmt.Errorf("parse queue engine config error: %w", err)
	}

	err = globalConfig.ParseAnalyticEngineConfig(v)
	if err != nil {
		return fmt.Errorf("parse analytic engine config error: %w", err)
	}

	err = globalConfig.ParseOpenTelemetryConfig(v)
	if err != nil {
		return fmt.Errorf("parse open telemetry config error: %w", err)
	}

	err = globalConfig.ParseAdminConfig(v)
	if err != nil {
		return fmt.Errorf("parse admin config error: %w", err)
	}

	err = globalConfig.ParseAPIServerConfig(v)
	if err != nil {
		return fmt.Errorf("parse API server config error: %w", err)
	}

	err = globalConfig.ParseIngestorConfig(v)
	if err != nil {
		return fmt.Errorf("parse ingestor config error: %w", err)
	}

	err = globalConfig.ParseSyncerConfig(v)
	if err != nil {
		return fmt.Errorf("parse syncer config error: %w", err)
	}

	err = globalConfig.ParseLogConfig(v)
	if err != nil {
		return fmt.Errorf("parse log config error: %w", err)
	}

	err = globalConfig.ParseSMTPConfig(v)
	if err != nil {
		return fmt.Errorf("parse SMTP config error: %w", err)
	}

	err = globalConfig.GetEnvironments()
	if err != nil {
		return fmt.Errorf("get environments error: %w", err)
	}

	err = globalConfig.ParseBillingConfig(v)
	if err != nil {
		return fmt.Errorf("parse billing config error: %w", err)
	}

	err = globalConfig.ParseDefaultModelsConfig(v)
	if err != nil {
		return fmt.Errorf("parse default models config error: %w", err)
	}

	err = globalConfig.ParseOAuthConfig(v)
	if err != nil {
		return fmt.Errorf("parse OAuth config error: %w", err)
	}

	return nil
}

// GetConfig gets the global configuration
func GetConfig() *config.Config {
	return globalConfig
}

// SetLogger sets the logger instance
func SetLogger(l *zap.Logger) {
	zapLogger = l
}

func GetAllConfigs() ([]map[string]interface{}, error) {
	var allConfigs []map[string]interface{}

	// Database
	databaseType := globalConfig.DatabaseType()
	switch databaseType {
	case "mysql":
		mysqlConfig := globalConfig.GetMySQLConfig()
		exportedMySQLConfigs := mysqlConfig.ExportConfigs()
		allConfigs = append(allConfigs, exportedMySQLConfigs)
	default:
		return nil, fmt.Errorf("not supported database: %s", databaseType)
	}

	// Doc engine
	docEngineType := globalConfig.DocEngineType()
	switch docEngineType {
	case "elasticsearch":
		elasticConfig := globalConfig.GetElasticsearchConfig()
		exportedESConfigs := elasticConfig.ExportConfigs()
		allConfigs = append(allConfigs, exportedESConfigs)
	case "infinity":
		infinityConfig := globalConfig.GetInfinityConfig()
		exportedInfinityConfigs := infinityConfig.ExportConfigs()
		allConfigs = append(allConfigs, exportedInfinityConfigs)
	default:
		return nil, fmt.Errorf("not supported doc engine: %s", docEngineType)
	}

	// storage engine
	storageType := globalConfig.StorageEngineType()
	switch storageType {
	case "minio":
		minioConfig := globalConfig.GetMinioConfig()
		exportedMinioConfigs := minioConfig.ExportConfigs()
		allConfigs = append(allConfigs, exportedMinioConfigs)
	case "s3":
		s3Config := globalConfig.GetS3Config()
		exportedS3Configs := s3Config.ExportConfigs()
		allConfigs = append(allConfigs, exportedS3Configs)
	case "oss":
		ossConfig := globalConfig.GetOSSConfig()
		exportedOSSConfigs := ossConfig.ExportConfigs()
		allConfigs = append(allConfigs, exportedOSSConfigs)
	case "gcs":
		gcsConfig := globalConfig.GetGCSConfig()
		exportedGCSConfigs := gcsConfig.ExportConfigs()
		allConfigs = append(allConfigs, exportedGCSConfigs)
	default:
		return nil, fmt.Errorf("not supported storage engine: %s", storageType)
	}

	// cache engine
	cacheType := globalConfig.CacheEngineType()
	switch cacheType {
	case "redis":
		redisConfig := globalConfig.GetRedisConfig()
		exportedRedisConfigs := redisConfig.ExportConfigs()
		allConfigs = append(allConfigs, exportedRedisConfigs)
	default:
		return nil, fmt.Errorf("not supported cache engine: %s", cacheType)
	}

	// message queue
	messageQueueType := globalConfig.QueueEngineType()
	switch messageQueueType {
	case "nats":
		natsConfig := globalConfig.GetNATSConfig()
		exportedNatsConfigs := natsConfig.ExportConfigs()
		allConfigs = append(allConfigs, exportedNatsConfigs)
	default:
		return nil, fmt.Errorf("not supported message queue: %s", messageQueueType)
	}

	// analytical engine
	olapType := globalConfig.AnalyticEngineType()
	switch olapType {
	case "clickhouse":
		clickhouseConfig := globalConfig.GetClickhouseConfig()
		exportedClickhouseConfigs := clickhouseConfig.ExportConfigs()
		allConfigs = append(allConfigs, exportedClickhouseConfigs)
	default:
		return nil, fmt.Errorf("not supported analytical engine: %s", olapType)
	}

	// tracing engine
	oTelConfig := globalConfig.GetOpenTelemetryConfig()
	exportedOTELConfigs := oTelConfig.ExportConfigs()
	allConfigs = append(allConfigs, exportedOTELConfigs)

	return allConfigs, nil
}

// PrintAll prints all configuration settings
func PrintAll() {
	if globalViper == nil {
		zapLogger.Info("Configuration not initialized")
		return
	}

	allSettings := globalViper.AllSettings()
	zapLogger.Info("=== All Configurations ===")
	for key, value := range allSettings {
		zapLogger.Info("config", zap.String("key", key), zap.Any("value", value))
	}
	zapLogger.Info("=== End Configurations ===")
}
