package api

import (
	"context"
	"net/http"
	"os"
	"path/filepath"
	"time"

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

func corsMiddleware(cfg *Server) gin.HandlerFunc {
	return func(c *gin.Context) {
		origin := c.Request.Header.Get("Origin")
		allowed := ""
		for _, o := range cfg.Cfg.CORSOrigins {
			if origin == o {
				allowed = o
				break
			}
		}
		if allowed != "" {
			c.Header("Access-Control-Allow-Origin", allowed)
			c.Header("Access-Control-Allow-Credentials", "true")
			c.Header("Vary", "Origin")
			// Preflight + fetch dari work.rycroftapparel.com (Bearer, JSON, SSE, ticket WS)
			c.Header("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, Accept-Language, Cache-Control, Pragma")
			c.Header("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS")
			c.Header("Access-Control-Max-Age", "86400")
		}
		if c.Request.Method == http.MethodOptions {
			c.AbortWithStatus(http.StatusNoContent)
			return
		}
		c.Next()
	}
}

func Router(s *Server) *gin.Engine {
	r := gin.New()
	r.Use(gin.Recovery())
	r.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
		return param.TimeStamp.Format(time.RFC3339) + "\t" + param.Method + "\t" + param.Path + "\t" + param.ClientIP + "\n"
	}))
	r.Use(corsMiddleware(s))

	if err := os.MkdirAll(filepath.Join(s.Cfg.UploadDir, "avatars"), 0o755); err != nil {
		panic("upload dir: " + err.Error())
	}
	r.Static("/api/v1/uploads", s.Cfg.UploadDir)

	r.GET("/healthz", func(c *gin.Context) {
		ctx, cancel := contextWithTimeout(c, 2*time.Second)
		defer cancel()
		if err := s.DB.PingContext(ctx); err != nil {
			c.JSON(http.StatusServiceUnavailable, gin.H{"ok": false, "status": "db_unreachable"})
			return
		}
		c.JSON(http.StatusOK, gin.H{"ok": true, "status": "ok"})
	})

	v1 := r.Group("/api/v1")

	v1.POST("/auth/login", s.postLogin)
	v1.POST("/auth/register", s.postRegister)
	v1.POST("/auth/refresh", s.postRefresh)
	v1.POST("/auth/logout", s.postLogout)
	v1.GET("/auth/google", s.getGoogleURL)
	v1.GET("/auth/google/callback", s.getGoogleCallback)
	v1.GET("/ws", s.getWS)

	authz := v1.Group("")
	authz.Use(s.authMiddleware)
	{
		authz.GET("/auth/me", s.getMe)
		authz.GET("/me", s.getMe)
		authz.PATCH("/me", s.patchMe)
		authz.GET("/me/activity-log", s.getMeActivityLog)
		authz.DELETE("/me/activity-log", s.deleteMeActivityLog)
		authz.POST("/me/password", s.postMePassword)
		authz.POST("/me/avatar", s.postMeAvatar)
		authz.DELETE("/me/avatar", s.deleteMeAvatar)
		featDashboard := authz.Group("")
		featDashboard.Use(s.requireFeature("dashboard"))
		{
			featDashboard.GET("/me/summary", s.getMeSummary)
			featDashboard.GET("/me/focus-preview", s.getMeFocusPreview)
			featDashboard.GET("/me/activity", s.getMeActivity)
		}
		authz.POST("/ws/ticket", s.postWSTicket)

		featReport := authz.Group("")
		featReport.Use(s.requireFeature("report"))
		{
			featReport.GET("/reports", s.listReports)
			featReport.POST("/reports", s.createReport)
			featReport.GET("/reports/:id", s.getReport)
			featReport.PATCH("/reports/:id", s.patchReport)
			featReport.DELETE("/reports/:id", s.deleteReport)
			featReport.POST("/reports/:id/attachments", s.postReportAttachment)
			featReport.DELETE("/reports/:id/attachments/:attachmentId", s.deleteReportAttachment)
		}

		featTeam := authz.Group("")
		featTeam.Use(s.requireFeature("team"))
		{
			featTeam.GET("/teams", s.listTeams)
			featTeam.GET("/teams/:id/summary", s.teamSummary)
			featTeam.GET("/teams/:id/members", s.teamMembers)
		}

		authz.GET("/org/divisions", s.listOrgDivisions)
		orgDivAdmin := authz.Group("/org/divisions")
		orgDivAdmin.Use(s.requireSuperadmin)
		{
			orgDivAdmin.GET("/:id/members", s.listOrgDivisionMembers)
			orgDivAdmin.PUT("/:id/members", s.putOrgDivisionMembers)
			orgDivAdmin.POST("/:id/members", s.postOrgDivisionMember)
			orgDivAdmin.DELETE("/:id/members/:userId", s.deleteOrgDivisionMember)
			orgDivAdmin.POST("", s.createOrgDivision)
			orgDivAdmin.PATCH("/:id", s.patchOrgDivision)
			orgDivAdmin.DELETE("/:id", s.deleteOrgDivision)
		}

		featCalendar := authz.Group("")
		featCalendar.Use(s.requireFeature("calendar"))
		featCalendar.GET("/calendar/events", s.listCalendar)

		calOrg := authz.Group("/calendar")
		calOrg.Use(s.requireSuperadmin)
		calOrg.GET("/org-events", s.listCalendarOrg)

		featNotif := authz.Group("")
		featNotif.Use(s.requireFeature("notifications"))
		{
			featNotif.GET("/notifications", s.listNotifications)
			featNotif.PATCH("/notifications/read", s.patchNotificationsRead)
			featNotif.PATCH("/notifications/:id/read", s.patchNotificationReadOne)
			featNotif.GET("/stream/notifications", s.streamNotifications)
		}

		featChat := authz.Group("")
		featChat.Use(s.requireFeature("chat"))
		{
			featChat.GET("/chat/messages", s.listChatMessages)
			featChat.POST("/chat/messages", s.postChatMessage)
			featChat.POST("/chat/messages/attachments", s.postChatMessageWithAttachments)
			featChat.GET("/chat/mention-users", s.listChatMentionUsers)
			featChat.DELETE("/chat/messages", s.deleteAllChatMessages)
		}

		featAnalytics := authz.Group("")
		featAnalytics.Use(s.requireFeature("analytics"))
		{
			featAnalytics.GET("/analytics/summary", s.analyticsSummary)
			featAnalytics.GET("/analytics/trends", s.analyticsTrends)
		}

		orgAn := authz.Group("/analytics/org")
		{
			orgAdmin := orgAn.Group("")
			orgAdmin.Use(s.requireSuperadmin)
			{
				orgAdmin.GET("/summary", s.analyticsOrgSummary)
				orgAdmin.GET("/top-reporters", s.analyticsOrgTopReporters)
			}
			orgTeam := orgAn.Group("")
			orgTeam.Use(s.requireFeature("team"))
			{
				orgTeam.GET("/by-division", s.analyticsOrgByDivision)
				orgTeam.GET("/reporters-by-division", s.analyticsOrgReportersByDivision)
				orgTeam.GET("/reports-by-reporter", s.analyticsOrgReportsByReporter)
			}
		}

		authz.GET("/settings", s.getSettings)
		authz.PATCH("/settings", s.patchSettings)

		authz.GET("/profile", s.getProfile)
		authz.PATCH("/profile", s.patchProfile)

		admin := authz.Group("/admin")
		admin.Use(s.requireSuperadmin)
		{
			admin.GET("/users", s.adminListUsers)
			admin.POST("/users", s.adminCreateUser)
			admin.GET("/users/:id", s.adminGetUser)
			admin.PATCH("/users/:id", s.adminPatchUser)
			admin.POST("/users/:id/password", s.adminPostUserPassword)
			admin.GET("/activity-log", s.getAdminActivityLog)
			admin.DELETE("/activity-log", s.deleteAdminActivityLog)
			admin.POST("/reports/calendar-sync-backfill", s.adminBackfillReportCalendar)
		}
	}

	return r
}

func contextWithTimeout(c *gin.Context, d time.Duration) (context.Context, context.CancelFunc) {
	return context.WithTimeout(c.Request.Context(), d)
}
