package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"dk/internal/database"
	"dk/internal/models/users"
	"dk/internal/routes"
	"dk/internal/template"

	sushi "git.sharkk.net/Sharkk/Sushi"
	"git.sharkk.net/Sharkk/Sushi/auth"
	"git.sharkk.net/Sharkk/Sushi/csrf"
	"git.sharkk.net/Sharkk/Sushi/session"
	"git.sharkk.net/Sharkk/Sushi/timing"
)

func main() {
	var port string
	flag.StringVar(&port, "p", "3000", "Port to run server on")

	if len(os.Args) < 2 {
		startServer(port)
		return
	}

	switch os.Args[1] {
	case "serve":
		flag.CommandLine.Parse(os.Args[2:])
		startServer(port)
	default:
		fmt.Fprintf(os.Stderr, "Unknown command: %s\n", os.Args[1])
		fmt.Fprintln(os.Stderr, "Available commands:")
		fmt.Fprintln(os.Stderr, "  serve   - Start the server")
		fmt.Fprintln(os.Stderr, "  (no command) - Start the server")
		os.Exit(1)
	}
}

func startServer(port string) {
	fmt.Println("Starting Dragon Knight server...")
	if err := start(port); err != nil {
		log.Fatalf("Server failed: %v", err)
	}
}

func start(port string) error {
	cwd, err := os.Getwd()
	if err != nil {
		return fmt.Errorf("failed to get current working directory: %w", err)
	}

	err = database.Init(filepath.Join(cwd, "data/dk.db"))
	if err != nil {
		log.Fatal("Failed to initialize database:", err)
	}
	defer database.DB().Close()

	template.InitializeCache(cwd)

	app := sushi.New()
	sushi.InitSessions(filepath.Join(cwd, "data/_sessions.json"))

	app.Use(session.Middleware())
	app.Use(auth.Middleware(getUserByID))
	app.Use(csrf.Middleware())
	app.Use(timing.Middleware())

	app.Get("/", routes.Index)

	protected := app.Group("")
	protected.Use(auth.RequireAuth("/login"))
	protected.Get("/explore", routes.Explore)
	protected.Post("/move", routes.Move)
	protected.Get("/teleport/:to", routes.Teleport)

	routes.RegisterAuthRoutes(app)
	routes.RegisterTownRoutes(app)
	routes.RegisterFightRoutes(app)

	app.Get("/assets/*path", sushi.Static(cwd))

	addr := ":" + port
	log.Printf("Server starting on %s", addr)

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)

	go func() {
		app.Listen(addr)
	}()

	<-c
	log.Println("\nReceived shutdown signal, shutting down gracefully...")

	log.Println("Saving sessions...")
	sushi.SaveSessions()

	log.Println("Server stopped")
	return nil
}

func getUserByID(userID int) any {
	return users.GetByID(userID)
}
