package main

import (
	"log"
	"os"
	"time"

	"server/internal/db"
	"server/internal/handlers"
	"server/internal/mail"
	"server/internal/middleware"
	"server/internal/qldt"
	"server/internal/syncjobs"
	internalWs "server/internal/websocket"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"github.com/gofiber/fiber/v2/middleware/recover"
	gofiberWs "github.com/gofiber/websocket/v2"
	"github.com/joho/godotenv"
)

func main() {
	if err := godotenv.Load(); err != nil {
		log.Println("Note: .env file not found or couldn't be loaded, using system environments.")
	}

	dbHost := getEnv("DB_HOST", "127.0.0.1")
	dbPort := getEnv("DB_PORT", "3306")
	dbUser := getEnv("DB_USER", "rika")
	dbPass := getEnv("DB_PASSWORD", "rika@123")
	dbName := getEnv("DB_NAME", "simple_care")

	qldtToken := getEnv("QLDT_TOKEN", "")
	qldtBaseURL := getEnv("QLDT_BASE_URL", "https://apiportal.rikkei.edu.vn")
	qldtOrigin := getEnv("QLDT_ORIGIN", "https://qldt.rikkei.edu.vn")

	if qldtToken == "" {
		log.Println("WARNING: QLDT_TOKEN is empty! Sync features will fail until QLDT_TOKEN is configured in environment.")
	}

	gormDB, err := db.ConnectDB(dbHost, dbPort, dbUser, dbPass, dbName)
	if err != nil {
		log.Fatalf("Database connection failed: %v", err)
	}

	if err := db.AutoMigrate(gormDB); err != nil {
		log.Fatalf("Database migration failed: %v", err)
	}

	qldtClient := qldt.NewClient(qldtBaseURL, qldtOrigin)
	classesJob := syncjobs.NewClassesSyncJob()
	studentsJob := syncjobs.NewStudentsSyncJob()
	mailer := mail.LoadConfigFromEnv()

	app := fiber.New(fiber.Config{
		AppName:   "Simple Care Sync Backend",
		BodyLimit: 100 * 1024 * 1024, // 100MB — upload PDF gói đề
	})

	app.Use(recover.New())
	app.Use(cors.New(cors.Config{
		AllowOrigins:  "*",
		AllowHeaders:  "Origin, Content-Type, Accept, Authorization",
		AllowMethods:  "GET, POST, PATCH, PUT, DELETE, OPTIONS",
		ExposeHeaders: "Content-Disposition, Content-Length",
	}))

	// WebSocket Upgrade Check
	app.Use("/ws", func(c *fiber.Ctx) error {
		if gofiberWs.IsWebSocketUpgrade(c) {
			c.Locals("allowed", true)
			return c.Next()
		}
		return fiber.ErrUpgradeRequired
	})

	// WebSocket Route
	app.Get("/ws", gofiberWs.New(internalWs.WebSocketHandler(gormDB)))

	api := app.Group("/api")

	// Auth công khai (management)
	api.Post("/auth/provision", handlers.ProvisionStaffHandler(gormDB, mailer))
	api.Post("/auth/login", handlers.LoginStaffHandler(gormDB))
	api.Post("/auth/forgot-password", handlers.ForgotPasswordHandler(gormDB, mailer))
	api.Post("/auth/reset-password", handlers.ResetPasswordHandler(gormDB))
	api.Get("/auth/github/callback", handlers.GitHubOAuthCallbackHandler(gormDB))

	// Student client (không cần JWT staff)
	api.Post("/student/sync-log", handlers.SyncStudentSessionLogHandler(gormDB))
	api.Get("/student/status", handlers.GetStudentStatusHandler(gormDB))
	api.Get("/student/wifi-policy", handlers.GetStudentWifiPolicyHandler(gormDB))
	api.Post("/student/report-wifi", handlers.ReportWifiHandler(gormDB))
	api.Post("/student/report-blocked-app", handlers.ReportBlockedAppHandler(gormDB))
	api.Post("/student/report-violation", handlers.ReportViolationHandler(gormDB))
	api.Get("/student/chat/messages", handlers.StudentListChatHandler(gormDB))
	api.Post("/student/chat/messages", handlers.StudentSendChatHandler(gormDB))
	api.Get("/student/chat/conversations", handlers.StudentListChatConversationsHandler(gormDB))
	api.Get("/student/exam", handlers.GetStudentExamHandler(gormDB))
	api.Get("/student/exam/paper-files", handlers.GetStudentExamPaperFilesHandler(gormDB))
	api.Get("/student/exam/download", handlers.DownloadStudentExamFileHandler(gormDB))
	api.Post("/student/exam/submit", handlers.SubmitStudentExamHandler(gormDB))
	api.Get("/classes/:rkId/allowed-apps", handlers.GetAllowedAppsHandler(gormDB))

	// Health check (public)
	api.Get("/health", func(c *fiber.Ctx) error {
		return c.JSON(fiber.Map{"ok": true})
	})

	api.Get("/public/app-downloads", handlers.ListAppDownloadsPublicHandler(gormDB))
	api.Get("/public/guide-links", handlers.ListGuideLinksPublicHandler(gormDB))
	api.Get("/public/app-guides", handlers.ListAppGuidesPublicHandler(gormDB))

	// Management — yêu cầu đăng nhập staff
	staff := api.Group("", middleware.RequireStaff())
	staff.Get("/auth/me", handlers.MeStaffHandler(gormDB))
	staff.Post("/auth/change-password", handlers.ChangePasswordHandler(gormDB))
	staff.Get("/auth/github/authorize", handlers.GitHubAuthorizeURLHandler(gormDB))
	staff.Get("/auth/github/status", handlers.GitHubStatusHandler(gormDB))
	staff.Get("/auth/github/repos", handlers.GitHubListReposHandler(gormDB))
	staff.Post("/auth/github/repos/bulk-delete", handlers.GitHubBulkDeleteReposHandler(gormDB))
	staff.Delete("/auth/github", handlers.GitHubDisconnectHandler(gormDB))

	staff.Get("/stats", handlers.GetStatsHandler(gormDB))

	// Classes endpoints
	staff.Get("/classes", handlers.ListClassesHandler(gormDB))
	staff.Get("/classes/schedules", handlers.ListActiveSchedulesHandler(gormDB))
	staff.Get("/classes/:rkId", handlers.GetClassHandler(gormDB))
	staff.Patch("/classes/:rkId/studying", handlers.PatchClassStudyingHandler(gormDB))
	staff.Get("/classes/:rkId/students", handlers.ListClassStudentsHandler(gormDB))
	staff.Get("/classes/:rkId/seating-layout", handlers.GetClassSeatingLayoutHandler(gormDB))
	staff.Put("/classes/:rkId/seating-layout", handlers.PutClassSeatingLayoutHandler(gormDB))
	staff.Delete("/classes/:rkId/seating-layout", handlers.DeleteClassSeatingLayoutHandler(gormDB))

	// My classes (lớp cá nhân theo tài khoản)
	staff.Get("/staff/my-classes", handlers.GetMyClassesHandler(gormDB))
	staff.Post("/staff/my-classes/:classRkId", handlers.AddMyClassHandler(gormDB))
	staff.Delete("/staff/my-classes/:classRkId", handlers.RemoveMyClassHandler(gormDB))

	// Students endpoints
	staff.Get("/students", handlers.ListAllStudentsHandler(gormDB))

	// Sync endpoints
	staff.Post("/sync/classes/start", handlers.StartClassesSyncHandler(gormDB, qldtClient, classesJob))
	staff.Get("/sync/classes/status", handlers.GetClassesSyncStatusHandler(classesJob))
	staff.Post("/sync/students/start", handlers.StartStudentsSyncHandler(gormDB, qldtClient, studentsJob))
	staff.Get("/sync/students/status", handlers.GetStudentsSyncStatusHandler(studentsJob))

	// Learning Management endpoints
	staff.Get("/classes/:rkId/schedule", handlers.GetClassScheduleHandler(gormDB))
	staff.Post("/classes/:rkId/schedule", handlers.SaveClassScheduleHandler(gormDB))
	staff.Delete("/classes/:rkId/schedule", handlers.DeleteClassScheduleHandler(gormDB))
	staff.Post("/classes/:rkId/allowed-apps", handlers.SaveAllowedAppsHandler(gormDB))
	staff.Get("/app-pool", handlers.ListAppPoolHandler(gormDB))
	staff.Get("/app-templates", handlers.ListAppTemplatesHandler(gormDB))
	staff.Post("/app-templates", handlers.CreateAppTemplateHandler(gormDB))
	staff.Patch("/app-templates/:id", handlers.UpdateAppTemplateHandler(gormDB))
	staff.Delete("/app-templates/:id", handlers.DeleteAppTemplateHandler(gormDB))

	staff.Get("/wifi-pool", handlers.ListWifiPoolHandler(gormDB))
	staff.Get("/network/accepted-wifis", handlers.ListAcceptedWifisHandler(gormDB))
	staff.Post("/network/accepted-wifis", handlers.SaveAcceptedWifisHandler(gormDB))
	staff.Post("/network/accepted-wifis/add", handlers.AddAcceptedWifiHandler(gormDB))
	staff.Delete("/network/accepted-wifis/:id", handlers.DeleteAcceptedWifiHandler(gormDB))
	staff.Get("/classes/:rkId/session-logs", handlers.ListClassSessionLogsHandler(gormDB))
	staff.Get("/classes/:rkId/violations", handlers.ListClassViolationsHandler(gormDB))
	staff.Get("/classes/:rkId/online-students", handlers.GetOnlineStudentsHandler(gormDB))
	staff.Get("/classes/:rkId/schedule-conflicts", handlers.GetClassScheduleConflictsHandler(gormDB))

	staff.Get("/classes/:rkId/courses", handlers.GetClassCoursesHandler(gormDB, qldtClient))
	staff.Post("/classes/:rkId/schedule/apply-template", handlers.ApplyScheduleTemplateHandler(gormDB))
	staff.Get("/classes/:rkId/attendance", handlers.GetClassAttendanceHandler(gormDB))
	staff.Get("/classes/:rkId/attendance/shifts", handlers.ListAttendanceShiftsHandler(gormDB))
	staff.Put("/classes/:rkId/attendance/status", handlers.UpdateAttendanceStatusHandler(gormDB))
	staff.Put("/classes/:rkId/attendance/bulk-status", handlers.UpdateAttendanceBulkStatusHandler(gormDB))
	staff.Post("/classes/:rkId/attendance/push-qldt", handlers.PushAttendanceToQLDTHandler(gormDB, qldtClient))
	staff.Get("/classes/:rkId/leave-requests", handlers.GetLeaveRequestsHandler(gormDB, qldtClient))
	staff.Post("/classes/:rkId/leave-requests/:leaveId/status", handlers.UpdateLeaveStatusHandler(gormDB, qldtClient))

	// Chat & quản lý tài khoản
	staff.Get("/chat/students", handlers.SearchChatStudentsHandler(gormDB))
	staff.Get("/chat/conversations", handlers.ListChatConversationsHandler(gormDB))
	staff.Get("/chat/messages/:studentRkId", handlers.ListChatMessagesHandler(gormDB))
	staff.Post("/chat/messages", handlers.SendChatMessageHandler(gormDB))
	staff.Get("/admin/email-domains", handlers.ListEmailDomainsHandler(gormDB))
	staff.Post("/admin/email-domains", handlers.AddEmailDomainHandler(gormDB))
	staff.Delete("/admin/email-domains/:id", handlers.DeleteEmailDomainHandler(gormDB))
	staff.Get("/admin/staff", handlers.ListStaffHandler(gormDB))
	staff.Get("/qldt-token", handlers.GetQldtTokenHandler(gormDB))
	staff.Post("/qldt-token", handlers.SaveQldtTokenHandler(gormDB))

	// Phòng thi
	staff.Get("/exam-rooms", handlers.ListExamRoomsHandler(gormDB))
	staff.Post("/exam-rooms", handlers.CreateExamRoomHandler(gormDB))
	staff.Get("/exam-rooms/search-students", handlers.SearchExamStudentsHandler(gormDB))
	staff.Get("/exam-rooms/:id", handlers.GetExamRoomHandler(gormDB))
	staff.Get("/exam-rooms/:id/online-students", handlers.GetExamRoomOnlineStudentsHandler(gormDB))
	staff.Get("/exam-rooms/:id/violations", handlers.ListExamRoomViolationsHandler(gormDB))
	staff.Patch("/exam-rooms/:id", handlers.UpdateExamRoomHandler(gormDB))
	staff.Delete("/exam-rooms/:id", handlers.DeleteExamRoomHandler(gormDB))
	staff.Post("/exam-rooms/:id/students", handlers.AddExamRoomStudentsHandler(gormDB))
	staff.Delete("/exam-rooms/:id/students/:studentRkId", handlers.RemoveExamRoomStudentHandler(gormDB))
	staff.Post("/exam-rooms/:id/papers", handlers.UploadExamPaperHandler(gormDB))
	staff.Post("/exam-rooms/:id/papers/:paperId/resources", handlers.UploadExamPaperResourceHandler(gormDB))
	staff.Delete("/exam-rooms/:id/papers/:paperId", handlers.DeleteExamPaperHandler(gormDB))
	staff.Get("/exam-rooms/:id/papers/:paperId/view", handlers.ViewStaffExamPaperHandler(gormDB))
	staff.Get("/exam-rooms/:id/papers/:paperId/resources/:resId/view", handlers.ViewStaffExamPaperResourceHandler(gormDB))
	staff.Get("/exam-rooms/:id/papers/:paperId/download", handlers.DownloadStaffExamPaperFileHandler(gormDB))
	staff.Post("/exam-rooms/:id/publish", handlers.PublishExamRoomHandler(gormDB))
	staff.Post("/exam-rooms/:id/unpublish", handlers.UnpublishExamRoomHandler(gormDB))
	staff.Post("/exam-rooms/:id/cancel", handlers.CancelExamRoomHandler(gormDB))
	staff.Post("/exam-rooms/:id/assign-random", handlers.RandomAssignExamPapersHandler(gormDB))
	staff.Post("/exam-rooms/:id/assign-papers-batch", handlers.AssignExamPapersBatchHandler(gormDB))
	staff.Get("/exam-rooms/:id/seating-layout", handlers.GetExamSeatingLayoutHandler(gormDB))
	staff.Put("/exam-rooms/:id/seating-layout", handlers.PutExamSeatingLayoutHandler(gormDB))
	staff.Delete("/exam-rooms/:id/seating-layout", handlers.DeleteExamSeatingLayoutHandler(gormDB))
	staff.Post("/exam-rooms/:id/send-papers", handlers.SendExamPapersHandler(gormDB))
	staff.Get("/exam-rooms/:id/submissions", handlers.ListExamSubmissionsHandler(gormDB))
	staff.Get("/exam-rooms/:id/submissions/download-all", handlers.DownloadAllExamSubmissionsHandler(gormDB))
	staff.Post("/exam-rooms/:id/submissions/publish-git", handlers.PublishExamSubmissionsGitHandler(gormDB))
	staff.Post("/exam-rooms/:id/submissions/publish-git-students", handlers.PublishExamSubmissionsGitPerStudentHandler(gormDB))
	staff.Patch("/exam-rooms/:id/git-settings", handlers.UpdateExamRoomGitSettingsHandler(gormDB))
	staff.Get("/exam-rooms/:id/submissions/:subId/download", handlers.DownloadExamSubmissionHandler(gormDB))

	staff.Get("/guide-links", handlers.ListGuideLinksHandler(gormDB))
	staff.Post("/guide-links", handlers.CreateGuideLinkHandler(gormDB))
	staff.Put("/guide-links/:id", handlers.UpdateGuideLinkHandler(gormDB))
	staff.Delete("/guide-links/:id", handlers.DeleteGuideLinkHandler(gormDB))

	staff.Get("/app-downloads", handlers.ListAppDownloadsHandler(gormDB))
	staff.Post("/app-downloads", handlers.CreateAppDownloadHandler(gormDB))
	staff.Put("/app-downloads/:id", handlers.UpdateAppDownloadHandler(gormDB))
	staff.Delete("/app-downloads/:id", handlers.DeleteAppDownloadHandler(gormDB))

	staff.Get("/app-guides", handlers.ListAppGuidesHandler(gormDB))
	staff.Post("/app-guides", handlers.CreateAppGuideHandler(gormDB))
	staff.Put("/app-guides/:id", handlers.UpdateAppGuideHandler(gormDB))
	staff.Delete("/app-guides/:id", handlers.DeleteAppGuideHandler(gormDB))

	go func() {
		ticker := time.NewTicker(30 * time.Second)
		defer ticker.Stop()
		for range ticker.C {
			handlers.ProcessScheduledExamSends(gormDB)
		}
	}()

	app.Static("/", "../landing_page")

	port := getEnv("PORT", "8080")
	log.Printf("Server starting on port %s...", port)
	if err := app.Listen(":" + port); err != nil {
		log.Fatalf("Failed to start web server: %v", err)
	}
}

func getEnv(key, defaultVal string) string {
	if val := os.Getenv(key); val != "" {
		return val
	}
	return defaultVal
}
