package router

import (
	"apskel-pos-be/config"
	"apskel-pos-be/internal/handler"
	"apskel-pos-be/internal/middleware"
	"apskel-pos-be/internal/service"
	"apskel-pos-be/internal/transformer"
	"apskel-pos-be/internal/validator"

	"github.com/gin-gonic/gin"
)

type Router struct {
	config                            *config.Config
	healthHandler                     *handler.HealthHandler
	authHandler                       *handler.AuthHandler
	userHandler                       *handler.UserHandler
	organizationHandler               *handler.OrganizationHandler
	outletHandler                     *handler.OutletHandler
	outletSettingHandler              *handler.OutletSettingHandlerImpl
	categoryHandler                   *handler.CategoryHandler
	productHandler                    *handler.ProductHandler
	productVariantHandler             *handler.ProductVariantHandler
	inventoryHandler                  *handler.InventoryHandler
	orderHandler                      *handler.OrderHandler
	fileHandler                       *handler.FileHandler
	customerHandler                   *handler.CustomerHandler
	paymentMethodHandler              *handler.PaymentMethodHandler
	analyticsHandler                  *handler.AnalyticsHandler
	reportHandler                     *handler.ReportHandler
	tableHandler                      *handler.TableHandler
	unitHandler                       *handler.UnitHandler
	ingredientHandler                 *handler.IngredientHandler
	productRecipeHandler              *handler.ProductRecipeHandler
	vendorHandler                     *handler.VendorHandler
	purchaseOrderHandler              *handler.PurchaseOrderHandler
	unitConverterHandler              *handler.IngredientUnitConverterHandler
	chartOfAccountTypeHandler         *handler.ChartOfAccountTypeHandler
	chartOfAccountHandler             *handler.ChartOfAccountHandler
	accountHandler                    *handler.AccountHandler
	orderIngredientTransactionHandler *handler.OrderIngredientTransactionHandler
	voucherHandler                    *handler.VoucherHandler
	authMiddleware                    *middleware.AuthMiddleware
}

func NewRouter(cfg *config.Config,
	healthHandler *handler.HealthHandler,
	authService service.AuthService,
	authMiddleware *middleware.AuthMiddleware,
	userService *service.UserServiceImpl,
	userValidator *validator.UserValidatorImpl,
	organizationService service.OrganizationService,
	organizationValidator validator.OrganizationValidator,
	outletService service.OutletService,
	outletValidator validator.OutletValidator,
	outletSettingService service.OutletSettingService,
	categoryService service.CategoryService,
	categoryValidator validator.CategoryValidator,
	productService service.ProductService,
	productValidator validator.ProductValidator,
	productVariantService service.ProductVariantService,
	productVariantValidator validator.ProductVariantValidator,
	inventoryService service.InventoryService,
	inventoryValidator validator.InventoryValidator,
	orderService service.OrderService,
	orderValidator validator.OrderValidator,
	fileService service.FileService,
	fileValidator validator.FileValidator,
	customerService service.CustomerService,
	customerValidator validator.CustomerValidator,
	paymentMethodService service.PaymentMethodService,
	paymentMethodValidator validator.PaymentMethodValidator,
	analyticsService *service.AnalyticsServiceImpl,
	reportService service.ReportService,
	tableService *service.TableServiceImpl,
	tableValidator *validator.TableValidator,
	unitService handler.UnitService,
	ingredientService handler.IngredientService,
	productRecipeService service.ProductRecipeService,
	vendorService service.VendorService,
	vendorValidator validator.VendorValidator,
	purchaseOrderService service.PurchaseOrderService,
	purchaseOrderValidator validator.PurchaseOrderValidator,
	unitConverterService service.IngredientUnitConverterService,
	unitConverterValidator validator.IngredientUnitConverterValidator,
	chartOfAccountTypeService service.ChartOfAccountTypeService,
	chartOfAccountTypeValidator validator.ChartOfAccountTypeValidator,
	chartOfAccountService service.ChartOfAccountService,
	chartOfAccountValidator validator.ChartOfAccountValidator,
	accountService service.AccountService,
	accountValidator validator.AccountValidator,
	orderIngredientTransactionService service.OrderIngredientTransactionService,
	orderIngredientTransactionValidator validator.OrderIngredientTransactionValidator,
	voucherService service.VoucherService,
	voucherValidator validator.VoucherValidator) *Router {

	return &Router{
		config:                            cfg,
		healthHandler:                     healthHandler,
		authHandler:                       handler.NewAuthHandler(authService),
		userHandler:                       handler.NewUserHandler(userService, userValidator),
		organizationHandler:               handler.NewOrganizationHandler(organizationService, organizationValidator),
		outletHandler:                     handler.NewOutletHandler(outletService, outletValidator),
		outletSettingHandler:              handler.NewOutletSettingHandlerImpl(outletSettingService),
		categoryHandler:                   handler.NewCategoryHandler(categoryService, categoryValidator),
		productHandler:                    handler.NewProductHandler(productService, productValidator),
		inventoryHandler:                  handler.NewInventoryHandler(inventoryService, inventoryValidator),
		orderHandler:                      handler.NewOrderHandler(orderService, orderValidator, transformer.NewTransformer()),
		fileHandler:                       handler.NewFileHandler(fileService, fileValidator, transformer.NewTransformer()),
		customerHandler:                   handler.NewCustomerHandler(customerService, customerValidator),
		paymentMethodHandler:              handler.NewPaymentMethodHandler(paymentMethodService, paymentMethodValidator),
		analyticsHandler:                  handler.NewAnalyticsHandler(analyticsService, transformer.NewTransformer()),
		reportHandler:                     handler.NewReportHandler(reportService, userService),
		tableHandler:                      handler.NewTableHandler(tableService, tableValidator),
		unitHandler:                       handler.NewUnitHandler(unitService),
		ingredientHandler:                 handler.NewIngredientHandler(ingredientService),
		productRecipeHandler:              handler.NewProductRecipeHandler(productRecipeService),
		vendorHandler:                     handler.NewVendorHandler(vendorService, vendorValidator),
		purchaseOrderHandler:              handler.NewPurchaseOrderHandler(purchaseOrderService, purchaseOrderValidator),
		unitConverterHandler:              handler.NewIngredientUnitConverterHandler(unitConverterService, unitConverterValidator),
		chartOfAccountTypeHandler:         handler.NewChartOfAccountTypeHandler(chartOfAccountTypeService, chartOfAccountTypeValidator),
		chartOfAccountHandler:             handler.NewChartOfAccountHandler(chartOfAccountService, chartOfAccountValidator),
		accountHandler:                    handler.NewAccountHandler(accountService, accountValidator),
		orderIngredientTransactionHandler: handler.NewOrderIngredientTransactionHandler(&orderIngredientTransactionService, orderIngredientTransactionValidator),
		voucherHandler:                    handler.NewVoucherHandler(voucherService, voucherValidator),
		authMiddleware:                    authMiddleware,
	}
}

func (r *Router) Init() *gin.Engine {
	gin.SetMode(gin.ReleaseMode)
	engine := gin.New()
	engine.Use(
		middleware.JsonAPI(),
		middleware.CORS(),
		middleware.CorrelationID(),
		middleware.Recover(),
		middleware.HTTPStatLogger(),
		middleware.PopulateContext(),
	)

	r.addAppRoutes(engine)
	return engine
}

func (r *Router) addAppRoutes(rg *gin.Engine) {
	rg.GET("/health", r.healthHandler.HealthCheck)

	v1 := rg.Group("/api/v1")
	{
		auth := v1.Group("/auth")
		{
			auth.POST("/login", r.authHandler.Login)
			auth.POST("/logout", r.authHandler.Logout)
			auth.POST("/refresh", r.authHandler.RefreshToken)
			auth.GET("/validate", r.authHandler.ValidateToken)
			auth.GET("/profile", r.authHandler.GetProfile)
		}

		organizations := v1.Group("/organizations")
		{
			organizations.POST("", r.organizationHandler.CreateOrganization)
		}

		protected := v1.Group("")
		protected.Use(r.authMiddleware.RequireAuth())
		{
			users := protected.Group("/users")
			{
				adminUsers := users.Group("")
				adminUsers.Use(r.authMiddleware.RequireAdminOrManager())
				{
					adminUsers.POST("", r.userHandler.CreateUser)
					adminUsers.GET("", r.userHandler.ListUsers)
					adminUsers.GET("/:id", r.userHandler.GetUser)
					adminUsers.PUT("/:id", r.userHandler.UpdateUser)
					adminUsers.DELETE("/:id", r.userHandler.DeleteUser)
					adminUsers.PUT("/:id/activate", r.userHandler.ActivateUser)
					adminUsers.PUT("/:id/deactivate", r.userHandler.DeactivateUser)
					adminUsers.POST("/select-outlet", r.userHandler.UpdateUserOutlet)
				}

				users.PUT("/:id/password", r.userHandler.ChangePassword)
			}

			protectedOrganizations := protected.Group("/organizations")
			{
				adminOrgRoutes := protectedOrganizations.Group("")
				adminOrgRoutes.Use(r.authMiddleware.RequireSuperAdmin())
				{
					adminOrgRoutes.GET("", r.organizationHandler.ListOrganizations)
					adminOrgRoutes.GET("/:id", r.organizationHandler.GetOrganization)
					adminOrgRoutes.PUT("/:id", r.organizationHandler.UpdateOrganization)
					adminOrgRoutes.DELETE("/:id", r.organizationHandler.DeleteOrganization)
				}
			}

			categories := protected.Group("/categories")
			categories.Use(r.authMiddleware.RequireAdminOrManager())
			{
				categories.POST("", r.categoryHandler.CreateCategory)
				categories.GET("", r.categoryHandler.ListCategories)
				categories.GET("/:id", r.categoryHandler.GetCategory)
				categories.PUT("/:id", r.categoryHandler.UpdateCategory)
				categories.DELETE("/:id", r.categoryHandler.DeleteCategory)
			}

			units := protected.Group("/units")
			units.Use(r.authMiddleware.RequireAdminOrManager())
			{
				units.POST("", r.unitHandler.Create)
				units.GET("", r.unitHandler.GetAll)
				units.GET("/:id", r.unitHandler.GetByID)
				units.PUT("/:id", r.unitHandler.Update)
				units.DELETE("/:id", r.unitHandler.Delete)
			}

			products := protected.Group("/products")
			products.Use(r.authMiddleware.RequireAdminOrManager())
			{
				products.POST("", r.productHandler.CreateProduct)
				products.GET("", r.productHandler.ListProducts)
				products.GET("/:id", r.productHandler.GetProduct)
				products.PUT("/:id", r.productHandler.UpdateProduct)
				products.DELETE("/:id", r.productHandler.DeleteProduct)
			}

			inventory := protected.Group("/inventory")
			inventory.Use(r.authMiddleware.RequireAdminOrManager())
			{
				inventory.POST("", r.inventoryHandler.CreateInventory)
				inventory.GET("", r.inventoryHandler.ListInventory)
				inventory.GET("/:id", r.inventoryHandler.GetInventory)
				inventory.PUT("/:id", r.inventoryHandler.UpdateInventory)
				inventory.DELETE("/:id", r.inventoryHandler.DeleteInventory)
				inventory.POST("/adjust", r.inventoryHandler.AdjustInventory)
				inventory.POST("/restock", r.inventoryHandler.RestockInventory)
				inventory.GET("/low-stock/:outlet_id", r.inventoryHandler.GetLowStockItems)
				inventory.GET("/zero-stock/:outlet_id", r.inventoryHandler.GetZeroStockItems)
				inventory.GET("/report/summary/:outlet_id", r.inventoryHandler.GetInventoryReportSummary)
				inventory.GET("/report/details/:outlet_id", r.inventoryHandler.GetInventoryReportDetails)
			}

			orders := protected.Group("/orders")
			orders.Use(r.authMiddleware.RequireAdminOrManager())
			{
				orders.GET("", r.orderHandler.ListOrders)
				orders.GET("/:id", r.orderHandler.GetOrderByID)
				orders.POST("", r.orderHandler.CreateOrder)
				orders.POST("/:id/add-items", r.orderHandler.AddToOrder)
				orders.PUT("/:id", r.orderHandler.UpdateOrder)
				orders.PUT("/:id/customer", r.orderHandler.SetOrderCustomer)
				orders.POST("/void", r.orderHandler.VoidOrder)
				orders.POST("/:id/refund", r.orderHandler.RefundOrder)
				orders.POST("/split-bill", r.orderHandler.SplitBill)
			}

			payments := protected.Group("/payments")
			payments.Use(r.authMiddleware.RequireAdminOrManager())
			{
				payments.POST("", r.orderHandler.CreatePayment)
				payments.POST("/:id/refund", r.orderHandler.RefundPayment)
			}

			paymentMethods := protected.Group("/payment-methods")
			paymentMethods.Use(r.authMiddleware.RequireAdminOrManager())
			{
				paymentMethods.POST("", r.paymentMethodHandler.CreatePaymentMethod)
				paymentMethods.GET("", r.paymentMethodHandler.ListPaymentMethods)
				paymentMethods.GET("/:id", r.paymentMethodHandler.GetPaymentMethod)
				paymentMethods.PUT("/:id", r.paymentMethodHandler.UpdatePaymentMethod)
				paymentMethods.DELETE("/:id", r.paymentMethodHandler.DeletePaymentMethod)
				paymentMethods.GET("/organization/:organization_id/active", r.paymentMethodHandler.GetActivePaymentMethodsByOrganization)
			}

			files := protected.Group("/files")
			files.Use(r.authMiddleware.RequireAdminOrManager())
			{
				files.GET("/organization", r.fileHandler.GetFilesByOrganization)
				files.GET("/user", r.fileHandler.GetFilesByUser)
				files.GET("/:id", r.fileHandler.GetFileByID)
				files.POST("/upload", r.fileHandler.UploadFile)
				files.PUT("/:id", r.fileHandler.UpdateFile)
			}

			customers := protected.Group("/customers")
			customers.Use(r.authMiddleware.RequireAdminOrManager())
			{
				customers.POST("", r.customerHandler.CreateCustomer)
				customers.GET("", r.customerHandler.ListCustomers)
				customers.GET("/:id", r.customerHandler.GetCustomer)
				customers.PUT("/:id", r.customerHandler.UpdateCustomer)
				customers.DELETE("/:id", r.customerHandler.DeleteCustomer)
				customers.POST("/set-default", r.customerHandler.SetDefaultCustomer)
				customers.GET("/default", r.customerHandler.GetDefaultCustomer)
			}

			analytics := protected.Group("/analytics")
			analytics.Use(r.authMiddleware.RequireAdminOrManager())
			{
				analytics.GET("/payment-methods", r.analyticsHandler.GetPaymentMethodAnalytics)
				analytics.GET("/sales", r.analyticsHandler.GetSalesAnalytics)
				analytics.GET("/products", r.analyticsHandler.GetProductAnalytics)
				analytics.GET("/categories", r.analyticsHandler.GetProductAnalyticsPerCategory)
				analytics.GET("/dashboard", r.analyticsHandler.GetDashboardAnalytics)
				analytics.GET("/profit-loss", r.analyticsHandler.GetProfitLossAnalytics)
			}

			tables := protected.Group("/tables")
			tables.Use(r.authMiddleware.RequireAdminOrManager())
			{
				tables.POST("", r.tableHandler.Create)
				tables.GET("", r.tableHandler.List)
				tables.GET("/:id", r.tableHandler.GetByID)
				tables.PUT("/:id", r.tableHandler.Update)
				tables.DELETE("/:id", r.tableHandler.Delete)
				tables.POST("/:id/occupy", r.tableHandler.OccupyTable)
				tables.POST("/:id/release", r.tableHandler.ReleaseTable)
			}

			ingredients := protected.Group("/ingredients")
			ingredients.Use(r.authMiddleware.RequireAdminOrManager())
			{
				ingredients.POST("", r.ingredientHandler.Create)
				ingredients.GET("", r.ingredientHandler.GetAll)
				ingredients.GET("/:id", r.ingredientHandler.GetByID)
				ingredients.PUT("/:id", r.ingredientHandler.Update)
				ingredients.DELETE("/:id", r.ingredientHandler.Delete)
			}

			vendors := protected.Group("/vendors")
			vendors.Use(r.authMiddleware.RequireAdminOrManager())
			{
				vendors.POST("", r.vendorHandler.CreateVendor)
				vendors.GET("", r.vendorHandler.ListVendors)
				vendors.GET("/active", r.vendorHandler.GetActiveVendors)
				vendors.GET("/:id", r.vendorHandler.GetVendor)
				vendors.PUT("/:id", r.vendorHandler.UpdateVendor)
				vendors.DELETE("/:id", r.vendorHandler.DeleteVendor)
			}

			purchaseOrders := protected.Group("/purchase-orders")
			purchaseOrders.Use(r.authMiddleware.RequireAdminOrManager())
			{
				purchaseOrders.POST("", r.purchaseOrderHandler.CreatePurchaseOrder)
				purchaseOrders.GET("", r.purchaseOrderHandler.ListPurchaseOrders)
				purchaseOrders.GET("/status/:status", r.purchaseOrderHandler.GetPurchaseOrdersByStatus)
				purchaseOrders.GET("/overdue", r.purchaseOrderHandler.GetOverduePurchaseOrders)
				purchaseOrders.GET("/:id", r.purchaseOrderHandler.GetPurchaseOrder)
				purchaseOrders.PUT("/:id", r.purchaseOrderHandler.UpdatePurchaseOrder)
				purchaseOrders.PUT("/:id/status/:status", r.purchaseOrderHandler.UpdatePurchaseOrderStatus)
				purchaseOrders.DELETE("/:id", r.purchaseOrderHandler.DeletePurchaseOrder)
			}

			unitConverters := protected.Group("/unit-converters")
			unitConverters.Use(r.authMiddleware.RequireAdminOrManager())
			{
				unitConverters.POST("", r.unitConverterHandler.CreateIngredientUnitConverter)
				unitConverters.GET("", r.unitConverterHandler.ListIngredientUnitConverters)
				unitConverters.GET("/ingredient/:ingredient_id", r.unitConverterHandler.GetConvertersForIngredient)
				unitConverters.GET("/ingredient/:ingredient_id/units", r.unitConverterHandler.GetUnitsByIngredientID)
				unitConverters.POST("/convert", r.unitConverterHandler.ConvertUnit)
				unitConverters.GET("/:id", r.unitConverterHandler.GetIngredientUnitConverter)
				unitConverters.PUT("/:id", r.unitConverterHandler.UpdateIngredientUnitConverter)
				unitConverters.DELETE("/:id", r.unitConverterHandler.DeleteIngredientUnitConverter)
			}

			productRecipes := protected.Group("/product-recipes")
			productRecipes.Use(r.authMiddleware.RequireAdminOrManager())
			{
				productRecipes.POST("", r.productRecipeHandler.Create)
				productRecipes.POST("/bulk", r.productRecipeHandler.BulkCreate)
				productRecipes.GET("/:id", r.productRecipeHandler.GetByID)
				productRecipes.PUT("/:id", r.productRecipeHandler.Update)
				productRecipes.DELETE("/:id", r.productRecipeHandler.Delete)
				productRecipes.GET("/product/:product_id", r.productRecipeHandler.GetByProductID)
				productRecipes.GET("/ingredient/:ingredient_id", r.productRecipeHandler.GetByIngredientID)
			}

			// Accounting routes
			chartOfAccountTypes := protected.Group("/chart-of-account-types")
			chartOfAccountTypes.Use(r.authMiddleware.RequireAdminOrManager())
			{
				chartOfAccountTypes.POST("", r.chartOfAccountTypeHandler.CreateChartOfAccountType)
				chartOfAccountTypes.GET("", r.chartOfAccountTypeHandler.ListChartOfAccountTypes)
				chartOfAccountTypes.GET("/:id", r.chartOfAccountTypeHandler.GetChartOfAccountTypeByID)
				chartOfAccountTypes.PUT("/:id", r.chartOfAccountTypeHandler.UpdateChartOfAccountType)
				chartOfAccountTypes.DELETE("/:id", r.chartOfAccountTypeHandler.DeleteChartOfAccountType)
			}

			chartOfAccounts := protected.Group("/chart-of-accounts")
			chartOfAccounts.Use(r.authMiddleware.RequireAdminOrManager())
			{
				chartOfAccounts.POST("", r.chartOfAccountHandler.CreateChartOfAccount)
				chartOfAccounts.GET("", r.chartOfAccountHandler.ListChartOfAccounts)
				chartOfAccounts.GET("/:id", r.chartOfAccountHandler.GetChartOfAccountByID)
				chartOfAccounts.PUT("/:id", r.chartOfAccountHandler.UpdateChartOfAccount)
				chartOfAccounts.DELETE("/:id", r.chartOfAccountHandler.DeleteChartOfAccount)
				chartOfAccounts.GET("/organization/:organization_id", r.chartOfAccountHandler.GetChartOfAccountsByOrganization)
				chartOfAccounts.GET("/organization/:organization_id/type/:type_id", r.chartOfAccountHandler.GetChartOfAccountsByType)
			}

			accounts := protected.Group("/accounts")
			accounts.Use(r.authMiddleware.RequireAdminOrManager())
			{
				accounts.POST("", r.accountHandler.CreateAccount)
				accounts.GET("", r.accountHandler.ListAccounts)
				accounts.GET("/:id", r.accountHandler.GetAccountByID)
				accounts.PUT("/:id", r.accountHandler.UpdateAccount)
				accounts.DELETE("/:id", r.accountHandler.DeleteAccount)
				accounts.GET("/organization/:organization_id", r.accountHandler.GetAccountsByOrganization)
				accounts.GET("/chart-of-account/:chart_of_account_id", r.accountHandler.GetAccountsByChartOfAccount)
				accounts.PUT("/:id/balance", r.accountHandler.UpdateAccountBalance)
				accounts.GET("/:id/balance", r.accountHandler.GetAccountBalance)
			}

			orderIngredientTransactions := protected.Group("/order-ingredient-transactions")
			orderIngredientTransactions.Use(r.authMiddleware.RequireAdminOrManager())
			{
				orderIngredientTransactions.POST("", r.orderIngredientTransactionHandler.CreateOrderIngredientTransaction)
				orderIngredientTransactions.GET("", r.orderIngredientTransactionHandler.ListOrderIngredientTransactions)
				orderIngredientTransactions.GET("/:id", r.orderIngredientTransactionHandler.GetOrderIngredientTransactionByID)
				orderIngredientTransactions.PUT("/:id", r.orderIngredientTransactionHandler.UpdateOrderIngredientTransaction)
				orderIngredientTransactions.DELETE("/:id", r.orderIngredientTransactionHandler.DeleteOrderIngredientTransaction)
				orderIngredientTransactions.GET("/order/:order_id", r.orderIngredientTransactionHandler.GetOrderIngredientTransactionsByOrder)
				orderIngredientTransactions.GET("/order-item/:order_item_id", r.orderIngredientTransactionHandler.GetOrderIngredientTransactionsByOrderItem)
				orderIngredientTransactions.GET("/ingredient/:ingredient_id", r.orderIngredientTransactionHandler.GetOrderIngredientTransactionsByIngredient)
				orderIngredientTransactions.GET("/summary", r.orderIngredientTransactionHandler.GetOrderIngredientTransactionSummary)
				orderIngredientTransactions.POST("/bulk", r.orderIngredientTransactionHandler.BulkCreateOrderIngredientTransactions)
			}

			vouchers := protected.Group("/vouchers")
			vouchers.Use(r.authMiddleware.RequireAdminOrManager())
			{
				vouchers.GET("/spin", r.voucherHandler.GetRandomVouchersForSpin)
				vouchers.GET("/rows", r.voucherHandler.GetRandomVouchersByRows)
				vouchers.GET("", r.voucherHandler.ListVouchers)
				vouchers.GET("/:id", r.voucherHandler.GetVoucherByID)
				vouchers.GET("/code/:code", r.voucherHandler.GetVoucherByCode)
			}

			outlets := protected.Group("/outlets")
			outlets.Use(r.authMiddleware.RequireAdminOrManager())
			{
				outlets.GET("/list", r.outletHandler.ListOutlets)
				outlets.GET("/detail/:id", r.outletHandler.GetOutlet)
				outlets.PUT("/detail/:id", r.outletHandler.UpdateOutlet)
				outlets.GET("/printer-setting/:outlet_id", r.outletSettingHandler.GetPrinterSettings)
				outlets.PUT("/printer-setting/:outlet_id", r.outletSettingHandler.UpdatePrinterSettings)
				outlets.GET("/:outlet_id/tables/available", r.tableHandler.GetAvailableTables)
				outlets.GET("/:outlet_id/tables/occupied", r.tableHandler.GetOccupiedTables)
				// Reports
				outlets.GET("/:outlet_id/reports/daily-transaction.pdf", r.reportHandler.GetDailyTransactionReportPDF)
			}
		}
	}
}
