package risk

import (
	"log/slog"
	"net/http"
	"strconv"
	"strings"

	"github.com/niels/banking-app/backend/internal/audit"
	"github.com/niels/banking-app/backend/internal/domain"
	"github.com/niels/banking-app/backend/internal/httpapi/middleware"
	"github.com/niels/banking-app/backend/internal/platform/httputil"
	"github.com/niels/banking-app/backend/internal/respond"
)

type Handler struct {
	repo  *Repository
	audit *audit.Repository
	log   *slog.Logger
}

type ruleRequest struct {
	UserID                      string `json:"user_id"`
	Operation                   string `json:"operation"`
	Currency                    string `json:"currency"`
	SingleTransactionLimitCents int64  `json:"single_transaction_limit_cents"`
	DailyLimitCents             int64  `json:"daily_limit_cents"`
	MonthlyLimitCents           int64  `json:"monthly_limit_cents"`
	DailyCountLimit             int    `json:"daily_count_limit"`
	MonthlyCountLimit           int    `json:"monthly_count_limit"`
	Active                      *bool  `json:"active"`
	Description                 string `json:"description"`
}

func NewHandler(repo *Repository, auditRepo *audit.Repository, log *slog.Logger) *Handler {
	return &Handler{repo: repo, audit: auditRepo, log: log}
}

func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
	dashboard, err := h.repo.Dashboard(
		r.Context(),
		strings.TrimSpace(r.URL.Query().Get("decision")),
		strings.TrimSpace(r.URL.Query().Get("operation")),
		queryLimit(r),
	)
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, dashboard)
}

func (h *Handler) ListRules(w http.ResponseWriter, r *http.Request) {
	rules, err := h.repo.ListRules(
		r.Context(),
		strings.TrimSpace(r.URL.Query().Get("operation")),
		strings.TrimSpace(r.URL.Query().Get("user_id")),
		r.URL.Query().Get("include_inactive") != "true",
		queryLimit(r),
	)
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"rules": rules})
}

func (h *Handler) ListEvents(w http.ResponseWriter, r *http.Request) {
	events, err := h.repo.ListEvents(
		r.Context(),
		strings.TrimSpace(r.URL.Query().Get("decision")),
		strings.TrimSpace(r.URL.Query().Get("operation")),
		queryLimit(r),
	)
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"events": events})
}

func (h *Handler) CreateRule(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	params, err := h.ruleParamsFromRequest(r, claims.Subject, "")
	if err != nil {
		respond.Error(w, err)
		return
	}
	rule, err := h.repo.CreateRule(r.Context(), params)
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.payment_limit_rule.created", rule)
	respond.Created(w, rule)
}

func (h *Handler) UpdateRule(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	ruleID := r.PathValue("id")
	if err := domain.ValidateUUID("id", ruleID); err != nil {
		respond.Error(w, err)
		return
	}
	params, err := h.ruleParamsFromRequest(r, claims.Subject, ruleID)
	if err != nil {
		respond.Error(w, err)
		return
	}
	rule, err := h.repo.UpdateRule(r.Context(), params)
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.payment_limit_rule.updated", rule)
	respond.JSON(w, http.StatusOK, rule)
}

func (h *Handler) ruleParamsFromRequest(r *http.Request, adminUserID, ruleID string) (RuleParams, error) {
	var req ruleRequest
	if err := respond.DecodeJSON(r, &req); err != nil {
		return RuleParams{}, err
	}
	active := true
	if req.Active != nil {
		active = *req.Active
	}
	return RuleParams{
		RuleID:                      ruleID,
		AdminUserID:                 adminUserID,
		UserID:                      req.UserID,
		Operation:                   req.Operation,
		Currency:                    req.Currency,
		SingleTransactionLimitCents: req.SingleTransactionLimitCents,
		DailyLimitCents:             req.DailyLimitCents,
		MonthlyLimitCents:           req.MonthlyLimitCents,
		DailyCountLimit:             req.DailyCountLimit,
		MonthlyCountLimit:           req.MonthlyCountLimit,
		Active:                      active,
		Description:                 req.Description,
	}, nil
}

func (h *Handler) recordAudit(r *http.Request, adminUserID, eventType string, rule LimitRule) {
	if h.audit == nil {
		return
	}
	if err := h.audit.Record(r.Context(), audit.Event{
		ActorUserID: &adminUserID,
		EventType:   eventType,
		TargetType:  "payment_limit_rule",
		TargetID:    rule.ID,
		Metadata: map[string]any{
			"user_id":                        rule.UserID,
			"operation":                      rule.Operation,
			"currency":                       rule.Currency,
			"single_transaction_limit_cents": rule.SingleTransactionLimitCents,
			"daily_limit_cents":              rule.DailyLimitCents,
			"monthly_limit_cents":            rule.MonthlyLimitCents,
			"daily_count_limit":              rule.DailyCountLimit,
			"monthly_count_limit":            rule.MonthlyCountLimit,
			"active":                         rule.Active,
		},
		RemoteIP:  httputil.RemoteIP(r),
		UserAgent: r.UserAgent(),
	}); err != nil && h.log != nil {
		h.log.Error("audit record failed", "error", err)
	}
}

func queryLimit(r *http.Request) int {
	limit := 50
	if raw := r.URL.Query().Get("limit"); raw != "" {
		if parsed, err := strconv.Atoi(raw); err == nil {
			limit = parsed
		}
	}
	return limit
}
