package router

import (
	"encoding/json"
	"log/slog"
	"net/http"
	"time"

	"github.com/jmoiron/sqlx"

	"mgit.msbls.de/m/KanzlAI-mGMT/internal/auth"
	"mgit.msbls.de/m/KanzlAI-mGMT/internal/config"
	"mgit.msbls.de/m/KanzlAI-mGMT/internal/handlers"
	"mgit.msbls.de/m/KanzlAI-mGMT/internal/middleware"
	"mgit.msbls.de/m/KanzlAI-mGMT/internal/services"
)

func New(db *sqlx.DB, authMW *auth.Middleware, cfg *config.Config, calDAVSvc *services.CalDAVService, notifSvc *services.NotificationService, youpcDB ...*sqlx.DB) http.Handler {
	mux := http.NewServeMux()

	// Services
	auditSvc := services.NewAuditService(db)
	tenantSvc := services.NewTenantService(db, auditSvc)
	caseSvc := services.NewCaseService(db, auditSvc)
	partySvc := services.NewPartyService(db, auditSvc)
	appointmentSvc := services.NewAppointmentService(db, auditSvc)
	holidaySvc := services.NewHolidayService(db)
	deadlineSvc := services.NewDeadlineService(db, auditSvc)
	deadlineRuleSvc := services.NewDeadlineRuleService(db)
	calculator := services.NewDeadlineCalculator(holidaySvc)
	determineSvc := services.NewDetermineService(db, calculator)
	storageCli := services.NewStorageClient(cfg.SupabaseURL, cfg.SupabaseServiceKey)
	documentSvc := services.NewDocumentService(db, storageCli, auditSvc)
	assignmentSvc := services.NewCaseAssignmentService(db)
	reportSvc := services.NewReportingService(db)
	timeEntrySvc := services.NewTimeEntryService(db, auditSvc)
	invoiceSvc := services.NewInvoiceService(db, auditSvc)
	billingRateSvc := services.NewBillingRateService(db, auditSvc)
	templateSvc := services.NewTemplateService(db, auditSvc)

	// AI service (optional — only if API key is configured)
	var aiH *handlers.AIHandler
	if cfg.AnthropicAPIKey != "" {
		var ydb *sqlx.DB
		if len(youpcDB) > 0 {
			ydb = youpcDB[0]
		}
		aiSvc := services.NewAIService(cfg.AnthropicAPIKey, db, ydb)
		aiH = handlers.NewAIHandler(aiSvc)
	}

	// Middleware
	tenantResolver := auth.NewTenantResolver(tenantSvc)

	noteSvc := services.NewNoteService(db, auditSvc)
	dashboardSvc := services.NewDashboardService(db)

	// Notification handler (optional — nil in tests)
	var notifH *handlers.NotificationHandler
	if notifSvc != nil {
		notifH = handlers.NewNotificationHandler(notifSvc, db)
	}

	// Handlers
	auditH := handlers.NewAuditLogHandler(auditSvc)
	tenantH := handlers.NewTenantHandler(tenantSvc)
	caseH := handlers.NewCaseHandler(caseSvc)
	partyH := handlers.NewPartyHandler(partySvc)
	apptH := handlers.NewAppointmentHandler(appointmentSvc)
	deadlineH := handlers.NewDeadlineHandlers(deadlineSvc)
	ruleH := handlers.NewDeadlineRuleHandlers(deadlineRuleSvc)
	calcH := handlers.NewCalculateHandlers(calculator, deadlineRuleSvc)
	determineH := handlers.NewDetermineHandlers(determineSvc, deadlineSvc)
	dashboardH := handlers.NewDashboardHandler(dashboardSvc)
	noteH := handlers.NewNoteHandler(noteSvc)
	eventH := handlers.NewCaseEventHandler(db)
	docH := handlers.NewDocumentHandler(documentSvc)
	assignmentH := handlers.NewCaseAssignmentHandler(assignmentSvc)
	reportH := handlers.NewReportHandler(reportSvc)
	timeH := handlers.NewTimeEntryHandler(timeEntrySvc)
	invoiceH := handlers.NewInvoiceHandler(invoiceSvc)
	billingH := handlers.NewBillingRateHandler(billingRateSvc)
	templateH := handlers.NewTemplateHandler(templateSvc, caseSvc, partySvc, deadlineSvc, tenantSvc)

	// Public routes
	mux.HandleFunc("GET /health", handleHealth(db))

	// Authenticated API routes
	api := http.NewServeMux()

	// Tenant management (no tenant resolver — these operate across tenants)
	api.HandleFunc("POST /api/tenants/auto-assign", tenantH.AutoAssign)
	api.HandleFunc("POST /api/tenants", tenantH.CreateTenant)
	api.HandleFunc("GET /api/tenants", tenantH.ListTenants)
	api.HandleFunc("GET /api/tenants/{id}", tenantH.GetTenant)
	api.HandleFunc("PUT /api/tenants/{id}/settings", tenantH.UpdateSettings)
	api.HandleFunc("POST /api/tenants/{id}/invite", tenantH.InviteUser)
	api.HandleFunc("DELETE /api/tenants/{id}/members/{uid}", tenantH.RemoveMember)
	api.HandleFunc("GET /api/tenants/{id}/members", tenantH.ListMembers)
	api.HandleFunc("PUT /api/tenants/{id}/members/{uid}/role", tenantH.UpdateMemberRole)

	// Permission-wrapping helper: wraps a HandlerFunc with a permission check
	perm := func(p auth.Permission, fn http.HandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			role := auth.UserRoleFromContext(r.Context())
			if !auth.HasPermission(role, p) {
				w.Header().Set("Content-Type", "application/json")
				w.WriteHeader(http.StatusForbidden)
				w.Write([]byte(`{"error":"insufficient permissions"}`))
				return
			}
			fn(w, r)
		}
	}

	// Tenant-scoped routes (require tenant context)
	scoped := http.NewServeMux()

	// Current user info (role, permissions) — all authenticated users
	scoped.HandleFunc("GET /api/me", tenantH.GetMe)

	// Cases — all can view, create needs PermCreateCase, archive needs PermCreateCase
	scoped.HandleFunc("GET /api/cases", caseH.List)
	scoped.HandleFunc("POST /api/cases", perm(auth.PermCreateCase, caseH.Create))
	scoped.HandleFunc("GET /api/cases/{id}", caseH.Get)
	scoped.HandleFunc("PUT /api/cases/{id}", caseH.Update)
	scoped.HandleFunc("DELETE /api/cases/{id}", perm(auth.PermCreateCase, caseH.Delete))

	// Parties — same access as case editing
	scoped.HandleFunc("GET /api/cases/{id}/parties", partyH.List)
	scoped.HandleFunc("POST /api/cases/{id}/parties", partyH.Create)
	scoped.HandleFunc("PUT /api/parties/{partyId}", partyH.Update)
	scoped.HandleFunc("DELETE /api/parties/{partyId}", partyH.Delete)

	// Deadlines — manage needs PermManageDeadlines, view is open
	scoped.HandleFunc("GET /api/deadlines/{deadlineID}", deadlineH.Get)
	scoped.HandleFunc("GET /api/deadlines", deadlineH.ListAll)
	scoped.HandleFunc("GET /api/cases/{caseID}/deadlines", deadlineH.ListForCase)
	scoped.HandleFunc("POST /api/cases/{caseID}/deadlines", perm(auth.PermManageDeadlines, deadlineH.Create))
	scoped.HandleFunc("PUT /api/deadlines/{deadlineID}", perm(auth.PermManageDeadlines, deadlineH.Update))
	scoped.HandleFunc("PATCH /api/deadlines/{deadlineID}/complete", perm(auth.PermManageDeadlines, deadlineH.Complete))
	scoped.HandleFunc("DELETE /api/deadlines/{deadlineID}", perm(auth.PermManageDeadlines, deadlineH.Delete))

	// Deadline rules (reference data) — all can read
	scoped.HandleFunc("GET /api/deadline-rules", ruleH.List)
	scoped.HandleFunc("GET /api/deadline-rules/{type}", ruleH.GetRuleTree)
	scoped.HandleFunc("GET /api/proceeding-types", ruleH.ListProceedingTypes)

	// Deadline calculator — all can use
	scoped.HandleFunc("POST /api/deadlines/calculate", calcH.Calculate)

	// Deadline determination — full timeline calculation with conditions
	scoped.HandleFunc("GET /api/proceeding-types/{code}/timeline", determineH.GetTimeline)
	scoped.HandleFunc("POST /api/deadlines/determine", determineH.Determine)
	scoped.HandleFunc("POST /api/cases/{caseID}/deadlines/batch", perm(auth.PermManageDeadlines, determineH.BatchCreate))

	// Appointments — all can manage (PermManageAppointments granted to all)
	scoped.HandleFunc("GET /api/appointments/{id}", apptH.Get)
	scoped.HandleFunc("GET /api/appointments", apptH.List)
	scoped.HandleFunc("POST /api/appointments", perm(auth.PermManageAppointments, apptH.Create))
	scoped.HandleFunc("PUT /api/appointments/{id}", perm(auth.PermManageAppointments, apptH.Update))
	scoped.HandleFunc("DELETE /api/appointments/{id}", perm(auth.PermManageAppointments, apptH.Delete))

	// Case assignments — manage team required for assign/unassign
	scoped.HandleFunc("GET /api/cases/{id}/assignments", assignmentH.List)
	scoped.HandleFunc("POST /api/cases/{id}/assignments", perm(auth.PermManageTeam, assignmentH.Assign))
	scoped.HandleFunc("DELETE /api/cases/{id}/assignments/{uid}", perm(auth.PermManageTeam, assignmentH.Unassign))

	// Case events — all can view
	scoped.HandleFunc("GET /api/case-events/{id}", eventH.Get)

	// Notes — all can manage
	scoped.HandleFunc("GET /api/notes", noteH.List)
	scoped.HandleFunc("POST /api/notes", noteH.Create)
	scoped.HandleFunc("PUT /api/notes/{id}", noteH.Update)
	scoped.HandleFunc("DELETE /api/notes/{id}", noteH.Delete)

	// Dashboard — all can view
	scoped.HandleFunc("GET /api/dashboard", dashboardH.Get)

	// Audit log
	scoped.HandleFunc("GET /api/audit-log", perm(auth.PermViewAuditLog, auditH.List))

	// Documents — all can upload, delete checked in handler (own vs all)
	scoped.HandleFunc("GET /api/cases/{id}/documents", docH.ListByCase)
	scoped.HandleFunc("POST /api/cases/{id}/documents", perm(auth.PermUploadDocuments, docH.Upload))
	scoped.HandleFunc("GET /api/documents/{docId}", docH.Download)
	scoped.HandleFunc("GET /api/documents/{docId}/meta", docH.GetMeta)
	scoped.HandleFunc("DELETE /api/documents/{docId}", docH.Delete)

	// AI endpoints (rate limited: 5 req/min burst 10 per IP)
	if aiH != nil {
		aiLimiter := middleware.NewTokenBucket(5.0/60.0, 10)
		scoped.HandleFunc("POST /api/ai/extract-deadlines", perm(auth.PermAIExtraction, aiLimiter.LimitFunc(aiH.ExtractDeadlines)))
		scoped.HandleFunc("POST /api/ai/summarize-case", perm(auth.PermAIExtraction, aiLimiter.LimitFunc(aiH.SummarizeCase)))
		scoped.HandleFunc("POST /api/ai/draft-document", perm(auth.PermAIExtraction, aiLimiter.LimitFunc(aiH.DraftDocument)))
		scoped.HandleFunc("POST /api/ai/case-strategy", perm(auth.PermAIExtraction, aiLimiter.LimitFunc(aiH.CaseStrategy)))
		scoped.HandleFunc("POST /api/ai/similar-cases", perm(auth.PermAIExtraction, aiLimiter.LimitFunc(aiH.SimilarCases)))
	}

	// Notifications
	if notifH != nil {
		scoped.HandleFunc("GET /api/notifications", notifH.List)
		scoped.HandleFunc("GET /api/notifications/unread-count", notifH.UnreadCount)
		scoped.HandleFunc("PATCH /api/notifications/{id}/read", notifH.MarkRead)
		scoped.HandleFunc("PATCH /api/notifications/read-all", notifH.MarkAllRead)
		scoped.HandleFunc("GET /api/notification-preferences", notifH.GetPreferences)
		scoped.HandleFunc("PUT /api/notification-preferences", notifH.UpdatePreferences)
	}

	// CalDAV sync endpoints — settings permission required
	if calDAVSvc != nil {
		calDAVH := handlers.NewCalDAVHandler(calDAVSvc)
		scoped.HandleFunc("POST /api/caldav/sync", perm(auth.PermManageSettings, calDAVH.TriggerSync))
		scoped.HandleFunc("GET /api/caldav/status", calDAVH.GetStatus)
	}

	// Reports — cases/deadlines/workload open to all, billing restricted
	scoped.HandleFunc("GET /api/reports/cases", reportH.Cases)
	scoped.HandleFunc("GET /api/reports/deadlines", reportH.Deadlines)
	scoped.HandleFunc("GET /api/reports/workload", reportH.Workload)
	scoped.HandleFunc("GET /api/reports/billing", perm(auth.PermManageBilling, reportH.Billing))

	// Time entries — all can view/create, tied to cases
	scoped.HandleFunc("GET /api/cases/{id}/time-entries", timeH.ListForCase)
	scoped.HandleFunc("GET /api/time-entries", timeH.List)
	scoped.HandleFunc("POST /api/cases/{id}/time-entries", timeH.Create)
	scoped.HandleFunc("PUT /api/time-entries/{id}", timeH.Update)
	scoped.HandleFunc("DELETE /api/time-entries/{id}", timeH.Delete)
	scoped.HandleFunc("GET /api/time-entries/summary", timeH.Summary)

	// Invoices — billing permission required
	scoped.HandleFunc("GET /api/invoices", perm(auth.PermManageBilling, invoiceH.List))
	scoped.HandleFunc("GET /api/invoices/{id}", perm(auth.PermManageBilling, invoiceH.Get))
	scoped.HandleFunc("POST /api/invoices", perm(auth.PermManageBilling, invoiceH.Create))
	scoped.HandleFunc("PUT /api/invoices/{id}", perm(auth.PermManageBilling, invoiceH.Update))
	scoped.HandleFunc("PATCH /api/invoices/{id}/status", perm(auth.PermManageBilling, invoiceH.UpdateStatus))

	// Billing rates — billing permission required
	scoped.HandleFunc("GET /api/billing-rates", perm(auth.PermManageBilling, billingH.List))
	scoped.HandleFunc("PUT /api/billing-rates", perm(auth.PermManageBilling, billingH.Upsert))

	// Document templates — all can view/use, manage needs case creation permission
	scoped.HandleFunc("GET /api/templates", templateH.List)
	scoped.HandleFunc("GET /api/templates/{id}", templateH.Get)
	scoped.HandleFunc("POST /api/templates", perm(auth.PermCreateCase, templateH.Create))
	scoped.HandleFunc("PUT /api/templates/{id}", perm(auth.PermCreateCase, templateH.Update))
	scoped.HandleFunc("DELETE /api/templates/{id}", perm(auth.PermCreateCase, templateH.Delete))
	scoped.HandleFunc("POST /api/templates/{id}/render", templateH.Render)

	// Wire: auth -> tenant routes go directly, scoped routes get tenant resolver
	api.Handle("/api/", tenantResolver.Resolve(scoped))

	mux.Handle("/api/", authMW.RequireAuth(api))

	// Apply security middleware stack: CORS -> Security Headers -> Request Logger -> Routes
	var handler http.Handler = mux
	handler = requestLogger(handler)
	handler = middleware.SecurityHeaders(handler)
	handler = middleware.CORS(cfg.FrontendOrigin)(handler)

	return handler
}

func handleHealth(db *sqlx.DB) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if err := db.Ping(); err != nil {
			w.WriteHeader(http.StatusServiceUnavailable)
			json.NewEncoder(w).Encode(map[string]string{"status": "error"})
			return
		}
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
	}
}

type statusWriter struct {
	http.ResponseWriter
	status int
}

func (w *statusWriter) WriteHeader(code int) {
	w.status = code
	w.ResponseWriter.WriteHeader(code)
}

func requestLogger(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Skip health checks to reduce noise
		if r.URL.Path == "/health" {
			next.ServeHTTP(w, r)
			return
		}

		sw := &statusWriter{ResponseWriter: w, status: http.StatusOK}
		start := time.Now()
		next.ServeHTTP(sw, r)

		slog.Info("request",
			"method", r.Method,
			"path", r.URL.Path,
			"status", sw.status,
			"duration_ms", time.Since(start).Milliseconds(),
		)
	})
}
