package api

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"

	"archivmail/internal/auth"
	imapstore "archivmail/internal/imap"
	"archivmail/internal/userstore"
)

// ── IMAP handlers ─────────────────────────────────────────────────────────

func (s *Server) handleListImap(w http.ResponseWriter, r *http.Request) {
	if s.imapStore == nil {
		writeError(w, http.StatusServiceUnavailable, "IMAP not configured")
		return
	}
	sess := sessionFromCtx(r.Context())
	// SEC-03: Use HasRole to correctly check admin privileges (domain_admin, admin, superadmin).
	isAdmin := auth.HasRole(sess.Role, userstore.RoleDomainAdmin)
	accounts, err := s.imapStore.List(r.Context(), sess.Username, isAdmin, sess.TenantID)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "failed to list IMAP accounts")
		return
	}
	if accounts == nil {
		accounts = []imapstore.Account{}
	}
	writeJSON(w, http.StatusOK, accounts)
}

func (s *Server) handleCreateImap(w http.ResponseWriter, r *http.Request) {
	if s.imapStore == nil {
		writeError(w, http.StatusServiceUnavailable, "IMAP not configured")
		return
	}
	var req struct {
		Name            string   `json:"name"`
		Host            string   `json:"host"`
		Port            int      `json:"port"`
		TLS             string   `json:"tls"`
		Username        string   `json:"username"`
		Password        string   `json:"password"`
		ExcludedFolders []string `json:"excluded_folders"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "invalid request body")
		return
	}
	if req.Name == "" || req.Host == "" || req.Username == "" || req.Password == "" {
		writeError(w, http.StatusBadRequest, "name, host, username and password are required")
		return
	}
	if req.Port <= 0 {
		req.Port = 993
	}
	if req.TLS == "" {
		req.TLS = "ssl"
	}
	if req.ExcludedFolders == nil {
		req.ExcludedFolders = []string{}
	}

	sess := sessionFromCtx(r.Context())
	acc := imapstore.Account{
		Owner:           sess.Username,
		Name:            req.Name,
		Host:            req.Host,
		Port:            req.Port,
		TLS:             req.TLS,
		Username:        req.Username,
		ExcludedFolders: req.ExcludedFolders,
		TenantID:        sess.TenantID,
	}

	created, err := s.imapStore.Create(r.Context(), acc, req.Password)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "failed to create IMAP account")
		return
	}

	writeJSON(w, http.StatusCreated, created)
}

func (s *Server) handleDeleteImap(w http.ResponseWriter, r *http.Request) {
	if s.imapStore == nil {
		writeError(w, http.StatusServiceUnavailable, "IMAP not configured")
		return
	}
	idStr := r.PathValue("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid id")
		return
	}

	acc, err := s.imapStore.Get(r.Context(), id)
	if err != nil {
		writeError(w, http.StatusNotFound, "account not found")
		return
	}

	sess := sessionFromCtx(r.Context())
	if acc.Owner != sess.Username && !auth.HasRole(sess.Role, userstore.RoleDomainAdmin) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}
	if !tenantAccessAllowed(sess, acc.TenantID) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}

	if err := s.imapStore.Delete(r.Context(), id); err != nil {
		writeError(w, http.StatusInternalServerError, "failed to delete account")
		return
	}

	writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
}

func (s *Server) handleTestImap(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Host     string `json:"host"`
		Port     int    `json:"port"`
		TLS      string `json:"tls"`
		Username string `json:"username"`
		Password string `json:"password"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "invalid request body")
		return
	}
	if req.Host == "" || req.Username == "" || req.Password == "" {
		writeError(w, http.StatusBadRequest, "host, username and password are required")
		return
	}
	if req.Port <= 0 {
		req.Port = 993
	}
	if req.TLS == "" {
		req.TLS = "ssl"
	}

	c, err := imapstore.Connect(req.Host, req.Port, req.TLS)
	if err != nil {
		writeJSON(w, http.StatusOK, map[string]interface{}{
			"ok":    false,
			"error": fmt.Sprintf("Verbindung fehlgeschlagen: %v", err),
		})
		return
	}
	defer c.Close()

	if err := c.Login(req.Username, req.Password).Wait(); err != nil {
		writeJSON(w, http.StatusOK, map[string]interface{}{
			"ok":    false,
			"error": fmt.Sprintf("Anmeldung fehlgeschlagen: %v", err),
		})
		return
	}

	folders, err := imapstore.ListFolders(c.Client)
	if err != nil {
		writeJSON(w, http.StatusOK, map[string]interface{}{
			"ok":    false,
			"error": fmt.Sprintf("Ordner konnten nicht gelesen werden: %v", err),
		})
		return
	}

	writeJSON(w, http.StatusOK, map[string]interface{}{
		"ok":      true,
		"folders": folders,
	})
}

func (s *Server) handleStartImport(w http.ResponseWriter, r *http.Request) {
	if s.imapStore == nil || s.imapImporter == nil {
		writeError(w, http.StatusServiceUnavailable, "IMAP not configured")
		return
	}
	idStr := r.PathValue("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid id")
		return
	}

	acc, err := s.imapStore.Get(r.Context(), id)
	if err != nil {
		writeError(w, http.StatusNotFound, "account not found")
		return
	}

	sess := sessionFromCtx(r.Context())
	if acc.Owner != sess.Username && !auth.HasRole(sess.Role, userstore.RoleDomainAdmin) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}
	if !tenantAccessAllowed(sess, acc.TenantID) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}

	if acc.Status == "running" {
		writeError(w, http.StatusConflict, "import already running")
		return
	}

	go s.imapImporter.Run(context.Background(), id)

	// Return current account state (status will switch to "running" shortly)
	acc.Status = "running"
	writeJSON(w, http.StatusOK, acc)
}

func (s *Server) handleImapProgress(w http.ResponseWriter, r *http.Request) {
	if s.imapStore == nil {
		writeError(w, http.StatusServiceUnavailable, "IMAP not configured")
		return
	}
	idStr := r.PathValue("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid id")
		return
	}

	acc, err := s.imapStore.Get(r.Context(), id)
	if err != nil {
		writeError(w, http.StatusNotFound, "account not found")
		return
	}

	sess := sessionFromCtx(r.Context())
	if acc.Owner != sess.Username && !auth.HasRole(sess.Role, userstore.RoleDomainAdmin) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}
	if !tenantAccessAllowed(sess, acc.TenantID) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}

	writeJSON(w, http.StatusOK, acc)
}

// handleSyncNow triggers an immediate incremental sync for a single IMAP account.
func (s *Server) handleSyncNow(w http.ResponseWriter, r *http.Request) {
	if s.imapStore == nil || s.imapScheduler == nil {
		writeError(w, http.StatusServiceUnavailable, "IMAP not configured")
		return
	}
	idStr := r.PathValue("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid id")
		return
	}

	acc, err := s.imapStore.Get(r.Context(), id)
	if err != nil {
		writeError(w, http.StatusNotFound, "account not found")
		return
	}

	sess := sessionFromCtx(r.Context())
	if acc.Owner != sess.Username && !auth.HasRole(sess.Role, userstore.RoleDomainAdmin) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}
	if !tenantAccessAllowed(sess, acc.TenantID) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}

	if err := s.imapScheduler.TriggerSync(r.Context(), id); err != nil {
		s.logger.Error("trigger sync failed", "err", err)
		writeError(w, http.StatusConflict, "sync already running or failed to start")
		return
	}

	// Return the account with the updated sync_running flag reflected immediately.
	acc.SyncRunning = true
	writeJSON(w, http.StatusOK, acc)
}

// handleUpdateImapInterval updates the automatic sync interval or full credentials
// of an IMAP account. When name/host/username are present the credentials are updated;
// otherwise only sync_interval_min is changed.
func (s *Server) handleUpdateImapInterval(w http.ResponseWriter, r *http.Request) {
	if s.imapStore == nil {
		writeError(w, http.StatusServiceUnavailable, "IMAP not configured")
		return
	}
	idStr := r.PathValue("id")
	id, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid id")
		return
	}

	acc, err := s.imapStore.Get(r.Context(), id)
	if err != nil {
		writeError(w, http.StatusNotFound, "account not found")
		return
	}

	sess := sessionFromCtx(r.Context())
	if acc.Owner != sess.Username && !auth.HasRole(sess.Role, userstore.RoleDomainAdmin) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}
	if !tenantAccessAllowed(sess, acc.TenantID) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}

	var req struct {
		SyncIntervalMin int    `json:"sync_interval_min"`
		Name            string `json:"name"`
		Host            string `json:"host"`
		Port            int    `json:"port"`
		TLS             string `json:"tls"`
		Username        string `json:"username"`
		Password        string `json:"password"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	// Credential update when name/host/username are provided.
	if req.Name != "" || req.Host != "" || req.Username != "" {
		if req.Name == "" || req.Host == "" || req.Username == "" {
			writeError(w, http.StatusBadRequest, "name, host and username are required for credential update")
			return
		}
		port := req.Port
		if port == 0 {
			port = acc.Port
		}
		tls := req.TLS
		if tls == "" {
			tls = acc.TLS
		}
		updated := imapstore.Account{Name: req.Name, Host: req.Host, Port: port, TLS: tls, Username: req.Username}
		if err := s.imapStore.UpdateCredentials(r.Context(), id, updated, req.Password); err != nil {
			writeError(w, http.StatusInternalServerError, "failed to update credentials")
			return
		}
		acc, err = s.imapStore.Get(r.Context(), id)
		if err != nil {
			writeError(w, http.StatusInternalServerError, "failed to reload account")
			return
		}
		writeJSON(w, http.StatusOK, acc)
		return
	}

	// Sync interval update.
	// 0 = disabled; otherwise must be between 5 and 1440 minutes.
	if req.SyncIntervalMin != 0 && (req.SyncIntervalMin < 5 || req.SyncIntervalMin > 1440) {
		writeError(w, http.StatusBadRequest, "sync_interval_min must be 0 (disabled) or between 5 and 1440")
		return
	}

	if err := s.imapStore.UpdateSyncInterval(r.Context(), id, req.SyncIntervalMin); err != nil {
		writeError(w, http.StatusInternalServerError, "failed to update sync interval")
		return
	}

	acc.SyncIntervalMin = req.SyncIntervalMin
	writeJSON(w, http.StatusOK, acc)
}
