package web

import (
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"path/filepath"
	"time"

	"inboxer/src/internal/auth"
	"inboxer/src/internal/db"
	"inboxer/src/internal/imap"
	"inboxer/src/internal/worker"
	"inboxer/src/pkg/config"
	"github.com/gorilla/mux"
)

// Handler holds dependencies for HTTP handlers
type Handler struct {
	authService *auth.AuthService
	db          *db.Database
	config      *config.Config
	templates   map[string]*template.Template // page name -> parsed template set
	worker      *worker.Worker
}

// NewHandler creates a new handler with dependencies
func NewHandler(authService *auth.AuthService, database *db.Database, cfg *config.Config, bgWorker *worker.Worker) (*Handler, error) {
	// Parse templates
	templates, err := parseTemplates()
	if err != nil {
		return nil, fmt.Errorf("failed to parse templates: %w", err)
	}

	return &Handler{
		authService: authService,
		db:          database,
		config:      cfg,
		templates:   templates,
		worker:      bgWorker,
	}, nil
}

// parseTemplates creates a separate template set per page.
// Each set contains the page template (defines "content") and the base template.
// This prevents the shared "content" template name from being overwritten
// when multiple page templates are parsed into the same template set.
func parseTemplates() (map[string]*template.Template, error) {
	funcMap := template.FuncMap{
		"currentYear": func() int { return time.Now().Year() },
	}

	templateDir := "src/web/templates"
	pages := []string{"login", "verify", "dashboard", "settings"}
	templates := make(map[string]*template.Template, len(pages))

	for _, page := range pages {
		pagePath := filepath.Join(templateDir, page+".html")
		basePath := filepath.Join(templateDir, "base.html")

		// Parse the page template first (defines "content", calls {{ template "base" . }})
		tmpl, err := template.New(page).Funcs(funcMap).ParseFiles(pagePath)
		if err != nil {
			return nil, fmt.Errorf("failed to parse %s: %w", pagePath, err)
		}

		// Parse the base template into the same set (defines "base" template)
		_, err = tmpl.ParseFiles(basePath)
		if err != nil {
			return nil, fmt.Errorf("failed to parse base template: %w", err)
		}

		templates[page] = tmpl
	}

	return templates, nil
}

// AppVersion is the current application version (matches docs/CHANGELOG.md)
const AppVersion = "2026-04.5"

// TemplateData holds data passed to templates
type TemplateData struct {
	Title       string
	CurrentPage string
	UserEmail   string
	ShowNav     bool
	ShowFooter  bool
	Flash       *FlashMessage
	Error       string
	Success     string
	CurrentYear int
	Version     string
}

// FlashMessage represents a flash message to display to the user
type FlashMessage struct {
	Type    string // success, error, warning, info
	Message string
}

// NewTemplateData creates base template data
func (h *Handler) NewTemplateData(r *http.Request) TemplateData {
	email, _ := h.authService.GetSessionManager().GetUserEmail(r)

	return TemplateData{
		Title:       "inBOXER",
		CurrentPage: "",
		UserEmail:   email,
		ShowNav:     true,
		ShowFooter:  true,
		CurrentYear: time.Now().Year(),
		Version:     AppVersion,
	}
}

// LoginHandler handles login page and OTP request
func (h *Handler) LoginHandler(w http.ResponseWriter, r *http.Request) {
	data := h.NewTemplateData(r)
	data.CurrentPage = "login"

	if r.Method == http.MethodPost {
		email := r.FormValue("email")
		if email == "" {
			data.Error = "Email address is required"
			h.renderTemplate(w, "login", data)
			return
		}

		// Request OTP
		err := h.authService.RequestOTP(email)
		if err != nil {
			data.Error = fmt.Sprintf("Failed to send OTP: %v", err)
			h.renderTemplate(w, "login", data)
			return
		}

		// Redirect to verify page
		http.Redirect(w, r, "/verify?email="+email, http.StatusSeeOther)
		return
	}

	h.renderTemplate(w, "login", data)
}

// VerifyHandler handles OTP verification
func (h *Handler) VerifyHandler(w http.ResponseWriter, r *http.Request) {
	data := h.NewTemplateData(r)
	data.CurrentPage = "verify"

	email := r.URL.Query().Get("email")
	if email == "" {
		email = r.FormValue("email")
	}

	if email == "" {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	data.UserEmail = email // Show email in template

	if r.Method == http.MethodPost {
		otp := r.FormValue("otp")
		if otp == "" || len(otp) != 6 {
			data.Error = "Please enter a valid 6-digit code"
			h.renderTemplate(w, "verify", data)
			return
		}

		// Verify OTP
		valid, err := h.authService.VerifyOTP(email, otp)
		if err != nil {
			data.Error = fmt.Sprintf("Verification failed: %v", err)
			h.renderTemplate(w, "verify", data)
			return
		}

		if !valid {
			data.Error = "Invalid or expired code. Please try again."
			h.renderTemplate(w, "verify", data)
			return
		}

		// Create session
		err = h.authService.GetSessionManager().CreateSession(w, r, email)
		if err != nil {
			data.Error = "Failed to create session. Please try again."
			h.renderTemplate(w, "verify", data)
			return
		}

		// Redirect to dashboard
		http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
		return
	}

	h.renderTemplate(w, "verify", data)
}

// ResendOTPHandler handles OTP resend requests
func (h *Handler) ResendOTPHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	email := r.FormValue("email")
	if email == "" {
		http.Error(w, "Email is required", http.StatusBadRequest)
		return
	}

	err := h.authService.RequestOTP(email)
	if err != nil {
		http.Error(w, fmt.Sprintf("Failed to resend OTP: %v", err), http.StatusInternalServerError)
		return
	}

	// Redirect back to verify page
	http.Redirect(w, r, "/verify?email="+email, http.StatusSeeOther)
}

// DashboardHandler handles the main dashboard
func (h *Handler) DashboardHandler(w http.ResponseWriter, r *http.Request) {
	data := h.NewTemplateData(r)
	data.CurrentPage = "dashboard"

	// Check authentication
	email, loggedIn := h.authService.GetSessionManager().GetUserEmail(r)
	if !loggedIn {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	// Get user from database
	user, err := h.db.GetUserByEmail(email)
	if err != nil {
		h.authService.GetSessionManager().DestroySession(w, r)
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	// Get dashboard stats
	total, err := h.db.GetTotalProcessed(user.ID)
	if err != nil {
		total = 0
	}

	counts, err := h.db.GetFolderCounts(user.ID)
	if err != nil {
		counts = []db.FolderCount{}
	}

	// Convert counts to map
	countsMap := make(map[string]int)
	for _, c := range counts {
		countsMap[c.Folder] = c.Count
	}

	lastProcessed, _ := h.db.GetLastProcessedTime(user.ID)

	// Get mailbox settings for test mode
	settings, err := h.db.GetMailboxSettings(user.ID)
	testMode := false
	if err == nil {
		testMode = settings.TestMode
	}

	// Add dashboard-specific data
	dashboardData := struct {
		TemplateData
		Stats         map[string]int
		TotalProcessed int64
		WorkerRunning bool
		LastProcessed *time.Time
		TestMode      bool
	}{
		TemplateData:  data,
		Stats:         countsMap,
		TotalProcessed: total,
		WorkerRunning: true, // TODO: Get actual worker status
		LastProcessed: lastProcessed,
		TestMode:      testMode,
	}

	h.renderTemplate(w, "dashboard", dashboardData)
}

// SettingsHandler handles email settings page
func (h *Handler) SettingsHandler(w http.ResponseWriter, r *http.Request) {
	data := h.NewTemplateData(r)
	data.CurrentPage = "settings"

	// Check authentication
	email, loggedIn := h.authService.GetSessionManager().GetUserEmail(r)
	if !loggedIn {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	user, err := h.db.GetUserByEmail(email)
	if err != nil {
		h.authService.GetSessionManager().DestroySession(w, r)
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	// Get current settings
	settings, err := h.db.GetMailboxSettings(user.ID)
	if err != nil {
		// Use defaults
		settings = &db.MailboxSettings{
			UserID:       user.ID,
			IMAPHost:     h.config.IMAP.Host,
			IMAPPort:     h.config.IMAP.Port,
			IMAPTLS:      h.config.IMAP.TLS,
			BatchSize:    h.config.IMAP.BatchSize,
			PollInterval: h.config.IMAP.PollIntervalMinutes,
			AutoStart:    true,
		}
	}

	if r.Method == http.MethodPost {
		// Update settings from form
		settings.IMAPHost = r.FormValue("imap_host")
		settings.IMAPPort = parseInt(r.FormValue("imap_port"), 993)
		settings.IMAPUser = r.FormValue("imap_user")
		settings.IMAPUsername = r.FormValue("imap_username")

		// Only update password if the user entered a new one
		// (password field is left blank to keep existing password)
		if pass := r.FormValue("imap_pass"); pass != "" {
			settings.IMAPPassEncrypted = pass
		}

		settings.IMAPTLS = r.FormValue("imap_tls") == "on"
		settings.BatchSize = parseInt(r.FormValue("batch_size"), 10)
		settings.PollInterval = parseInt(r.FormValue("poll_interval"), 5)
		settings.AutoStart = r.FormValue("auto_start") == "on"

		// Use system SMTP for now (could make configurable later)
		settings.SMTPHost = h.config.Server.Host
		settings.SMTPPort = 587 // Default SMTP port
		settings.SMTPUser = ""
		settings.SMTPPassEncrypted = ""

		err := h.db.SaveMailboxSettings(settings)
		if err != nil {
			data.Error = fmt.Sprintf("Failed to save settings: %v", err)
		} else {
			data.Success = "Settings saved successfully!"
			// Re-read settings so the template gets fresh (decrypted) values for next edit
			settings, err = h.db.GetMailboxSettings(user.ID)
			if err != nil {
				// If re-read fails, keep current in-memory settings
			}
		}
	}

	settingsData := struct {
		TemplateData
		Settings *db.MailboxSettings
	}{
		TemplateData: data,
		Settings:     settings,
	}

	h.renderTemplate(w, "settings", settingsData)
}

// LogoutHandler handles user logout
func (h *Handler) LogoutHandler(w http.ResponseWriter, r *http.Request) {
	h.authService.GetSessionManager().DestroySession(w, r)
	http.Redirect(w, r, "/login", http.StatusSeeOther)
}

// ToggleTestModeHandler toggles test mode
func (h *Handler) ToggleTestModeHandler(w http.ResponseWriter, r *http.Request) {
	// Check authentication
	email, loggedIn := h.authService.GetSessionManager().GetUserEmail(r)
	if !loggedIn {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	user, err := h.db.GetUserByEmail(email)
	if err != nil {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	// Get current test mode
	settings, err := h.db.GetMailboxSettings(user.ID)
	if err != nil {
		http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
		return
	}

	// Toggle test mode
	err = h.db.ToggleTestMode(user.ID, !settings.TestMode)
	if err != nil {
		// Handle error
	}

	http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
}

// ProcessNowHandler triggers immediate email processing
func (h *Handler) ProcessNowHandler(w http.ResponseWriter, r *http.Request) {
	// Check authentication
	_, loggedIn := h.authService.GetSessionManager().GetUserEmail(r)
	if !loggedIn {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	// Signal the worker to process all users immediately
	h.worker.ProcessNow()

	// Redirect back to dashboard
	http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
}

// TestConnectionHandler tests IMAP connection with provided settings
func (h *Handler) TestConnectionHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	enc := json.NewEncoder(w)

	// Check authentication
	email, loggedIn := h.authService.GetSessionManager().GetUserEmail(r)
	if !loggedIn {
		enc.Encode(map[string]interface{}{"success": false, "error": "Not authenticated"})
		return
	}

	// Parse multipart form explicitly (FormData sends multipart)
	contentType := r.Header.Get("Content-Type")
	fmt.Printf("TestConnection Content-Type: %q\n", contentType)

	// Use ParseMultipartForm directly with 32MB max memory
	if err := r.ParseMultipartForm(32 << 20); err != nil {
		fmt.Printf("TestConnection ParseMultipartForm ERROR: %v\n", err)
		enc.Encode(map[string]interface{}{"success": false, "error": "Invalid form data: " + err.Error()})
		return
	}

	// DEBUG: log all multipart form values
	fmt.Printf("TestConnection MultipartForm keys: ")
	if r.MultipartForm != nil {
		for k, v := range r.MultipartForm.Value {
			fmt.Printf("%s=%q ", k, v[0])
		}
	}
	fmt.Println()

	imapHost := ""
	imapPort := 993
	imapUser := ""
	imapUsername := ""
	imapPass := ""
	imapTLS := false

	if r.MultipartForm != nil {
		if v, ok := r.MultipartForm.Value["imap_host"]; ok && len(v) > 0 {
			imapHost = v[0]
		}
		if v, ok := r.MultipartForm.Value["imap_port"]; ok && len(v) > 0 {
			imapPort = parseInt(v[0], 993)
		}
		if v, ok := r.MultipartForm.Value["imap_user"]; ok && len(v) > 0 {
			imapUser = v[0]
		}
		if v, ok := r.MultipartForm.Value["imap_username"]; ok && len(v) > 0 {
			imapUsername = v[0]
		}
		if v, ok := r.MultipartForm.Value["imap_pass"]; ok && len(v) > 0 {
			imapPass = v[0]
		}
		if v, ok := r.MultipartForm.Value["imap_tls"]; ok && len(v) > 0 {
			imapTLS = v[0] == "on"
		}
	}

	// Use separate username if provided, otherwise fall back to email
	loginUser := imapUsername
	if loginUser == "" {
		loginUser = imapUser
	}

	// If password is blank, try to use the stored (decrypted) password
	if imapPass == "" {
		user, uErr := h.db.GetUserByEmail(email)
		if uErr == nil {
			if settings, sErr := h.db.GetMailboxSettings(user.ID); sErr == nil {
				imapPass = settings.IMAPPassEncrypted
				// Also reload username from saved settings if form field was empty
				if imapUsername == "" && settings.IMAPUsername != "" {
					loginUser = settings.IMAPUsername
				}
			}
		}
	}

	// Log what we received for debugging
	fmt.Printf("TestConnection: host=%q port=%d user=%q username=%q login=%q pass=%q tls=%v\n",
		imapHost, imapPort, imapUser, imapUsername, loginUser, imapPass, imapTLS)

	if imapHost == "" || loginUser == "" || imapPass == "" {
		enc.Encode(map[string]interface{}{
			"success": false,
			"error":   fmt.Sprintf("IMAP host, user, and password are required. Got host=%q user=%q pass=%q", imapHost, loginUser, imapPass),
		})
		return
	}

	config := imap.Config{
		Host:     imapHost,
		Port:     imapPort,
		User:     loginUser,
		Password: imapPass,
		TLS:      imapTLS,
	}

	testClient := imap.NewClient(config)
	if err := testClient.Connect(); err != nil {
		enc.Encode(map[string]interface{}{"success": false, "error": err.Error()})
		return
	}
	testClient.Close()

	enc.Encode(map[string]interface{}{"success": true, "error": nil})
}

// renderTemplate renders a page template with the given data.
// page is one of: "login", "verify", "dashboard", "settings"
func (h *Handler) renderTemplate(w http.ResponseWriter, page string, data interface{}) {
	tmpl, ok := h.templates[page]
	if !ok {
		http.Error(w, fmt.Sprintf("Template not found: %s", page), http.StatusInternalServerError)
		return
	}
	// Execute the base template which calls {{ template "content" . }}
	// (the page-specific "content" is available within this isolated template set)
	err := tmpl.ExecuteTemplate(w, "base", data)
	if err != nil {
		http.Error(w, fmt.Sprintf("Template error: %v", err), http.StatusInternalServerError)
	}
}

// parseInt parses an integer with a default value
func parseInt(s string, defaultValue int) int {
	if s == "" {
		return defaultValue
	}
	var result int
	_, err := fmt.Sscanf(s, "%d", &result)
	if err != nil {
		return defaultValue
	}
	return result
}

// RegisterRoutes registers all HTTP routes
func (h *Handler) RegisterRoutes(router *mux.Router) {
	// Static files
	router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("src/web/static"))))

	// Public routes
	router.HandleFunc("/", h.LoginHandler).Methods("GET")
	router.HandleFunc("/login", h.LoginHandler).Methods("GET", "POST")
	router.HandleFunc("/verify", h.VerifyHandler).Methods("GET", "POST")
	router.HandleFunc("/resend-otp", h.ResendOTPHandler).Methods("POST")

	// Protected routes (require authentication)
	router.HandleFunc("/dashboard", h.DashboardHandler).Methods("GET")
	router.HandleFunc("/settings", h.SettingsHandler).Methods("GET", "POST")
	router.HandleFunc("/logout", h.LogoutHandler).Methods("GET")
	router.HandleFunc("/toggle-test-mode", h.ToggleTestModeHandler).Methods("POST")
	router.HandleFunc("/test-connection", h.TestConnectionHandler).Methods("POST")
	router.HandleFunc("/process-now", h.ProcessNowHandler).Methods("GET")
}