//go:build wireinject
// +build wireinject

package app

import (
	"context"
	"os"
	"strings"

	"github.com/google/wire"
	"go.uber.org/zap"

	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/maplefile/client"
	"codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/config"
	"codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/pkg/storage/leveldb"

	// Domain imports
	sessionDomain "codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/domain/session"

	// Repository imports
	sessionRepo "codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/repo/session"

	// Service imports
	authService "codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/service/auth"
	"codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/service/httpclient"
	keyCache "codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/service/keycache"
	passwordStore "codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/service/passwordstore"
	rateLimiter "codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/service/ratelimiter"
	"codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/service/search"
	"codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/service/securitylog"
	"codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/service/storagemanager"
	syncService "codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/service/sync"
	tokenManager "codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/service/tokenmanager"

	// Use case imports
	sessionUC "codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/usecase/session"
)

// InitializeApplication creates a fully configured Application using Wire DI
func InitializeApplication() (*Application, error) {
	wire.Build(
		// Infrastructure
		ProvideLogger,
		config.New,
		ProvideMapleFileClient,

		// Session Repository (global - not user-specific)
		ProvideSessionRepository,

		// Storage Manager (handles user-specific storage lifecycle)
		storagemanager.ProvideManager,
		// Bind *storagemanager.Manager to sync.RepositoryProvider interface
		wire.Bind(new(syncService.RepositoryProvider), new(*storagemanager.Manager)),

		// Use Case Layer
		sessionUC.ProvideCreateUseCase,
		sessionUC.ProvideGetByIdUseCase,
		sessionUC.ProvideDeleteUseCase,
		sessionUC.ProvideSaveUseCase,

		// Service Layer
		authService.ProvideService,
		tokenManager.ProvideManager,
		passwordStore.ProvideService,
		keyCache.ProvideService,
		rateLimiter.ProvideService,
		httpclient.ProvideService,
		securitylog.ProvideService,
		search.New,

		// Sync Services
		syncService.ProvideCollectionSyncService,
		syncService.ProvideFileSyncService,
		syncService.ProvideService,

		// Application
		ProvideApplication,
	)

	return nil, nil
}

// ProvideLogger creates the application logger with environment-aware configuration.
// Defaults to production mode for security. Development mode must be explicitly enabled.
func ProvideLogger() (*zap.Logger, error) {
	mode := os.Getenv("MAPLEFILE_MODE")

	// Only use development logger if explicitly set to "dev" or "development"
	if mode == "dev" || mode == "development" {
		// Development: console format, debug level, with caller and stacktrace
		return zap.NewDevelopment()
	}

	// Default to production: JSON format, info level, no caller info, no stacktrace
	// This is the secure default - production mode unless explicitly in dev
	cfg := zap.NewProductionConfig()
	cfg.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
	cfg.DisableCaller = true
	cfg.DisableStacktrace = true
	return cfg.Build()
}

// ProvideSessionRepository creates the session repository with its storage.
// Session storage is GLOBAL (not user-specific) because it stores the current login session.
func ProvideSessionRepository(logger *zap.Logger) (sessionDomain.Repository, error) {
	provider, err := config.NewLevelDBConfigurationProviderForSession()
	if err != nil {
		return nil, err
	}
	sessionStorage := leveldb.NewDiskStorage(provider, logger.Named("session-storage"))
	return sessionRepo.ProvideRepository(sessionStorage), nil
}

// zapLoggerAdapter adapts *zap.Logger to client.Logger interface
type zapLoggerAdapter struct {
	logger *zap.Logger
}

func (a *zapLoggerAdapter) Debug(msg string, keysAndValues ...interface{}) {
	fields := keysAndValuesToZapFields(keysAndValues...)
	a.logger.Debug(msg, fields...)
}

func (a *zapLoggerAdapter) Info(msg string, keysAndValues ...interface{}) {
	fields := keysAndValuesToZapFields(keysAndValues...)
	a.logger.Info(msg, fields...)
}

func (a *zapLoggerAdapter) Warn(msg string, keysAndValues ...interface{}) {
	fields := keysAndValuesToZapFields(keysAndValues...)
	a.logger.Warn(msg, fields...)
}

func (a *zapLoggerAdapter) Error(msg string, keysAndValues ...interface{}) {
	fields := keysAndValuesToZapFields(keysAndValues...)
	a.logger.Error(msg, fields...)
}

// keysAndValuesToZapFields converts key-value pairs to zap fields
func keysAndValuesToZapFields(keysAndValues ...interface{}) []zap.Field {
	fields := make([]zap.Field, 0, len(keysAndValues)/2)
	for i := 0; i+1 < len(keysAndValues); i += 2 {
		key, ok := keysAndValues[i].(string)
		if !ok {
			continue
		}
		fields = append(fields, zap.Any(key, keysAndValues[i+1]))
	}
	return fields
}

// BuildMode is set at compile time via -ldflags
// Example: go build -ldflags "-X codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/app.BuildMode=dev"
var BuildMode string

// ProvideMapleFileClient creates the backend API client
func ProvideMapleFileClient(configService config.ConfigService, logger *zap.Logger) (*client.Client, error) {
	ctx := context.Background()

	// Determine the API URL based on the mode
	// Priority: 1) Environment variable, 2) Build-time variable, 3) Default to production
	mode := os.Getenv("MAPLEFILE_MODE")

	// Log the detected mode
	logger.Info("Startup: checking mode configuration",
		zap.String("MAPLEFILE_MODE_env", mode),
		zap.String("BuildMode_compile_time", BuildMode),
	)

	if mode == "" {
		if BuildMode != "" {
			mode = BuildMode
			logger.Info("Startup: using compile-time BuildMode", zap.String("mode", mode))
		} else {
			mode = "production" // Default to production (secure default)
			logger.Info("Startup: no mode set, defaulting to production", zap.String("mode", mode))
		}
	}

	var baseURL string
	switch mode {
	case "production":
		baseURL = client.ProductionURL // https://maplefile.ca
	case "dev", "development":
		baseURL = client.LocalURL // http://localhost:8000
	default:
		// Fallback: check config file for custom URL
		cfg, err := configService.GetConfig(ctx)
		if err != nil {
			return nil, err
		}
		baseURL = cfg.CloudProviderAddress
	}

	// Create logger adapter for the API client
	clientLogger := &zapLoggerAdapter{logger: logger.Named("api-client")}

	// Create client with the determined URL and logger
	apiClient := client.New(client.Config{
		BaseURL: baseURL,
		Logger:  clientLogger,
	})

	logger.Info("MapleFile API client initialized",
		zap.String("mode", mode),
		zap.String("base_url", baseURL),
	)

	// Security: Warn if using unencrypted HTTP (should only happen in dev mode)
	if strings.HasPrefix(baseURL, "http://") {
		logger.Warn("SECURITY WARNING: Using unencrypted HTTP connection",
			zap.String("mode", mode),
			zap.String("base_url", baseURL),
			zap.String("recommendation", "This should only be used for local development"),
		)
	}

	// Update the config to reflect the current backend URL (skip in production as it's immutable)
	if mode != "production" {
		if err := configService.SetCloudProviderAddress(ctx, baseURL); err != nil {
			logger.Warn("Failed to update cloud provider address in config", zap.Error(err))
		}
	}

	return apiClient, nil
}
