// cmd/server/main.go
package main

import (
	"flag"
	"fmt"
	"os"
	"time"

	"git.sovbit.dev/Enki/nostr-poster/internal/api"
	"git.sovbit.dev/Enki/nostr-poster/internal/auth"
	"git.sovbit.dev/Enki/nostr-poster/internal/config"
	"git.sovbit.dev/Enki/nostr-poster/internal/crypto"
	"git.sovbit.dev/Enki/nostr-poster/internal/db"
	"git.sovbit.dev/Enki/nostr-poster/internal/media/prepare"
	"git.sovbit.dev/Enki/nostr-poster/internal/media/upload/blossom"
	"git.sovbit.dev/Enki/nostr-poster/internal/media/upload/nip94"
	"git.sovbit.dev/Enki/nostr-poster/internal/nostr/events"
	"git.sovbit.dev/Enki/nostr-poster/internal/nostr/poster"
	"git.sovbit.dev/Enki/nostr-poster/internal/nostr/relay"
	"git.sovbit.dev/Enki/nostr-poster/internal/scheduler"
	"git.sovbit.dev/Enki/nostr-poster/internal/utils"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

func main() {
	// Parse command line flags
	configPath := flag.String("config", "", "Path to config file")
	dbPath := flag.String("db", "", "Path to database file")
	port := flag.Int("port", 0, "Port to listen on")
	password := flag.String("password", "", "Password for encrypting private keys")
	flag.Parse()
	
	// Setup logger
	logConfig := zap.NewProductionConfig()
	logConfig.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	logger, err := logConfig.Build()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to create logger: %v\n", err)
		os.Exit(1)
	}
	defer logger.Sync()
	
	// Load config
	cfg, err := config.LoadConfig(*configPath)
	if err != nil {
		logger.Fatal("Failed to load config", zap.Error(err))
	}
	
	// Override config with command line flags if provided
	if *dbPath != "" {
		cfg.DB.Path = *dbPath
	}
	if *port != 0 {
		cfg.ServerPort = *port
	}
	
	// Ensure directories exist
	if err := utils.EnsureDir(cfg.Bot.ContentDir); err != nil {
		logger.Fatal("Failed to create content directory", zap.Error(err))
	}
	if err := utils.EnsureDir(cfg.Bot.ArchiveDir); err != nil {
		logger.Fatal("Failed to create archive directory", zap.Error(err))
	}
	
	// Initialize database
	database, err := db.New(cfg.DB.Path)
	if err != nil {
		logger.Fatal("Failed to connect to database", zap.Error(err))
	}
	if err := database.Initialize(); err != nil {
		logger.Fatal("Failed to initialize database", zap.Error(err))
	}
	
	// Initialize key store
	keyPassword := *password
	if keyPassword == "" {
		// Use a default password or prompt for one
		// In a production environment, you'd want to handle this more securely
		keyPassword = "nostr-poster-default-password"
	}
	keyStore, err := crypto.NewKeyStore(cfg.Bot.KeysFile, keyPassword)
	if err != nil {
		logger.Fatal("Failed to initialize key store", zap.Error(err))
	}
	
	// Initialize event manager
	eventManager := events.NewEventManager(func(pubkey string) (string, error) {
		return keyStore.GetPrivateKey(pubkey)
	})
	
	// Initialize relay manager
	relayManager := relay.NewManager(logger)
	
	// Initialize media preparation manager
	mediaPrep := prepare.NewManager(logger)
	
	// Initialize uploaders
	// NIP-94 uploader
	nip94Uploader := nip94.NewUploader(
		cfg.Media.NIP94.ServerURL,
		"", // Download URL will be discovered
		nil, // Supported types will be discovered
		logger,
		func(url, method string, payload []byte) (string, error) {
			// Replace with a valid bot's public key.
			botPubkey := "your_valid_bot_pubkey_here"
			privkey, err := keyStore.GetPrivateKey(botPubkey)
			if err != nil {
				return "", err
			}
			return nip94.CreateNIP98AuthHeader(url, method, payload, privkey)
		},
	)
	
	// Blossom uploader
	blossomUploader := blossom.NewUploader(
		cfg.Media.Blossom.ServerURL,
		logger,
		func(url, method string) (string, error) {
			// Replace with the appropriate bot's public key
			botPubkey := "your_valid_bot_pubkey_here"
			privkey, err := keyStore.GetPrivateKey(botPubkey)
			if err != nil {
				return "", err
			}
			return blossom.CreateBlossomAuthHeader(url, method, privkey)
		},
	)
	
	// Initialize authentication service
	authService := auth.NewService(
		database,
		logger,
		keyPassword, // Use the same password for simplicity
		24*time.Hour, // Token duration
	)
	
	// Initialize bot service
	botService := api.NewBotService(
		database,
		keyStore,
		eventManager,
		relayManager,
		logger,
	)
	
	// Create post content function
	postContentFunc := poster.CreatePostContentFunc(
		eventManager,
		relayManager,
		mediaPrep,
		logger,
	)
	
	// Initialize scheduler
	posterScheduler := scheduler.NewScheduler(
		database,
		logger,
		cfg.Bot.ContentDir,
		cfg.Bot.ArchiveDir,
		nip94Uploader,
		blossomUploader,
		postContentFunc,
	)
	
	// Initialize API
	apiServer := api.NewAPI(
		logger,
		botService,
		authService,
		posterScheduler,
	)
	
	// Start the scheduler
	if err := posterScheduler.Start(); err != nil {
		logger.Error("Failed to start scheduler", zap.Error(err))
	}
	
	// Start the server
	addr := fmt.Sprintf(":%d", cfg.ServerPort)
	logger.Info("Starting server", zap.String("address", addr))
	if err := apiServer.Run(addr); err != nil {
		logger.Fatal("Failed to start server", zap.Error(err))
	}
}