package heroagent

import (
	"fmt"
	"log"
	"sync"

	"git.threefold.info/herocode/heroagent/pkg/servers/redisserver"
	"git.threefold.info/herocode/heroagent/pkg/servers/ui"
	"git.threefold.info/herocode/heroagent/pkg/servers/webdavserver"
	"github.com/gofiber/fiber/v2"
)

// ServerFactory manages the lifecycle of all servers
type ServerFactory struct {
	config Config

	// Server instances
	redisServer  *redisserver.Server
	webdavServer *webdavserver.Server
	uiApp        *AppInstance
	jobManager   *JobManager

	// Control channels
	stopCh chan struct{}
	wg     sync.WaitGroup
}

// AppInstance wraps the UI app and its listening status
type AppInstance struct {
	App  *fiber.App
	Port string
}

// New creates a new ServerFactory with the given configuration
func New(config Config) *ServerFactory {
	return &ServerFactory{
		config: config,
		stopCh: make(chan struct{}),
	}
}

// Start initializes and starts all enabled servers
func (f *ServerFactory) Start() error {
	log.Println("Starting HeroAgent ServerFactory...")

	// Start Redis server if enabled
	if f.config.EnableRedis {
		if err := f.startRedisServer(); err != nil {
			return fmt.Errorf("failed to start Redis server: %w", err)
		}
	}

	// Start WebDAV server if enabled
	if f.config.EnableWebDAV {
		if err := f.startWebDAVServer(); err != nil {
			return fmt.Errorf("failed to start WebDAV server: %w", err)
		}
	}

	// Start UI server if enabled
	if f.config.EnableUI {
		if err := f.startUIServer(); err != nil {
			return fmt.Errorf("failed to start UI server: %w", err)
		}
	}

	// Start job manager if enabled
	if f.config.EnableJobs {
		if err := f.startJobManager(); err != nil {
			return fmt.Errorf("failed to start job manager: %w", err)
		}
	}

	log.Println("All servers started successfully")
	return nil
}

// Stop gracefully stops all running servers
func (f *ServerFactory) Stop() error {
	log.Println("Stopping all servers...")

	// Signal all goroutines to stop
	close(f.stopCh)

	// Stop WebDAV server if it's running
	if f.webdavServer != nil {
		if err := f.webdavServer.Stop(); err != nil {
			log.Printf("Error stopping WebDAV server: %v", err)
		}
	}

	// Stop job manager if it's running
	if f.jobManager != nil {
		if err := f.jobManager.Stop(); err != nil {
			log.Printf("Error stopping job manager: %v", err)
		}
	}

	// Wait for all goroutines to finish
	f.wg.Wait()

	log.Println("All servers stopped")
	return nil
}

// startRedisServer initializes and starts the Redis server
func (f *ServerFactory) startRedisServer() error {
	log.Println("Starting Redis server...")

	// Create Redis server configuration
	redisConfig := redisserver.ServerConfig{
		TCPPort:        f.config.Redis.TCPPort,
		UnixSocketPath: f.config.Redis.UnixSocketPath,
	}

	// Create and start Redis server
	f.redisServer = redisserver.NewServer(redisConfig)

	log.Printf("Redis server started on port %d and socket %s",
		redisConfig.TCPPort, redisConfig.UnixSocketPath)
	return nil
}

// startWebDAVServer initializes and starts the WebDAV server
func (f *ServerFactory) startWebDAVServer() error {
	log.Println("Starting WebDAV server...")

	// Create WebDAV server
	webdavServer, err := webdavserver.NewServer(f.config.WebDAV.Config)
	if err != nil {
		return fmt.Errorf("failed to create WebDAV server: %w", err)
	}

	f.webdavServer = webdavServer

	// Start WebDAV server in a goroutine
	f.wg.Add(1)
	go func() {
		defer f.wg.Done()

		// Start the server
		if err := webdavServer.Start(); err != nil {
			log.Printf("WebDAV server error: %v", err)
		}
	}()

	log.Printf("WebDAV server started on port %d", f.config.WebDAV.Config.TCPPort)
	return nil
}

// startUIServer initializes and starts the UI server
func (f *ServerFactory) startUIServer() error {
	log.Println("Starting UI server...")

	// Create UI app
	uiApp := ui.NewApp(f.config.UI.AppConfig)

	// Store UI app instance
	f.uiApp = &AppInstance{
		App:  uiApp,
		Port: f.config.UI.Port,
	}

	// Start UI server in a goroutine
	f.wg.Add(1)
	go func() {
		defer f.wg.Done()

		// Start the server
		addr := ":" + f.config.UI.Port
		log.Printf("UI server listening on %s", addr)
		if err := uiApp.Listen(addr); err != nil {
			log.Printf("UI server error: %v", err)
		}
	}()

	return nil
}

// GetRedisServer returns the Redis server instance
func (f *ServerFactory) GetRedisServer() *redisserver.Server {
	return f.redisServer
}

// GetWebDAVServer returns the WebDAV server instance
func (f *ServerFactory) GetWebDAVServer() *webdavserver.Server {
	return f.webdavServer
}

// GetUIApp returns the UI app instance
func (f *ServerFactory) GetUIApp() *AppInstance {
	return f.uiApp
}

// startJobManager initializes and starts the job manager
func (f *ServerFactory) startJobManager() error {
	log.Println("Starting job manager...")

	// Create Redis connection for job manager
	redisConn := &RedisConnection{
		TCPPort:        f.config.Redis.TCPPort,
		UnixSocketPath: f.config.Redis.UnixSocketPath,
	}

	// Create job manager
	jobManager, err := NewJobManager(f.config.Jobs, redisConn)
	if err != nil {
		return fmt.Errorf("failed to create job manager: %w", err)
	}

	f.jobManager = jobManager

	// Start job manager
	if err := jobManager.Start(); err != nil {
		return fmt.Errorf("failed to start job manager: %w", err)
	}

	log.Println("Job manager started")
	return nil
}

// GetJobManager returns the job manager instance
func (f *ServerFactory) GetJobManager() *JobManager {
	return f.jobManager
}
