package api

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

	"archivmail/internal/audit"
	"archivmail/internal/storage"
)

// PROJ-51: Archiving rules CRUD (minimal PROJ-43). Rules optionally carry a
// retention_days value that determines a matching mail's retention period.
// Superadmin only — global, cross-tenant configuration.

type archivingRuleBody struct {
	TenantID      *int64 `json:"tenant_id"`
	CondType      string `json:"condition_type"`
	Pattern       string `json:"pattern"`
	Priority      int    `json:"priority"`
	RetentionDays *int   `json:"retention_days"`
}

// handleListArchivingRules returns all archiving rules.
// GET /api/admin/archiving-rules — superadmin only.
func (s *Server) handleListArchivingRules(w http.ResponseWriter, r *http.Request) {
	rules, err := s.store.ListArchivingRules(r.Context(), nil)
	if err != nil {
		writeError(w, http.StatusInternalServerError, err.Error())
		return
	}
	if rules == nil {
		rules = []storage.ArchivingRule{}
	}
	writeJSON(w, http.StatusOK, map[string]interface{}{
		"rules":              rules,
		"min_retention_days": s.store.MinRetentionDays(),
	})
}

// handleCreateArchivingRule creates a new archiving rule.
// POST /api/admin/archiving-rules — superadmin only.
func (s *Server) handleCreateArchivingRule(w http.ResponseWriter, r *http.Request) {
	var body archivingRuleBody
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, http.StatusBadRequest, "invalid body")
		return
	}
	id, err := s.store.CreateArchivingRule(r.Context(), storage.ArchivingRule{
		TenantID:      body.TenantID,
		CondType:      body.CondType,
		Pattern:       body.Pattern,
		Priority:      body.Priority,
		RetentionDays: body.RetentionDays,
	})
	if err != nil {
		writeError(w, http.StatusBadRequest, err.Error())
		return
	}

	s.auditRule(r, "archiving_rule_created", fmt.Sprintf("id=%d type=%s pattern=%s retention_days=%s",
		id, body.CondType, body.Pattern, retDaysStr(body.RetentionDays)))

	writeJSON(w, http.StatusCreated, map[string]interface{}{"id": id})
}

// handleUpdateArchivingRule updates an existing archiving rule.
// PUT /api/admin/archiving-rules/{id} — superadmin only.
func (s *Server) handleUpdateArchivingRule(w http.ResponseWriter, r *http.Request) {
	id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid rule id")
		return
	}
	var body archivingRuleBody
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, http.StatusBadRequest, "invalid body")
		return
	}
	if err := s.store.UpdateArchivingRule(r.Context(), storage.ArchivingRule{
		ID:            id,
		TenantID:      body.TenantID,
		CondType:      body.CondType,
		Pattern:       body.Pattern,
		Priority:      body.Priority,
		RetentionDays: body.RetentionDays,
	}); err != nil {
		writeError(w, http.StatusBadRequest, err.Error())
		return
	}

	s.auditRule(r, "archiving_rule_updated", fmt.Sprintf("id=%d type=%s pattern=%s retention_days=%s",
		id, body.CondType, body.Pattern, retDaysStr(body.RetentionDays)))

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

// handleDeleteArchivingRule deletes an archiving rule. Existing retain_until
// values on already-archived mails are left unchanged (PROJ-51).
// DELETE /api/admin/archiving-rules/{id} — superadmin only.
func (s *Server) handleDeleteArchivingRule(w http.ResponseWriter, r *http.Request) {
	id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid rule id")
		return
	}
	if err := s.store.DeleteArchivingRule(r.Context(), id); err != nil {
		writeError(w, http.StatusNotFound, err.Error())
		return
	}

	s.auditRule(r, "archiving_rule_deleted", fmt.Sprintf("id=%d", id))

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

func (s *Server) auditRule(r *http.Request, event, detail string) {
	if s.audlog == nil {
		return
	}
	sess := sessionFromCtx(r.Context())
	s.audlog.Log(audit.Entry{
		EventType: event,
		Username:  sess.Username,
		TenantID:  sess.TenantID,
		IPAddress: s.remoteIP(r),
		Success:   true,
		Detail:    detail,
	})
}

func retDaysStr(d *int) string {
	if d == nil {
		return "none"
	}
	return strconv.Itoa(*d)
}
