package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/breakpilot/consent-service/internal/config"
	"github.com/breakpilot/consent-service/internal/database"
	"github.com/breakpilot/consent-service/internal/handlers"
	"github.com/breakpilot/consent-service/internal/middleware"
	"github.com/breakpilot/consent-service/internal/services"
	"github.com/breakpilot/consent-service/internal/services/jitsi"
	"github.com/breakpilot/consent-service/internal/services/matrix"
	"github.com/gin-gonic/gin"
)

func main() {
	// Load configuration
	cfg, err := config.Load()
	if err != nil {
		log.Fatalf("Failed to load config: %v", err)
	}

	// Initialize database
	db, err := database.Connect(cfg.DatabaseURL)
	if err != nil {
		log.Fatalf("Failed to connect to database: %v", err)
	}
	defer db.Close()

	// Run migrations
	if err := database.Migrate(db); err != nil {
		log.Fatalf("Failed to run migrations: %v", err)
	}

	// Setup Gin router
	if cfg.Environment == "production" {
		gin.SetMode(gin.ReleaseMode)
	}

	router := gin.Default()

	// Global middleware
	router.Use(middleware.CORS())
	router.Use(middleware.RequestLogger())
	router.Use(middleware.RateLimiter())

	// Health check
	router.GET("/health", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"status":  "healthy",
			"service": "consent-service",
			"version": "1.0.0",
		})
	})

	// Initialize services
	authService := services.NewAuthService(db.Pool, cfg.JWTSecret, cfg.JWTRefreshSecret)
	oauthService := services.NewOAuthService(db.Pool, cfg.JWTSecret)
	totpService := services.NewTOTPService(db.Pool, "BreakPilot")
	emailService := services.NewEmailService(services.EmailConfig{
		Host:     cfg.SMTPHost,
		Port:     cfg.SMTPPort,
		Username: cfg.SMTPUsername,
		Password: cfg.SMTPPassword,
		FromName: cfg.SMTPFromName,
		FromAddr: cfg.SMTPFromAddr,
		BaseURL:  cfg.FrontendURL,
	})
	notificationService := services.NewNotificationService(db.Pool, emailService)
	deadlineService := services.NewDeadlineService(db.Pool, notificationService)
	emailTemplateService := services.NewEmailTemplateService(db.Pool)
	dsrService := services.NewDSRService(db.Pool, notificationService, emailService)

	// Initialize handlers
	h := handlers.New(db)
	authHandler := handlers.NewAuthHandler(authService, emailService)
	oauthHandler := handlers.NewOAuthHandler(oauthService, totpService, authService)
	notificationHandler := handlers.NewNotificationHandler(notificationService)
	deadlineHandler := handlers.NewDeadlineHandler(deadlineService)
	emailTemplateHandler := handlers.NewEmailTemplateHandler(emailTemplateService)
	dsrHandler := handlers.NewDSRHandler(dsrService)

	// Initialize Matrix service (if enabled)
	var matrixService *matrix.MatrixService
	if cfg.MatrixEnabled && cfg.MatrixAccessToken != "" {
		matrixService = matrix.NewMatrixService(matrix.Config{
			HomeserverURL: cfg.MatrixHomeserverURL,
			AccessToken:   cfg.MatrixAccessToken,
			ServerName:    cfg.MatrixServerName,
		})
		log.Println("Matrix service initialized")
	} else {
		log.Println("Matrix service disabled or not configured")
	}

	// Initialize Jitsi service (if enabled)
	var jitsiService *jitsi.JitsiService
	if cfg.JitsiEnabled {
		jitsiService = jitsi.NewJitsiService(jitsi.Config{
			BaseURL:   cfg.JitsiBaseURL,
			AppID:     cfg.JitsiAppID,
			AppSecret: cfg.JitsiAppSecret,
		})
		log.Println("Jitsi service initialized")
	} else {
		log.Println("Jitsi service disabled")
	}

	// Initialize communication handlers
	communicationHandler := handlers.NewCommunicationHandlers(matrixService, jitsiService)

	// Initialize default email templates (runs only once)
	if err := emailTemplateService.InitDefaultTemplates(context.Background()); err != nil {
		log.Printf("Warning: Failed to initialize default email templates: %v", err)
	}

	// API v1 routes
	v1 := router.Group("/api/v1")
	{
		// =============================================
		// OAuth 2.0 Endpoints (RFC 6749)
		// =============================================
		oauth := v1.Group("/oauth")
		{
			// Authorization endpoint (requires user auth for consent)
			oauth.GET("/authorize", middleware.AuthMiddleware(cfg.JWTSecret), oauthHandler.Authorize)
			// Token endpoint (public)
			oauth.POST("/token", oauthHandler.Token)
			// Revocation endpoint (RFC 7009)
			oauth.POST("/revoke", oauthHandler.Revoke)
			// Introspection endpoint (RFC 7662)
			oauth.POST("/introspect", oauthHandler.Introspect)
		}

		// =============================================
		// Authentication Routes (with 2FA support)
		// =============================================
		auth := v1.Group("/auth")
		{
			// Registration with mandatory 2FA setup
			auth.POST("/register", oauthHandler.RegisterWith2FA)
			// Login with 2FA support
			auth.POST("/login", oauthHandler.LoginWith2FA)
			// 2FA challenge verification (during login)
			auth.POST("/2fa/verify", oauthHandler.Verify2FAChallenge)
			// Legacy endpoints (kept for compatibility)
			auth.POST("/logout", authHandler.Logout)
			auth.POST("/refresh", authHandler.RefreshToken)
			auth.POST("/verify-email", authHandler.VerifyEmail)
			auth.POST("/resend-verification", authHandler.ResendVerification)
			auth.POST("/forgot-password", authHandler.ForgotPassword)
			auth.POST("/reset-password", authHandler.ResetPassword)
		}

		// =============================================
		// 2FA Management Routes (require auth)
		// =============================================
		twoFA := v1.Group("/auth/2fa")
		twoFA.Use(middleware.AuthMiddleware(cfg.JWTSecret))
		{
			twoFA.GET("/status", oauthHandler.Get2FAStatus)
			twoFA.POST("/setup", oauthHandler.Setup2FA)
			twoFA.POST("/verify-setup", oauthHandler.Verify2FASetup)
			twoFA.POST("/disable", oauthHandler.Disable2FA)
			twoFA.POST("/recovery-codes", oauthHandler.RegenerateRecoveryCodes)
		}

		// =============================================
		// Profile Routes (require auth)
		// =============================================
		profile := v1.Group("/profile")
		profile.Use(middleware.AuthMiddleware(cfg.JWTSecret))
		{
			profile.GET("", authHandler.GetProfile)
			profile.PUT("", authHandler.UpdateProfile)
			profile.PUT("/password", authHandler.ChangePassword)
			profile.GET("/sessions", authHandler.GetActiveSessions)
			profile.DELETE("/sessions/:id", authHandler.RevokeSession)
		}

		// =============================================
		// Public consent routes (require user auth)
		// =============================================
		public := v1.Group("")
		public.Use(middleware.AuthMiddleware(cfg.JWTSecret))
		{
			// Documents
			public.GET("/documents", h.GetDocuments)
			public.GET("/documents/:type", h.GetDocumentByType)
			public.GET("/documents/:type/latest", h.GetLatestDocumentVersion)

			// User Consent
			public.POST("/consent", h.CreateConsent)
			public.GET("/consent/my", h.GetMyConsents)
			public.GET("/consent/check/:documentType", h.CheckConsent)
			public.DELETE("/consent/:id", h.WithdrawConsent)

			// Cookie Consent
			public.GET("/cookies/categories", h.GetCookieCategories)
			public.POST("/cookies/consent", h.SetCookieConsent)
			public.GET("/cookies/consent/my", h.GetMyCookieConsent)

			// GDPR / Data Subject Rights
			public.GET("/privacy/my-data", h.GetMyData)
			public.POST("/privacy/export", h.RequestDataExport)
			public.POST("/privacy/delete", h.RequestDataDeletion)

			// Data Subject Requests (User-facing)
			public.POST("/dsr", dsrHandler.CreateDSR)
			public.GET("/dsr", dsrHandler.GetMyDSRs)
			public.GET("/dsr/:id", dsrHandler.GetMyDSR)
			public.POST("/dsr/:id/cancel", dsrHandler.CancelMyDSR)

			// Notifications
			public.GET("/notifications", notificationHandler.GetNotifications)
			public.GET("/notifications/unread-count", notificationHandler.GetUnreadCount)
			public.PUT("/notifications/:id/read", notificationHandler.MarkAsRead)
			public.PUT("/notifications/read-all", notificationHandler.MarkAllAsRead)
			public.DELETE("/notifications/:id", notificationHandler.DeleteNotification)
			public.GET("/notifications/preferences", notificationHandler.GetPreferences)
			public.PUT("/notifications/preferences", notificationHandler.UpdatePreferences)

			// Consent Deadlines & Suspension Status
			public.GET("/consent/deadlines", deadlineHandler.GetPendingDeadlines)
			public.GET("/account/suspension-status", deadlineHandler.GetSuspensionStatus)
		}

		// Admin routes (require admin auth)
		admin := v1.Group("/admin")
		admin.Use(middleware.AuthMiddleware(cfg.JWTSecret))
		admin.Use(middleware.AdminOnly())
		{
			// Document Management
			admin.GET("/documents", h.AdminGetDocuments)
			admin.POST("/documents", h.AdminCreateDocument)
			admin.PUT("/documents/:id", h.AdminUpdateDocument)
			admin.DELETE("/documents/:id", h.AdminDeleteDocument)
			admin.GET("/documents/:docId/versions", h.AdminGetVersions)

			// Version Management
			admin.POST("/versions", h.AdminCreateVersion)
			admin.PUT("/versions/:id", h.AdminUpdateVersion)
			admin.DELETE("/versions/:id", h.AdminDeleteVersion)
			admin.POST("/versions/:id/archive", h.AdminArchiveVersion)
			admin.POST("/versions/:id/submit-review", h.AdminSubmitForReview)
			admin.POST("/versions/:id/approve", h.AdminApproveVersion)
			admin.POST("/versions/:id/reject", h.AdminRejectVersion)
			admin.GET("/versions/:id/compare", h.AdminCompareVersions)
			admin.GET("/versions/:id/approval-history", h.AdminGetApprovalHistory)

			// Publishing (DSB role recommended but Admin can also do it in dev)
			admin.POST("/versions/:id/publish", h.AdminPublishVersion)

			// Cookie Categories
			admin.GET("/cookies/categories", h.AdminGetCookieCategories)
			admin.POST("/cookies/categories", h.AdminCreateCookieCategory)
			admin.PUT("/cookies/categories/:id", h.AdminUpdateCookieCategory)
			admin.DELETE("/cookies/categories/:id", h.AdminDeleteCookieCategory)

			// Statistics & Audit
			admin.GET("/stats/consents", h.GetConsentStats)
			admin.GET("/stats/cookies", h.GetCookieStats)
			admin.GET("/audit-log", h.GetAuditLog)

			// Deadline Management (for testing/manual trigger)
			admin.POST("/deadlines/process", deadlineHandler.TriggerDeadlineProcessing)

			// Scheduled Publishing
			admin.GET("/scheduled-versions", h.GetScheduledVersions)
			admin.POST("/scheduled-publishing/process", h.ProcessScheduledPublishing)

			// OAuth Client Management
			admin.GET("/oauth/clients", oauthHandler.AdminGetClients)
			admin.POST("/oauth/clients", oauthHandler.AdminCreateClient)

			// =============================================
			// E-Mail Template Management
			// =============================================
			admin.GET("/email-templates/types", emailTemplateHandler.GetAllTemplateTypes)
			admin.GET("/email-templates", emailTemplateHandler.GetAllTemplates)
			admin.GET("/email-templates/settings", emailTemplateHandler.GetSettings)
			admin.PUT("/email-templates/settings", emailTemplateHandler.UpdateSettings)
			admin.GET("/email-templates/stats", emailTemplateHandler.GetEmailStats)
			admin.GET("/email-templates/logs", emailTemplateHandler.GetSendLogs)
			admin.GET("/email-templates/default/:type", emailTemplateHandler.GetDefaultContent)
			admin.POST("/email-templates/initialize", emailTemplateHandler.InitializeTemplates)
			admin.GET("/email-templates/:id", emailTemplateHandler.GetTemplate)
			admin.POST("/email-templates", emailTemplateHandler.CreateTemplate)
			admin.GET("/email-templates/:id/versions", emailTemplateHandler.GetTemplateVersions)

			// E-Mail Template Versions
			admin.GET("/email-template-versions/:id", emailTemplateHandler.GetVersion)
			admin.POST("/email-template-versions", emailTemplateHandler.CreateVersion)
			admin.PUT("/email-template-versions/:id", emailTemplateHandler.UpdateVersion)
			admin.POST("/email-template-versions/:id/submit", emailTemplateHandler.SubmitForReview)
			admin.POST("/email-template-versions/:id/approve", emailTemplateHandler.ApproveVersion)
			admin.POST("/email-template-versions/:id/reject", emailTemplateHandler.RejectVersion)
			admin.POST("/email-template-versions/:id/publish", emailTemplateHandler.PublishVersion)
			admin.GET("/email-template-versions/:id/approvals", emailTemplateHandler.GetApprovals)
			admin.POST("/email-template-versions/:id/preview", emailTemplateHandler.PreviewVersion)
			admin.POST("/email-template-versions/:id/send-test", emailTemplateHandler.SendTestEmail)

			// =============================================
			// Data Subject Requests (DSR) Management
			// =============================================
			admin.GET("/dsr", dsrHandler.AdminListDSR)
			admin.GET("/dsr/stats", dsrHandler.AdminGetDSRStats)
			admin.POST("/dsr", dsrHandler.AdminCreateDSR)
			admin.GET("/dsr/:id", dsrHandler.AdminGetDSR)
			admin.PUT("/dsr/:id", dsrHandler.AdminUpdateDSR)
			admin.POST("/dsr/:id/status", dsrHandler.AdminUpdateDSRStatus)
			admin.POST("/dsr/:id/verify-identity", dsrHandler.AdminVerifyIdentity)
			admin.POST("/dsr/:id/assign", dsrHandler.AdminAssignDSR)
			admin.POST("/dsr/:id/extend", dsrHandler.AdminExtendDSRDeadline)
			admin.POST("/dsr/:id/complete", dsrHandler.AdminCompleteDSR)
			admin.POST("/dsr/:id/reject", dsrHandler.AdminRejectDSR)
			admin.GET("/dsr/:id/history", dsrHandler.AdminGetDSRHistory)
			admin.GET("/dsr/:id/communications", dsrHandler.AdminGetDSRCommunications)
			admin.POST("/dsr/:id/communicate", dsrHandler.AdminSendDSRCommunication)
			admin.GET("/dsr/:id/exception-checks", dsrHandler.AdminGetExceptionChecks)
			admin.POST("/dsr/:id/exception-checks/init", dsrHandler.AdminInitExceptionChecks)
			admin.PUT("/dsr/:id/exception-checks/:checkId", dsrHandler.AdminUpdateExceptionCheck)
			admin.POST("/dsr/deadlines/process", dsrHandler.ProcessDeadlines)

			// DSR Templates
			admin.GET("/dsr-templates", dsrHandler.AdminGetDSRTemplates)
			admin.GET("/dsr-templates/published", dsrHandler.AdminGetPublishedDSRTemplates)
			admin.GET("/dsr-templates/:id/versions", dsrHandler.AdminGetDSRTemplateVersions)
			admin.POST("/dsr-templates/:id/versions", dsrHandler.AdminCreateDSRTemplateVersion)
			admin.POST("/dsr-template-versions/:versionId/publish", dsrHandler.AdminPublishDSRTemplateVersion)
		}

		// =============================================
		// Communication Routes (Matrix + Jitsi)
		// =============================================
		communicationHandler.RegisterRoutes(v1, cfg.JWTSecret, middleware.AuthMiddleware(cfg.JWTSecret))

		// =============================================
		// Cookie Banner SDK Routes (Public - Anonymous)
		// =============================================
		// Diese Endpoints werden vom @breakpilot/consent-sdk verwendet
		// für anonyme (device-basierte) Cookie-Einwilligungen.
		banner := v1.Group("/banner")
		{
			// Public Endpoints (keine Auth erforderlich)
			banner.POST("/consent", h.CreateBannerConsent)
			banner.GET("/consent", h.GetBannerConsent)
			banner.DELETE("/consent/:consentId", h.RevokeBannerConsent)
			banner.GET("/config/:siteId", h.GetSiteConfig)
			banner.GET("/consent/export", h.ExportBannerConsent)
		}

		// Banner Admin Routes (require admin auth)
		bannerAdmin := v1.Group("/banner/admin")
		bannerAdmin.Use(middleware.AuthMiddleware(cfg.JWTSecret))
		bannerAdmin.Use(middleware.AdminOnly())
		{
			bannerAdmin.GET("/stats/:siteId", h.GetBannerStats)
		}
	}

	// Start background scheduler for scheduled publishing
	go startScheduledPublishingWorker(db)

	// Start DSR deadline monitoring worker
	go startDSRDeadlineWorker(dsrService)

	// Start server
	port := cfg.Port
	if port == "" {
		port = "8080"
	}

	log.Printf("Starting Consent Service on port %s", port)
	if err := router.Run(":" + port); err != nil {
		log.Fatalf("Failed to start server: %v", err)
	}
}

// startScheduledPublishingWorker runs every minute to check for scheduled versions
func startScheduledPublishingWorker(db *database.DB) {
	ticker := time.NewTicker(1 * time.Minute)
	defer ticker.Stop()

	log.Println("Scheduled publishing worker started (checking every minute)")

	for range ticker.C {
		processScheduledVersions(db)
	}
}

func processScheduledVersions(db *database.DB) {
	ctx := context.Background()

	// Find all scheduled versions that are due
	rows, err := db.Pool.Query(ctx, `
		SELECT id, document_id, version
		FROM document_versions
		WHERE status = 'scheduled'
		  AND scheduled_publish_at IS NOT NULL
		  AND scheduled_publish_at <= NOW()
	`)
	if err != nil {
		log.Printf("Scheduler: Error fetching scheduled versions: %v", err)
		return
	}
	defer rows.Close()

	var publishedCount int
	for rows.Next() {
		var versionID, docID string
		var version string
		if err := rows.Scan(&versionID, &docID, &version); err != nil {
			continue
		}

		// Publish this version
		_, err := db.Pool.Exec(ctx, `
			UPDATE document_versions
			SET status = 'published', published_at = NOW(), updated_at = NOW()
			WHERE id = $1
		`, versionID)

		if err == nil {
			// Archive previous published versions for this document
			db.Pool.Exec(ctx, `
				UPDATE document_versions
				SET status = 'archived', updated_at = NOW()
				WHERE document_id = $1 AND id != $2 AND status = 'published'
			`, docID, versionID)

			// Log the publishing
			details := fmt.Sprintf("Version %s automatically published by scheduler", version)
			db.Pool.Exec(ctx, `
				INSERT INTO consent_audit_log (action, entity_type, entity_id, details, user_agent)
				VALUES ('version_scheduled_published', 'document_version', $1, $2, 'scheduler')
			`, versionID, details)

			publishedCount++
			log.Printf("Scheduler: Published version %s (ID: %s)", version, versionID)
		}
	}

	if publishedCount > 0 {
		log.Printf("Scheduler: Published %d version(s)", publishedCount)
	}
}

// startDSRDeadlineWorker monitors DSR deadlines and sends notifications
func startDSRDeadlineWorker(dsrService *services.DSRService) {
	ticker := time.NewTicker(1 * time.Hour)
	defer ticker.Stop()

	log.Println("DSR deadline monitoring worker started (checking every hour)")

	// Run immediately on startup
	ctx := context.Background()
	if err := dsrService.ProcessDeadlines(ctx); err != nil {
		log.Printf("DSR Worker: Error processing deadlines: %v", err)
	}

	for range ticker.C {
		ctx := context.Background()
		if err := dsrService.ProcessDeadlines(ctx); err != nil {
			log.Printf("DSR Worker: Error processing deadlines: %v", err)
		}
	}
}
