package admin_routes

import (
	configs "astrology-api/configs"
	admincontroller "astrology-api/controllers/admin"
	middleware "astrology-api/middleware"
	"astrology-api/repositories"
	"astrology-api/repositories_admin"
	"astrology-api/services"
	"astrology-api/services_admin"

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

// The admin API, mounted at /api/admin.
//
// This is the third stack alongside the user and astrologer ones, and it
// exists for one reason: the admin panel is a separate application, and the
// settlement lifecycle — review, release, settle — has to happen somewhere
// that is neither the customer's app nor the astrologer's.
//
// Two things to know about it:
//
//   - Authentication is AdminAuthMiddleware, which takes either an admin's
//     bearer token or the scheduler's X-ADMIN-API-KEY. The panel's cron uses
//     the key to call /settlement/run and /consultation/sweep-stale.
//   - This API schedules nothing itself. The schedule lives in systemflag,
//     the panel's scheduler reads it and calls the run endpoint.
//
// Like the other route files, this one is also the DI container.
func RegisterAdminRoutes(router *gin.RouterGroup) {

	//------------------------------------------------
	// Wiring
	//------------------------------------------------

	settlementRepository := repositories_admin.NewSettlementRepository(configs.DB)

	// Sessions that are still running: the monitoring screens, the transcript
	// for dispute handling, and force-end.
	adminConsultationRepository := repositories_admin.NewConsultationRepository(configs.DB)

	settingsService := services_admin.NewSettingsService(
		settlementRepository,
		adminConsultationRepository,
	)

	// The stale-session sweep bills abandoned sessions, and billing belongs
	// to the user-side consultation service. It is wired in here rather than
	// reimplemented so there is exactly one billing code path.
	consultationService := services.NewConsultationService(
		repositories.NewConsultationRepository(configs.DB),

		// The admin side never mints a token — it sweeps and reports — but
		// the service takes one, so it gets the real thing rather than a nil
		// that would have to be special-cased.
		services.NewAgoraTokenService(configs.Agora),
	)

	settlementService := services_admin.NewSettlementService(
		settlementRepository,
		settingsService,
		consultationService,
	)

	settlementController := admincontroller.NewSettlementController(settlementService)
	settingsController := admincontroller.NewSettingsController(settingsService)

	// Push notifications the panel triggers. Stateless — it only renders and
	// dispatches through the notifications package.
	notificationController := admincontroller.NewNotificationController(
		services_admin.NewNotificationService(),
	)

	// Live session monitoring. Reuses the consultationService built above, so
	// an admin force-ending a session bills through the same single path as
	// either app hanging up.
	adminConsultationController := admincontroller.NewConsultationController(
		services_admin.NewConsultationMonitorService(
			adminConsultationRepository,
			settlementRepository,
			consultationService,
		),
	)

	//------------------------------------------------
	// Routes
	//------------------------------------------------

	api := router.Group("/admin")
	{
		auth := api.Group("/", middleware.AdminAuthMiddleware())
		{
			//------------------------------------------------
			// Finance manager
			//------------------------------------------------

			// Header cards: pending, to-be-settled, settled, liability.
			auth.GET("/settlement/summary", settlementController.GetFinanceSummary)

			// The review queue — chat/call/video sessions billed to a
			// customer whose earning nobody has released yet.
			auth.GET("/settlement/pending", settlementController.GetPending)

			// Released and waiting for the job.
			auth.GET("/settlement/to-be-settled", settlementController.GetToBeSettled)

			// Settled history, consultation by consultation.
			auth.GET("/settlement/settled", settlementController.GetSettled)

			auth.GET("/settlement/on-hold", settlementController.GetOnHold)

			// Any status, with medium / astrologer / customer / date
			// filters. Backs the chat, call and video media history screens.
			auth.GET("/settlement/consultations", settlementController.GetConsultations)

			// The review screen: session, money split, customer review and
			// the full settlement trail.
			auth.GET("/settlement/consultation/:id", settlementController.GetConsultationDetail)

			//------------------------------------------------
			// Review actions
			//------------------------------------------------
			//
			// All bulk: the panel reviews with checkboxes. A row that cannot
			// move is reported in `skipped` rather than failing the call.

			auth.POST("/settlement/approve", settlementController.Approve)

			auth.POST("/settlement/hold", settlementController.Hold)

			auth.POST("/settlement/reject", settlementController.Reject)

			auth.POST("/settlement/revert", settlementController.Revert)

			// The same four, with the action in the body.
			auth.POST("/settlement/review", settlementController.Review)

			//------------------------------------------------
			// The settlement job
			//------------------------------------------------

			// Called by the panel's cron on the configured schedule, and by
			// the panel's own "Run now" button. Pass dry_run to preview.
			auth.POST("/settlement/run", settlementController.RunSettlement)

			// Short-interval cron. The path and the name are unchanged so the
			// panel's existing entry keeps working, but it now runs every
			// pass the consultation lifecycle needs: send the ending
			// warnings, close sessions that hit their cap, close abandoned
			// ones, expire requests nobody answered, release astrologers the
			// customer never joined, and reconcile the busy flag.
			//
			// Every pass is idempotent, so calling it twice at once is safe.
			auth.POST("/consultation/sweep-stale", settlementController.SweepStaleConsultations)

			//------------------------------------------------
			// Live sessions
			//------------------------------------------------

			auth.GET("/consultation/live", adminConsultationController.GetLive)

			// Separate from the list because the dashboard header
			// auto-refreshes and must not re-run the joined, searched,
			// paginated query every few seconds.
			auth.GET("/consultation/live-summary", adminConsultationController.GetLiveSummary)

			auth.GET("/consultation/live/:id", adminConsultationController.GetLiveDetail)

			// Bulk, with mandatory remarks and a skipped list, like the
			// settlement review actions. Works on a healthy session too: the
			// case this exists for is support with the customer on the phone.
			auth.POST("/consultation/force-end", adminConsultationController.ForceEnd)

			// Only what the apps uploaded exists — Agora RTM keeps no
			// history, so a session neither app managed to flush comes back
			// empty rather than as an error.
			auth.GET("/consultation/transcript/:id", adminConsultationController.GetTranscript)

			//------------------------------------------------
			// Chat configuration
			//------------------------------------------------

			auth.GET("/settings/chat", settingsController.GetChatSettings)
			auth.PUT("/settings/chat", settingsController.UpdateChatSettings)

			//------------------------------------------------
			// Settlement history (batches)
			//------------------------------------------------

			auth.GET("/settlement/history", settlementController.GetSettlementHistory)

			auth.GET("/settlement/history/:id", settlementController.GetSettlementDetail)

			//------------------------------------------------
			// General settings
			//------------------------------------------------

			auth.GET("/settings/general", settingsController.GetGeneralSettings)

			auth.PUT("/settings/general", settingsController.UpdateGeneralSettings)

			//------------------------------------------------
			// Push notifications
			//------------------------------------------------
			//
			// A message an admin writes, to one person or a whole audience.
			auth.POST("/notification/send", notificationController.Send)

			// The notification for a decision the panel handled itself —
			// profile verification and withdrawal approval live there, not
			// here, so the panel reports the event and this API sends the
			// message in the wording the apps expect.
			auth.POST("/notification/event", notificationController.NotifyEvent)
		}
	}
}
