package server

import (
	"context"
	"crypto/tls"
	"cursor-api-proxy/internal/config"
	"cursor-api-proxy/internal/handlers"
	"cursor-api-proxy/internal/pool"
	"cursor-api-proxy/internal/process"
	"cursor-api-proxy/internal/logger"
	"cursor-api-proxy/internal/router"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

type ServerOptions struct {
	Version string
	Config  config.BridgeConfig
	Pool    pool.PoolHandle
}

func StartBridgeServer(opts ServerOptions) []*http.Server {
	cfg := opts.Config
	var servers []*http.Server

	if len(cfg.ConfigDirs) > 0 {
		if cfg.MultiPort {
			for i, dir := range cfg.ConfigDirs {
				port := cfg.Port + i
				subCfg := cfg
				subCfg.Port = port
				subCfg.ConfigDirs = []string{dir}
				subCfg.MultiPort = false
				subPool := pool.NewAccountPool([]string{dir})
				srv := startSingleServer(ServerOptions{Version: opts.Version, Config: subCfg, Pool: subPool})
				servers = append(servers, srv)
			}
			return servers
		}
		pool.InitAccountPool(cfg.ConfigDirs)
	}

	servers = append(servers, startSingleServer(opts))
	return servers
}

func startSingleServer(opts ServerOptions) *http.Server {
	cfg := opts.Config

	modelCache := &handlers.ModelCacheRef{}
	lastModel := cfg.DefaultModel

	ph := opts.Pool
	if ph == nil {
		ph = pool.GlobalPoolHandle{}
	}
	handler := router.NewRouter(router.RouterOptions{
		Version:    opts.Version,
		Config:     cfg,
		ModelCache: modelCache,
		LastModel:  &lastModel,
		Pool:       ph,
	})
	handler = router.WrapWithRecovery(cfg.SessionsLogPath, handler)

	useTLS := cfg.TLSCertPath != "" && cfg.TLSKeyPath != ""

	srv := &http.Server{
		Addr:    fmt.Sprintf("%s:%d", cfg.Host, cfg.Port),
		Handler: handler,
	}

	if useTLS {
		cert, err := tls.LoadX509KeyPair(cfg.TLSCertPath, cfg.TLSKeyPath)
		if err != nil {
			fmt.Fprintf(os.Stderr, "TLS error: %v\n", err)
			os.Exit(1)
		}
		srv.TLSConfig = &tls.Config{Certificates: []tls.Certificate{cert}}
	}

	scheme := "http"
	if useTLS {
		scheme = "https"
	}

	go func() {
		var err error
		if useTLS {
			err = srv.ListenAndServeTLS("", "")
		} else {
			err = srv.ListenAndServe()
		}
		if err != nil && err != http.ErrServerClosed {
			if isAddrInUse(err) {
				fmt.Fprintf(os.Stderr, "❌ Port %d is already in use. Set CURSOR_BRIDGE_PORT to use a different port.\n", cfg.Port)
			} else {
				fmt.Fprintf(os.Stderr, "❌ Server error: %v\n", err)
			}
			os.Exit(1)
		}
	}()

	logger.LogServerStart(opts.Version, scheme, cfg.Host, cfg.Port, cfg)

	return srv
}

func SetupGracefulShutdown(servers []*http.Server, timeoutMs int) {
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)

	go func() {
		sig := <-sigCh
		logger.LogShutdown(sig.String())

		process.KillAllChildProcesses()

		ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutMs)*time.Millisecond)
		defer cancel()

		done := make(chan struct{})
		go func() {
			for _, srv := range servers {
				_ = srv.Shutdown(ctx)
			}
			close(done)
		}()

		select {
		case <-done:
			os.Exit(0)
		case <-ctx.Done():
			fmt.Fprintln(os.Stderr, "[shutdown] Timed out waiting for connections to drain — forcing exit.")
			os.Exit(1)
		}
	}()
}

func isAddrInUse(err error) bool {
	return err != nil && (contains(err.Error(), "address already in use") || contains(err.Error(), "bind: address already in use"))
}

func contains(s, sub string) bool {
	return len(s) >= len(sub) && (s == sub || len(s) > 0 && containsHelper(s, sub))
}

func containsHelper(s, sub string) bool {
	for i := 0; i <= len(s)-len(sub); i++ {
		if s[i:i+len(sub)] == sub {
			return true
		}
	}
	return false
}
