package legal

import (
	"encoding/json"
	"fmt"
	"log/slog"
	"net/http"
	"strconv"
	"strings"
	"time"

	"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 controlRequest struct {
	ControlKey          string          `json:"control_key"`
	ControlType         string          `json:"control_type"`
	Jurisdiction        string          `json:"jurisdiction"`
	ProductScope        string          `json:"product_scope"`
	ProviderCategory    string          `json:"provider_category"`
	ProviderName        string          `json:"provider_name"`
	Status              string          `json:"status"`
	RiskLevel           string          `json:"risk_level"`
	OwnerTeam           string          `json:"owner_team"`
	DecisionSummary     string          `json:"decision_summary"`
	LegalMemoReference  string          `json:"legal_memo_reference"`
	ContractReference   string          `json:"contract_reference"`
	PolicyVersion       string          `json:"policy_version"`
	EvidenceReference   string          `json:"evidence_reference"`
	RequiredDisclosures []string        `json:"required_disclosures"`
	EffectiveAt         string          `json:"effective_at"`
	ExpiresAt           string          `json:"expires_at"`
	Metadata            json.RawMessage `json:"metadata"`
}

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(), queryLimit(r))
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, dashboard)
}

func (h *Handler) Controls(w http.ResponseWriter, r *http.Request) {
	h.listControls(w, r, strings.TrimSpace(r.URL.Query().Get("type")), "legal_market_access_controls")
}

func (h *Handler) UpsertControl(w http.ResponseWriter, r *http.Request) {
	h.upsertControl(w, r, "")
}

func (h *Handler) RegulatedActivities(w http.ResponseWriter, r *http.Request) {
	h.listControls(w, r, TypeRegulatedActivity, "regulated_activities")
}

func (h *Handler) UpsertRegulatedActivity(w http.ResponseWriter, r *http.Request) {
	h.upsertControl(w, r, TypeRegulatedActivity)
}

func (h *Handler) OperatingModels(w http.ResponseWriter, r *http.Request) {
	h.listControls(w, r, TypeOperatingModel, "operating_models")
}

func (h *Handler) UpsertOperatingModel(w http.ResponseWriter, r *http.Request) {
	h.upsertControl(w, r, TypeOperatingModel)
}

func (h *Handler) LegalMemos(w http.ResponseWriter, r *http.Request) {
	h.listControls(w, r, TypeLegalMemo, "legal_memos")
}

func (h *Handler) UpsertLegalMemo(w http.ResponseWriter, r *http.Request) {
	h.upsertControl(w, r, TypeLegalMemo)
}

func (h *Handler) ProductTermApprovals(w http.ResponseWriter, r *http.Request) {
	h.listControls(w, r, TypeProductTermApproval, "product_term_approvals")
}

func (h *Handler) UpsertProductTermApproval(w http.ResponseWriter, r *http.Request) {
	h.upsertControl(w, r, TypeProductTermApproval)
}

func (h *Handler) ProviderContracts(w http.ResponseWriter, r *http.Request) {
	h.listControls(w, r, TypeProviderContract, "provider_contracts")
}

func (h *Handler) UpsertProviderContract(w http.ResponseWriter, r *http.Request) {
	h.upsertControl(w, r, TypeProviderContract)
}

func (h *Handler) SafeguardingModels(w http.ResponseWriter, r *http.Request) {
	h.listControls(w, r, TypeSafeguardingModel, "safeguarding_models")
}

func (h *Handler) UpsertSafeguardingModel(w http.ResponseWriter, r *http.Request) {
	h.upsertControl(w, r, TypeSafeguardingModel)
}

func (h *Handler) PolicyDocuments(w http.ResponseWriter, r *http.Request) {
	h.listControls(w, r, TypePolicyDocument, "policy_documents")
}

func (h *Handler) UpsertPolicyDocument(w http.ResponseWriter, r *http.Request) {
	h.upsertControl(w, r, TypePolicyDocument)
}

func (h *Handler) JurisdictionRules(w http.ResponseWriter, r *http.Request) {
	h.listControls(w, r, TypeJurisdictionRule, "jurisdiction_product_rules")
}

func (h *Handler) UpsertJurisdictionRule(w http.ResponseWriter, r *http.Request) {
	h.upsertControl(w, r, TypeJurisdictionRule)
}

func (h *Handler) listControls(w http.ResponseWriter, r *http.Request, controlType, responseKey string) {
	items, err := h.repo.ListControls(
		r.Context(),
		controlType,
		strings.TrimSpace(r.URL.Query().Get("jurisdiction")),
		strings.TrimSpace(r.URL.Query().Get("status")),
		queryLimit(r),
	)
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{responseKey: items})
}

func (h *Handler) upsertControl(w http.ResponseWriter, r *http.Request, forcedType string) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	var req controlRequest
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	if forcedType != "" {
		req.ControlType = forcedType
	}
	effectiveAt, err := parseOptionalTime(req.EffectiveAt, "effective_at")
	if err != nil {
		respond.Error(w, err)
		return
	}
	expiresAt, err := parseOptionalTime(req.ExpiresAt, "expires_at")
	if err != nil {
		respond.Error(w, err)
		return
	}
	control, err := h.repo.UpsertControl(r.Context(), ControlParams{
		ControlKey:          req.ControlKey,
		ControlType:         req.ControlType,
		Jurisdiction:        req.Jurisdiction,
		ProductScope:        req.ProductScope,
		ProviderCategory:    req.ProviderCategory,
		ProviderName:        req.ProviderName,
		Status:              req.Status,
		RiskLevel:           req.RiskLevel,
		OwnerTeam:           req.OwnerTeam,
		DecisionSummary:     req.DecisionSummary,
		LegalMemoReference:  req.LegalMemoReference,
		ContractReference:   req.ContractReference,
		PolicyVersion:       req.PolicyVersion,
		EvidenceReference:   req.EvidenceReference,
		RequiredDisclosures: req.RequiredDisclosures,
		EffectiveAt:         effectiveAt,
		ExpiresAt:           expiresAt,
		Metadata:            req.Metadata,
		AdminUserID:         claims.Subject,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.legal_market_access_control.upserted", "legal_market_access_control", control.ID, map[string]any{
		"control_key":   control.ControlKey,
		"control_type":  control.ControlType,
		"jurisdiction":  control.Jurisdiction,
		"product_scope": control.ProductScope,
		"status":        control.Status,
	})
	respond.JSON(w, http.StatusOK, control)
}

func parseOptionalTime(raw, field string) (*time.Time, error) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return nil, nil
	}
	parsed, err := time.Parse(time.RFC3339, raw)
	if err != nil {
		return nil, fmt.Errorf("%w: %s must be RFC3339", domain.ErrValidation, field)
	}
	utc := parsed.UTC()
	return &utc, nil
}

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
}

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