package main

import (
	"context"
	"fmt"
	"log/slog"
	"net"
	"net/http"
	"os"
	"os/signal"
	"syscall"

	"github.com/flowy-live/llink/internal"
	"github.com/flowy-live/llink/internal/auth"
	"github.com/flowy-live/llink/internal/db"
	"github.com/flowy-live/llink/internal/network"
	"github.com/flowy-live/llink/internal/pusher"
	"github.com/flowy-live/llink/internal/utils"

	pbpusher "github.com/flowy-live/llink/genproto/llink/pusher"
	"google.golang.org/grpc"
)

func main() {
	port := utils.MustGetEnv("PORT")
	grpcPort := utils.MustGetEnv("GRPC_PORT")

	db.Init()
	defer db.Cleanup()

	authRedis := internal.ConnectAndTestRedis(db.RedisDBAuth)     // shared with orion
	pusherRedis := internal.ConnectAndTestRedis(db.RedisDBPusher) // presence + pub/sub

	sessionReader := auth.NewSessionReader(authRedis)
	networkReader := network.NewReader(db.Pool())

	// Hostname is the k8s pod name.
	podID, err := os.Hostname()
	if err != nil {
		podID = fmt.Sprintf("pod-%d", os.Getpid())
	}

	bridge := pusher.NewRedisBridge(pusherRedis, podID)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	authorizer := pusher.NewAuthorizer(networkReader)
	hub := pusher.NewHub(bridge, authorizer)
	bridge.SetHub(hub)
	server := pusher.NewServer(ctx, hub, bridge, sessionReader)

	go hub.Run(ctx)
	go bridge.Listen(ctx)
	go bridge.Heartbeat(ctx)

	// --- gRPC server (internal presence queries) ---
	grpcListener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%s", grpcPort))
	if err != nil {
		slog.Error("failed to listen for gRPC", "port", grpcPort, "error", err)
		os.Exit(1)
	}
	grpcServer := grpc.NewServer()
	pbpusher.RegisterPusherServiceServer(grpcServer, server)
	go func() {
		slog.Info("gRPC server listening", "port", grpcPort)
		if err := grpcServer.Serve(grpcListener); err != nil {
			slog.Error("gRPC server failed", "error", err)
		}
	}()

	// --- HTTP server (WebSocket + health) ---
	mux := http.NewServeMux()
	mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	})
	mux.HandleFunc("GET /ws", server.HandleWebSocket)

	httpAddr := fmt.Sprintf("0.0.0.0:%s", port)
	httpServer := &http.Server{Addr: httpAddr, Handler: mux}

	go func() {
		slog.Info("HTTP server listening", "addr", httpAddr)
		if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			slog.Error("HTTP server failed", "error", err)
			os.Exit(1)
		}
	}()

	// --- Graceful shutdown ---
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
	<-sigCh

	slog.Info("shutting down...")
	cancel()

	grpcServer.GracefulStop()
	httpServer.Shutdown(context.Background())
	slog.Info("shutdown complete")
}
