package api

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

	"archivmail/internal/audit"
)

// handleListExpiredMails returns metadata (no body content, SEC-29) for all
// mails whose retain_until has passed, so an admin can review and mark
// individual mails for the cron-driven purge (PROJ-56c).
// GET /api/admin/retention/expired — domain_admin+, tenant-scoped.
func (s *Server) handleListExpiredMails(w http.ResponseWriter, r *http.Request) {
	sess := sessionFromCtx(r.Context())

	mails, err := s.store.ListExpiredMails(r.Context(), sess.TenantID)
	if err != nil {
		writeError(w, http.StatusInternalServerError, err.Error())
		return
	}

	type item struct {
		ID          string `json:"id"`
		From        string `json:"from"`
		Subject     string `json:"subject"`
		ReceivedAt  string `json:"received_at"`
		RetainUntil string `json:"retain_until"`
		Marked      bool   `json:"marked"`
		MarkedBy    string `json:"marked_by,omitempty"`
	}
	out := make([]item, len(mails))
	for i, m := range mails {
		out[i] = item{
			ID:          m.ID,
			From:        m.From,
			Subject:     m.Subject,
			ReceivedAt:  m.ReceivedAt.UTC().Format(time.RFC3339),
			RetainUntil: m.RetainUntil.UTC().Format(time.RFC3339),
			Marked:      m.Marked,
			MarkedBy:    m.MarkedBy,
		}
	}
	writeJSON(w, http.StatusOK, map[string]interface{}{"mails": out})
}

// handleSetMarkedForDeletion sets or clears marked_for_deletion for one mail
// (PROJ-56c). This is the only way a mail becomes eligible for the
// cron-driven purge — Store.ListExpiredMarkedMailIDs requires retain_until
// to have passed AND this flag to be set; an expired retention date alone
// is never enough to delete a mail unattended.
// PUT /api/admin/mails/{id}/mark-deletion — domain_admin+ only.
func (s *Server) handleSetMarkedForDeletion(w http.ResponseWriter, r *http.Request) {
	id := r.PathValue("id")
	if id == "" {
		writeError(w, http.StatusBadRequest, "missing mail id")
		return
	}

	sess := sessionFromCtx(r.Context())
	mailTenant, err := s.store.GetTenantForMail(r.Context(), id)
	if err != nil {
		writeError(w, http.StatusNotFound, "mail not found")
		return
	}
	if !tenantAccessAllowed(sess, mailTenant) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}

	var body struct {
		Marked bool `json:"marked"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, http.StatusBadRequest, "invalid body")
		return
	}

	if err := s.store.SetMarkedForDeletion(r.Context(), id, body.Marked, sess.Username); err != nil {
		writeError(w, http.StatusInternalServerError, err.Error())
		return
	}

	if s.audlog != nil {
		eventType := "mail_marked_for_deletion"
		detail := fmt.Sprintf("mail_id=%s zur Löschung markiert", id)
		if !body.Marked {
			eventType = "mail_unmarked_for_deletion"
			detail = fmt.Sprintf("mail_id=%s Löschmarkierung entfernt", id)
		}
		s.audlog.Log(audit.Entry{
			EventType: eventType,
			Username:  sess.Username,
			TenantID:  sess.TenantID,
			IPAddress: s.remoteIP(r),
			MailID:    id,
			Success:   true,
			Detail:    detail,
		})
	}

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

// handlePurge deletes all mails whose retention period has expired.
// POST /api/admin/purge — superadmin only (PROJ-34).
func (s *Server) handlePurge(w http.ResponseWriter, r *http.Request) {
	deleted, err := s.store.Purge(r.Context())
	if err != nil {
		writeError(w, http.StatusInternalServerError, err.Error())
		return
	}
	writeJSON(w, http.StatusOK, map[string]interface{}{
		"deleted": deleted,
	})
}

// handleGetRetention returns the global retention config and per-tenant overrides.
// GET /api/admin/retention — superadmin only.
func (s *Server) handleGetRetention(w http.ResponseWriter, r *http.Request) {
	tenants, err := s.tenantStore.List(r.Context())
	if err != nil {
		writeError(w, http.StatusInternalServerError, err.Error())
		return
	}
	writeJSON(w, http.StatusOK, map[string]interface{}{
		"global_retention_days": s.globalRetentionDays,
		"tenants":               tenants,
	})
}

// handleSetTenantRetention sets retention_days for a specific tenant.
// PUT /api/admin/tenant/{id}/retention — superadmin only.
func (s *Server) handleSetTenantRetention(w http.ResponseWriter, r *http.Request) {
	idStr := r.PathValue("id")
	tenantID, err := strconv.ParseInt(idStr, 10, 64)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid tenant id")
		return
	}

	var body struct {
		RetentionDays int `json:"retention_days"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, http.StatusBadRequest, "invalid body")
		return
	}

	if err := s.tenantStore.SetRetentionDays(r.Context(), tenantID, body.RetentionDays); err != nil {
		writeError(w, http.StatusInternalServerError, err.Error())
		return
	}

	sess := sessionFromCtx(r.Context())
	if s.audlog != nil {
		s.audlog.Log(audit.Entry{
			EventType: "tenant_retention_changed",
			Username:  sess.Username,
			TenantID:  sess.TenantID,
			IPAddress: s.remoteIP(r),
			Success:   true,
			Detail:    fmt.Sprintf("tenant_id=%d retention_days=%d", tenantID, body.RetentionDays),
		})
	}

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