package beneficiaries

import (
	"fmt"
	"log/slog"
	"net/http"

	"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 createRequest struct {
	Name         string               `json:"name"`
	IBAN         string               `json:"iban"`
	BIC          string               `json:"bic"`
	Currency     string               `json:"currency"`
	RoutingCodes []domain.RoutingCode `json:"routing_codes"`
}

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

func (h *Handler) Create(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}

	var req createRequest
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	details, err := NormalizeAndValidate(req.Name, req.IBAN, req.BIC, req.Currency, req.RoutingCodes)
	if err != nil {
		respond.Error(w, err)
		return
	}

	beneficiary, err := h.repo.Create(r.Context(), CreateParams{
		UserID:       claims.Subject,
		Name:         details.Name,
		IBAN:         details.IBAN,
		BIC:          details.BIC,
		Currency:     details.Currency,
		RoutingCodes: details.RoutingCodes,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "beneficiary.saved", beneficiary.ID, map[string]string{
		"iban": beneficiary.IBAN, "currency": beneficiary.Currency, "routing_code_count": fmt.Sprintf("%d", len(beneficiary.RoutingCodes)),
	})
	respond.Created(w, beneficiary)
}

func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	beneficiaries, err := h.repo.ListByUser(r.Context(), claims.Subject)
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"beneficiaries": beneficiaries})
}

func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	id := r.PathValue("id")
	if err := domain.ValidateUUID("id", id); err != nil {
		respond.Error(w, err)
		return
	}
	if err := h.repo.Delete(r.Context(), claims.Subject, id); err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "beneficiary.deleted", id, nil)
	respond.NoContent(w)
}

func (h *Handler) recordAudit(r *http.Request, actorID, eventType, targetID string, metadata any) {
	if err := h.audit.Record(r.Context(), audit.Event{
		ActorUserID: &actorID,
		EventType:   eventType,
		TargetType:  "beneficiary",
		TargetID:    targetID,
		Metadata:    metadata,
		RemoteIP:    httputil.RemoteIP(r),
		UserAgent:   r.UserAgent(),
	}); err != nil {
		h.log.Error("audit record failed", "error", err)
	}
}
