package facade

import (
	"nearle/controllers"
	"nearle/repositories"
	"nearle/services"

	"gorm.io/gorm"
)

type Facade struct {
	UserController         *controllers.UserController
	ProductController      *controllers.ProductController
	OrderController        *controllers.OrderController
	DeliveriesController   *controllers.DeliveriesController
	UtilsController        *controllers.UtilsController
	TenantController       *controllers.TenantController
	PartnerController      *controllers.PartnerController
	CustomerController     *controllers.CustomerController
	StockRequestController *controllers.StockRequestController
	CatalogueController    *controllers.CatalogueController
	PosController          *controllers.PosController

	// Held so the NATS consumer can reach the ingest without going through
	// HTTP. Unexported: everything else should use the controller.
	posService services.PosService
}

// NewFacade wires up modules against the main (nearledb) connection.
// catalogueDB is a separate connection to the pgvector catalogue database;
// it may be nil if catalogue env vars are not configured, in which case
// catalogue endpoints will error at query time rather than at startup.
func NewFacade(db *gorm.DB, catalogueDB *gorm.DB) *Facade {

	// User Module
	userRepo := repositories.NewUserRepository(db)
	userService := services.NewUserService(userRepo)
	userController := controllers.NewUserController(userService)

	// Catalogue Module (separate pgvector DB — never the main `db`). Built
	// before the Product Module because ProductService depends on it to
	// bridge catalogue imports into a tenant's own product catalogue.
	catalogueRepo := repositories.NewCatalogueRepository(catalogueDB)
	catalogueService := services.NewCatalogueService(catalogueRepo)
	catalogueController := controllers.NewCatalogueController(catalogueService)

	// Product Module
	productRepo := repositories.NewProductRepository(db)
	productService := services.NewProductService(productRepo, catalogueService)
	productController := controllers.NewProductController(productService)

	// Order Module
	orderRepo := repositories.NewOrderRepository(db)
	orderService := services.NewOrderService(orderRepo)
	orderController := controllers.NewOrderController(orderService)

	// Deliveries Module
	deliveriesRepo := repositories.NewDeliveriesRepository(db)
	deliveriesService := services.NewDeliveriesService(deliveriesRepo)
	deliveriesController := controllers.NewDeliveriesController(deliveriesService)

	// Utils Module
	utilsRepo := repositories.NewUtilsRepository(db)
	utilsService := services.NewUtilsService(utilsRepo)
	utilsController := controllers.NewUtilsController(utilsService)

	//Tenant Module
	tenantRepo := repositories.NewTenantRepository(db)
	tenantService := services.NewTenantService(tenantRepo)
	tenantController := controllers.NewTenantController(tenantService)

	//Partner Module
	partnerRepo := repositories.NewPartnerRepository(db)
	partnerService := services.NewPartnerService(partnerRepo)
	partnerController := controllers.NewPartnerController(partnerService)

	//Customer Module
	customerRepo := repositories.NewCustomerRepository(db)
	customerService := services.NewCustomerService(customerRepo)
	customerController := controllers.NewCustomerController(customerService)

	// Stock Request Module
	stockRequestRepo := repositories.NewStockRequestRepository(db)
	stockRequestService := services.NewStockRequestService(stockRequestRepo, productService)
	stockRequestController := controllers.NewStockRequestController(stockRequestService)

	// POS Module — ingest from the in-store terminals.
	//
	// Presence has no *gorm.DB: terminal health lives in Redis under a TTL, so
	// a till that loses power ages out of the board by itself instead of
	// leaving a Postgres row claiming it is online.
	posRepo := repositories.NewPosRepository(db)
	posPresence := repositories.NewPosPresenceRepository()
	posService := services.NewPosService(posRepo, posPresence)
	posController := controllers.NewPosController(posService)

	return &Facade{
		UserController:         userController,
		ProductController:      productController,
		OrderController:        orderController,
		DeliveriesController:   deliveriesController,
		UtilsController:        utilsController,
		TenantController:       tenantController,
		PartnerController:      partnerController,
		CustomerController:     customerController,
		StockRequestController: stockRequestController,
		CatalogueController:    catalogueController,
		PosController:          posController,
		posService:             posService,
	}
}

// PosService exposes the ingest to callers outside the HTTP layer — the NATS
// consumer runs the same code path a POST does, so a bill arriving over MQTT
// and one arriving over HTTP cannot diverge.
func (f *Facade) PosService() services.PosService { return f.posService }
