package yookassa

import (
	"encoding/json"
	"fmt"
)

// Notification events this integration subscribes to. An event name is "<object>.<status>": the
// object whose status changed and the status it entered.
const (
	// EventPaymentSucceeded means the money was taken and the order may be credited.
	EventPaymentSucceeded = "payment.succeeded"
	// EventPaymentCanceled means the payment was actively declined or abandoned; nothing is credited
	// and the payer is told the attempt failed.
	EventPaymentCanceled = "payment.canceled"
	// EventRefundSucceeded reports a completed refund. It matters because the merchant cabinet is a
	// second way to issue one: a refund made there never passes through our API, so without this
	// event the money would go back while the chips stayed credited.
	EventRefundSucceeded = "refund.succeeded"
)

// notificationType is the fixed value of a notification envelope's type field.
const notificationType = "notification"

// Notification is an incoming webhook envelope. Object is left raw because its shape depends on the
// event — a payment for payment.*, a refund for refund.* — and because nothing in it may be acted on
// before GetPayment confirms it: YooKassa does not sign notifications.
type Notification struct {
	Type   string          `json:"type"`
	Event  string          `json:"event"`
	Object json.RawMessage `json:"object"`
}

// ParseNotification decodes a webhook body and checks the envelope is a notification with an event.
// It deliberately validates nothing else: the body is untrusted input whose only job is to name an
// object to re-read from the API.
func ParseNotification(body []byte) (Notification, error) {
	var n Notification
	if err := json.Unmarshal(body, &n); err != nil {
		return Notification{}, fmt.Errorf("yookassa: decode notification: %w", err)
	}
	if n.Type != notificationType || n.Event == "" {
		return Notification{}, fmt.Errorf("yookassa: not a notification envelope (type %q, event %q)", n.Type, n.Event)
	}
	return n, nil
}

// Payment decodes the notification's object as a payment. Use it only for payment.* events; it
// yields the payment id to re-read, never the payment state to act on.
func (n Notification) Payment() (Payment, error) {
	var p Payment
	if err := json.Unmarshal(n.Object, &p); err != nil {
		return Payment{}, fmt.Errorf("yookassa: decode notification payment: %w", err)
	}
	if p.ID == "" {
		return Payment{}, fmt.Errorf("yookassa: notification payment has no id")
	}
	return p, nil
}

// Refund decodes the notification's object as a refund. Use it only for refund.* events; it yields
// the refund id to re-read, never the refund state to act on.
func (n Notification) Refund() (Refund, error) {
	var r Refund
	if err := json.Unmarshal(n.Object, &r); err != nil {
		return Refund{}, fmt.Errorf("yookassa: decode notification refund: %w", err)
	}
	if r.ID == "" {
		return Refund{}, fmt.Errorf("yookassa: notification refund has no id")
	}
	return r, nil
}
