package routes

import (
	"context"
	"time"

	"doormile/config"
	"doormile/controllers"
	"doormile/db"
	"doormile/internal/ws"
	"doormile/middlewares"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/websocket/v2"
)

func RegisterRoutes(app *fiber.App, cfg *config.Config) {
	api := app.Group("/api/v1")

	// --------------------
	// HEALTH & READINESS PROBES
	// --------------------
	api.Get("/health", func(c *fiber.Ctx) error {
		return c.JSON(fiber.Map{"status": "OK", "time": time.Now()})
	})

	api.Get("/ready", func(c *fiber.Ctx) error {
		dbStatus := "connected"
		sqlDB, err := db.DB.DB()
		if err != nil || sqlDB.Ping() != nil {
			dbStatus = "unavailable"
		}

		redisStatus := "connected"
		if db.Rdb == nil {
			redisStatus = "unavailable"
		} else {
			ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
			defer cancel()
			if _, pingErr := db.Rdb.Ping(ctx).Result(); pingErr != nil {
				redisStatus = "unavailable"
			}
		}

		status := fiber.StatusOK
		if dbStatus == "unavailable" || redisStatus == "unavailable" {
			status = fiber.StatusServiceUnavailable
		}

		return c.Status(status).JSON(fiber.Map{
			"status": "ready",
			"checks": fiber.Map{
				"postgres": dbStatus,
				"redis":    redisStatus,
			},
		})
	})

	// --------------------
	// CUSTOMER APIS
	// --------------------
	customer := api.Group("/customer")
	customer.Post("/register", controllers.RegisterCustomer(cfg))
	customer.Post("/login", controllers.LoginCustomer)
	customer.Post("/verify-pin", controllers.VerifyCustomerPin(cfg))
	customer.Post("/reset-pin", controllers.ResetCustomerPin)
	customer.Post("/send-email-otp", controllers.SendCustomerEmailOtp(cfg))
	customer.Post("/verify-email-otp", controllers.VerifyCustomerEmailOtp())

	// Authenticated Customer App routes
	customerAuth := customer.Use(middlewares.AuthMiddleware(cfg), middlewares.RoleCheckMiddleware(9))
	customerAuth.Get("/profile", controllers.GetCustomerProfile)
	customerAuth.Put("/profile", controllers.UpdateCustomerProfile)

	customerAuth.Get("/locations", controllers.GetCustomerLocations)
	customerAuth.Post("/locations", controllers.CreateCustomerLocation)
	customerAuth.Put("/locations/:id", controllers.UpdateCustomerLocation)
	customerAuth.Delete("/locations/:id", controllers.DeleteCustomerLocation)

	customerAuth.Put("/device-token", controllers.SaveCustomerDeviceToken)

	customerAuth.Post("/bookings", middlewares.CityGateMiddleware, controllers.CreateCustomerBooking)
	customerAuth.Get("/bookings", controllers.GetCustomerBookings)
	customerAuth.Get("/bookings/:bookingid", controllers.GetCustomerBookingDetails)
	customerAuth.Post("/bookings/:bookingid/cancel", controllers.CancelCustomerBooking)
	customerAuth.Get("/bookings/:bookingid/price", controllers.GetCustomerBookingQuote)
	customerAuth.Get("/track/:trackingno", controllers.TrackConsignment)

	// --------------------
	// MILER APIS
	// --------------------
	miler := api.Group("/miler")
	miler.Post("/login", controllers.LoginMiler(cfg))
	miler.Post("/verify-pin", controllers.VerifyMilerPin(cfg))

	// Authenticated Miler App routes
	milerAuth := miler.Use(middlewares.AuthMiddleware(cfg), middlewares.RoleCheckMiddleware(5))
	milerAuth.Get("/profile", controllers.GetMilerProfile)
	milerAuth.Put("/profile", controllers.UpdateMilerProfile)

	milerAuth.Put("/device-token", controllers.SaveMilerDeviceToken)

	milerAuth.Put("/location", controllers.UpdateMilerLocation)
	milerAuth.Put("/availability", controllers.UpdateMilerAvailability)

	milerAuth.Get("/assignments", controllers.GetMilerAssignments)
	milerAuth.Get("/assignments/:id", controllers.GetMilerAssignmentDetails)
	milerAuth.Post("/assignments/:id/accept", controllers.AcceptMilerAssignment)
	milerAuth.Post("/assignments/:id/reject", controllers.RejectMilerAssignment)

	milerAuth.Post("/bookings/:bookingid/reached", controllers.BookingReachedCustomer)
	milerAuth.Post("/bookings/:bookingid/parcel", controllers.BookingParcelConfirm)
	milerAuth.Post("/bookings/:bookingid/payment", controllers.BookingPaymentCollect)
	milerAuth.Post("/bookings/:bookingid/pickup-complete", controllers.BookingPickupComplete)
	milerAuth.Post("/bookings/:bookingid/vehicle-required", controllers.BookingVehicleRequiredEscalate)

	// Redis periodic telemetry and status logs
	milerAuth.Post("/logs", controllers.CreateMilerPeriodicLog)
	milerAuth.Get("/logs", controllers.GetMilerPeriodicLogs)
	milerAuth.Post("/status", controllers.CreateMilerStatus)
	milerAuth.Get("/status", controllers.GetMilerStatus)

	// Redis consignment tracking and telemetry logs
	milerAuth.Post("/consignments/logs", controllers.PublishConsignmentLogs)
	milerAuth.Get("/consignments/logs/:consignmentid", controllers.GetConsignmentLogs)
	milerAuth.Get("/consignments/userlogs/:userid", controllers.GetUserConsignmentLogs)

	// Duty management
	milerAuth.Post("/duty/start", controllers.MilerStartDuty)
	milerAuth.Put("/duty/end", controllers.MilerEndDuty)
	milerAuth.Get("/duty/current", controllers.MilerGetDutyStatus)

	// Break management
	milerAuth.Post("/breaks/start", controllers.MilerStartBreak)
	milerAuth.Put("/breaks/end", controllers.MilerEndBreak)

	// Miler's own bookings
	milerAuth.Get("/bookings", controllers.MilerGetMyBookings)

	// Delivery confirmation
	milerAuth.Post("/consignments/:id/deliver", controllers.MilerDeliverConsignment)

	// Earnings
	milerAuth.Get("/earnings", controllers.MilerGetEarnings)

	// Notifications
	milerAuth.Get("/notifications", controllers.MilerGetNotifications)
	milerAuth.Patch("/notifications/:id/read", controllers.MilerMarkNotificationRead)

	// Support
	milerAuth.Post("/support", controllers.MilerCreateTicket)
	milerAuth.Get("/support", controllers.MilerGetTickets)

	// --------------------
	// ADMIN CONSOLE APIS — all open, no token required
	// --------------------
	admin := api.Group("/admin")

	admin.Post("/login", controllers.LoginAdmin(cfg))

	// Authenticated Admin Console routes
	adminAuth := admin.Use(middlewares.AuthMiddleware(cfg), middlewares.RoleCheckMiddleware(1, 3, 4))

	adminAuth.Get("/dashboard", controllers.GetAdminDashboard)
	adminAuth.Get("/profile", controllers.GetAdminProfile)
	adminAuth.Get("/me", controllers.GetAdminProfile)

	// App Users Management
	adminAuth.Get("/users", controllers.GetAppUsers)
	adminAuth.Post("/users", controllers.CreateAppUser)
	adminAuth.Put("/users/:id", controllers.UpdateAppUser)
	adminAuth.Delete("/users/:id", controllers.DeleteAppUser)

	// Tenant management
	adminAuth.Get("/tenants", controllers.GetTenants)
	adminAuth.Post("/tenants", controllers.CreateTenant)
	adminAuth.Get("/tenants/:id", controllers.GetTenantDetails)
	adminAuth.Put("/tenants/:id", controllers.UpdateTenant)
	adminAuth.Delete("/tenants/:id", controllers.DeleteTenant)
	adminAuth.Get("/tenants/:id/locations", controllers.GetTenantLocations)
	adminAuth.Post("/tenants/:id/locations", controllers.CreateTenantLocation)
	adminAuth.Put("/tenantlocations/:id", controllers.UpdateTenantLocation)

	// Tenant customers
	adminAuth.Get("/tenantcustomers", controllers.GetTenantCustomers)
	adminAuth.Post("/tenantcustomers", controllers.CreateTenantCustomer)
	adminAuth.Get("/tenantcustomers/:id", controllers.GetTenantCustomerDetails)
	adminAuth.Put("/tenantcustomers/:id", controllers.UpdateTenantCustomer)
	adminAuth.Delete("/tenantcustomers/:id", controllers.DeleteTenantCustomer)

	// B2C App customers
	adminAuth.Get("/customers", controllers.GetAdminCustomers)
	adminAuth.Patch("/customers/:id", controllers.UpdateAdminCustomer)

	// Hubs
	adminAuth.Get("/hubs", controllers.GetHubs)
	adminAuth.Post("/hubs", controllers.CreateHub)
	adminAuth.Get("/hubs/:id", controllers.GetHubDetails)
	adminAuth.Put("/hubs/:id", controllers.UpdateHub)
	adminAuth.Delete("/hubs/:id", controllers.DeleteHub)

	// Vehicles
	adminAuth.Get("/vehicles", controllers.GetVehicles)
	adminAuth.Post("/vehicles", controllers.CreateVehicle)
	adminAuth.Get("/vehicles/:id", controllers.GetVehicleDetails)
	adminAuth.Put("/vehicles/:id", controllers.UpdateVehicle)
	adminAuth.Delete("/vehicles/:id", controllers.DeleteVehicle)

	// Milers
	adminAuth.Get("/milers", controllers.GetMilers)
	adminAuth.Post("/milers", controllers.CreateMiler)
	adminAuth.Get("/milers/:id", controllers.GetMilerDetails)
	adminAuth.Put("/milers/:id", controllers.UpdateMiler)
	adminAuth.Put("/milers/:id/block", controllers.BlockMiler)
	adminAuth.Put("/milers/:id/assign-vehicle", controllers.AssignMilerVehicle)

	// Bookings
	adminAuth.Get("/bookings", controllers.GetAdminBookings)
	adminAuth.Post("/crmbooking", middlewares.CityGateMiddleware, controllers.CreateCRMBooking)
	adminAuth.Get("/bookings/:id", controllers.GetAdminBookingDetails)
	adminAuth.Post("/bookings/:id/assign-miler", controllers.AdminAssignMiler)
	adminAuth.Post("/bookings/:id/assign-vehicle", controllers.AdminAssignVehicle)
	adminAuth.Put("/bookings/:id/status", controllers.AdminUpdateBookingStatus)
	adminAuth.Post("/bookings/:id/cancel", controllers.AdminCancelBooking)

	// Consignments
	adminAuth.Get("/consignments", controllers.GetAdminConsignments)
	adminAuth.Get("/consignments/:id", controllers.GetAdminConsignmentDetails)
	adminAuth.Get("/consignments/track/:trackingno", controllers.GetAdminConsignmentTracking)
	adminAuth.Put("/consignments/:id/status", controllers.AdminUpdateConsignmentStatus)

	// Tripsheets
	adminAuth.Get("/tripsheets", controllers.GetTripsheets)
	adminAuth.Post("/tripsheets", controllers.CreateTripsheet)
	adminAuth.Get("/tripsheets/:id", controllers.GetTripsheetDetails)
	adminAuth.Post("/tripsheets/:id/items", controllers.AddTripsheetItem)
	adminAuth.Delete("/tripsheets/:id/items/:itemid", controllers.DeleteTripsheetItem)
	adminAuth.Put("/tripsheets/:id/dispatch", controllers.DispatchTripsheet)
	adminAuth.Put("/tripsheets/:id/arrive", controllers.ArriveTripsheet)

	// Competitor branch survey data (from Enquiry.xlsx Sheet 1)
	adminAuth.Get("/competitor-branches", controllers.GetCompetitorBranches)
	adminAuth.Post("/competitor-branches", controllers.CreateCompetitorBranch)
	adminAuth.Put("/competitor-branches/:id", controllers.UpdateCompetitorBranch)
	adminAuth.Delete("/competitor-branches/:id", controllers.DeleteCompetitorBranch)

	// Carrier pricing benchmarks (from Enquiry.xlsx Sheet 2)
	adminAuth.Get("/carrier-pricing", controllers.GetCarrierPricing)
	adminAuth.Post("/carrier-pricing", controllers.CreateCarrierPricing)
	adminAuth.Put("/carrier-pricing/:id", controllers.UpdateCarrierPricing)
	adminAuth.Delete("/carrier-pricing/:id", controllers.DeleteCarrierPricing)

	// Pricing
	adminAuth.Get("/pricing", controllers.GetPricing)
	adminAuth.Post("/pricing", controllers.CreatePricing)
	adminAuth.Put("/pricing/:id", controllers.UpdatePricing)
	adminAuth.Delete("/pricing/:id", controllers.DeletePricing)
	adminAuth.Post("/pricing/simulate", controllers.GetPricingQuoteSimulate)
	adminAuth.Post("/pricing/quote", controllers.GetPricingQuoteSimulate)

	// Doormile pricing bands
	adminAuth.Get("/doormile-pricing", controllers.GetDoormilePricing)
	adminAuth.Post("/doormile-pricing", controllers.CreateDoormilePricing)
	adminAuth.Put("/doormile-pricing/:id", controllers.UpdateDoormilePricing)
	adminAuth.Delete("/doormile-pricing/:id", controllers.DeleteDoormilePricing)

	// Exceptions
	adminAuth.Get("/exceptions", controllers.GetExceptions)
	adminAuth.Post("/exceptions", controllers.CreateException)
	adminAuth.Get("/exceptions/:id", controllers.GetExceptionDetails)
	adminAuth.Put("/exceptions/:id/status", controllers.ResolveException)

	// --------------------
	// HUB CONSOLE APIS
	// --------------------
	hub := api.Group("/hub")
	hub.Post("/login", controllers.HubStaffLogin(cfg))

	// Authenticated Hub Console routes (role 6)
	hubAuth := hub.Use(middlewares.HubStaffAuth(cfg))
	hubAuth.Get("/dashboard", controllers.GetHubDashboardStats)
	hubAuth.Get("/bookings/unassigned", controllers.GetHubUnassignedBookings)
	hubAuth.Get("/bookings", controllers.GetHubBookingsRange)
	hubAuth.Get("/inbound/today", controllers.GetHubInboundToday)
	hubAuth.Get("/inbound", controllers.GetHubInboundRange)
	hubAuth.Post("/bookings/:id/inbound", controllers.CreateInboundScan)
	hubAuth.Post("/bookings/:id/assign-miler", controllers.HubAssignMiler)
	hubAuth.Post("/bookings/:id/auto-assign", controllers.HubAutoAssign)
	hubAuth.Get("/batches", controllers.GetHubBatches)
	hubAuth.Post("/batches", controllers.CreateHubBatch)
	hubAuth.Patch("/batches/:id/status", controllers.UpdateBatchStatus)
	hubAuth.Get("/milers", controllers.GetHubMilers)

	// Hub management — enforced Doormile-staff-only inside each handler
	hubAuth.Post("/staff", controllers.CreateHubStaffAccount)
	hubAuth.Get("/hubs", controllers.GetHubsInCity)
	hubAuth.Post("/hubs", controllers.CreateCityHub)

	hubAuth.Get("/tripsheets/in-transit", controllers.GetInTransitTripsheets)
	hubAuth.Get("/inbound/vehicles", controllers.GetInboundVehicles)
	hubAuth.Get("/activity", controllers.GetHubActivity)
	hubAuth.Get("/zones", controllers.GetHubZones)
	hubAuth.Get("/notifications", controllers.GetHubNotifications)
	hubAuth.Patch("/notifications/:id/read", controllers.MarkNotificationRead)
	hubAuth.Get("/routing/:trackingno", controllers.GetRoutingInfo)
	hubAuth.Get("/rider-routes", controllers.GetAllRiderRoutes)
	hubAuth.Get("/milers/:id/route", controllers.GetMilerRoute)

	hubAuth.Get("/messages", controllers.GetHubMessages)
	hubAuth.Get("/messages/:id", controllers.GetHubMessageThread)
	hubAuth.Post("/messages/:id", controllers.SendHubMessage)
	hubAuth.Patch("/messages/:id/read", controllers.MarkHubMessagesRead)

	hubAuth.Get("/report", controllers.GetHubReport)

	// Redis active user caching utilities
	redisUsers := api.Group("/utils/users/redis")
	redisUsers.Post("/", controllers.CreateUserRedis)
	redisUsers.Get("/", controllers.GetUserRedis)
	redisUsers.Put("/:userid", controllers.UpdateUserRedis)
	redisUsers.Delete("/:userid", controllers.DeleteUserRedis)

	// --------------------
	// DOORMILE PRICING — public check & meta (no auth)
	// --------------------
	api.Get("/pricing/meta", controllers.GetPricingMeta)
	api.Post("/pricing/check", controllers.CheckPrice)

	// --------------------
	// BOOKING CACHE APIS — no auth required (testing)
	// --------------------
	bookingCache := api.Group("/bookings/cache")
	bookingCache.Get("/", controllers.ListAllBookingsFromCache)
	bookingCache.Get("/customer/:customer_id", controllers.GetCustomerBookingsFromCache)
	bookingCache.Get("/:booking_id", controllers.GetBookingFromCache)

	// --------------------
	// CRM APIS — all open (field reps register from Flutter; web console reads without a separate CRM login)
	// --------------------
	crm := api.Group("/crm")
	crm.Post("/clients", controllers.RegisterClient)
	crm.Get("/clients", controllers.GetClients)
	crm.Get("/clients/:id", controllers.GetClientDetails)
	crm.Put("/clients/:id", controllers.UpdateClient)
	crm.Delete("/clients/:id", controllers.DeleteClient)

	// --------------------
	// INTERNAL — machine-to-machine endpoints (API key auth, no JWT)
	// --------------------
	internal := api.Group("/internal", middlewares.InternalKeyAuth)
	internal.Post("/notify", controllers.InternalNotify)
	internal.Post("/bookings/:id/reassign", controllers.InternalReassign)
	internal.Post("/agent-decisions", controllers.CreateAgentDecision)
	internal.Get("/agent-decisions/similar", controllers.FindSimilarDecisions)
	internal.Patch("/agent-decisions/:id/outcome", controllers.UpdateDecisionOutcome)

	// --------------------
	// WEBSOCKET — live miler tracking (no auth, public tracking link)
	// --------------------
	app.Use("/ws", func(c *fiber.Ctx) error {
		if websocket.IsWebSocketUpgrade(c) {
			return c.Next()
		}
		return fiber.ErrUpgradeRequired
	})
	app.Get("/ws/bookings/:bookingid/track", websocket.New(ws.TrackingHandler))
	app.Get("/ws/bookings/:bookingid/chat", middlewares.WsChatAuth(cfg), websocket.New(ws.ChatHandler))
}
