package main

import (
	"fmt"
	"log"
	"nearle/db"
	"nearle/facade"
	"nearle/messaging"
	"nearle/models"
	"nearle/routes"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
	_ "time/tzdata"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"github.com/joho/godotenv"
	"gorm.io/gorm"
)

func init() {
	godotenv.Load()
}

func main() {

	app := fiber.New()

	app.Use(cors.New(cors.Config{
		AllowHeaders:     "Origin,Content-Type,Accept,Content-Length,Accept-Language,Accept-Encoding,Connection,Access-Control-Allow-Origin",
		AllowOrigins:     "*",
		AllowCredentials: true,
		AllowMethods:     "GET,POST,HEAD,PUT,DELETE,PATCH,OPTIONS",
	}))

	fmt.Println("🌐 Connecting to databases...")
	db.Connect()
	fmt.Println("✅ Database connections established!")

	// Shared with the express backend. POS terminal presence lives here under a
	// TTL; optional, because losing the health board is an inconvenience and
	// losing a sale is not.
	db.InitRedis()

	// Ensure schema is updated
	db.DB.AutoMigrate(&models.StockRequest{})

	// Counter sales from the in-store terminals. Separate tables from `orders`
	// because a bill carries a cashier, a terminal, rounding, promos, loyalty
	// and a payment split that `orders` has nowhere to put.
	if err := db.DB.AutoMigrate(&models.PosOrders{}, &models.PosOrderItems{}); err != nil {
		log.Fatal("POS schema migration failed:", err)
	}

	f := facade.NewFacade(db.DB, db.CatalogueDB)

	routes.RegisterRoutes(app, f)

	// POS terminals reach the ingest over MQTT when MQTT_URL is set, and over
	// HTTP otherwise. Both land on the same service, so a bill cannot behave
	// differently depending on how it arrived.
	//
	// A broker that is configured but unreachable is fatal on purpose: coming
	// up healthy while every till quietly queues is the worse failure.
	posMqtt, err := messaging.StartPosMqttConsumer(f.PosService())
	if err != nil {
		log.Fatal("POS MQTT consumer failed to start:", err)
	}

	// Start server
	go func() {
		if err := app.Listen(":1122"); err != nil {
			log.Fatal("Server failed to start:", err)
		}
	}()

	gracefulShutdown(posMqtt)
}

func selectDBMiddleware(c *fiber.Ctx) error {
	path := c.Path()
	result := strings.Split(path, "/")

	var flavour string
	if len(result) > 1 {
		flavour = result[1]
	}

	var currentDB *gorm.DB
	switch flavour {
	case "dev", "live":
		currentDB = db.DB
	}

	if currentDB != nil {
		c.Locals("DB", currentDB)
	}

	return c.Next()
}

func gracefulShutdown(posMqtt *messaging.PosMqttConsumer) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)

	<-c
	fmt.Println("\nShutting down gracefully...")

	// Drained before anything else: a bill mid-commit still gets its ack, and
	// without one the terminal would hold it and send it again on restart.
	posMqtt.Close()
	db.CloseRedis()

	// Normally: close db.DB_DEV and db.DB_LIVE
	// Example:
	// closeDB(db.DB_DEV)
	// closeDB(db.DB_LIVE)

	time.Sleep(2 * time.Second)
	fmt.Println("Shutdown complete.")

	os.Exit(0)
}
