// Package webhooks owns the HMAC-validated public webhook ingress and the
// per-source handlers that translate inbound payloads into typed events on
// the eventbus.
//
// HMAC validation is mandatory. Per-source signature conventions are
// declared in each handler file (X-Hub-Signature-256, autobrr's static
// token header via X-Webhook-Token, etc.). Each endpoint has a shared
// secret stored in the age-encrypted secrets store under the canonical key
// `webhook:<endpoint_id>:token`.
package webhooks

import (
	"context"
	"net/http"
	"time"
)

// AutobrrGrabEvent is published on TopicAutobrrGrab when autobrr's webhook
// reports a successful filter match. The release name is parsed from the
// payload; the info_hash may be empty at grab time (qBittorrent assigns it
// after the torrent is added).
type AutobrrGrabEvent struct {
	EventID      string    `json:"event_id"`
	FilterID     string    `json:"filter_id"`
	FilterName   string    `json:"filter_name"`
	ReleaseName  string    `json:"release_name"`
	Indexer      string    `json:"indexer"`
	InfoHash     string    `json:"info_hash,omitempty"`
	Size         int64     `json:"size,omitempty"`
	GrabbedAt    time.Time `json:"grabbed_at"`
	ReceivedAt   time.Time `json:"received_at"`
	EndpointID   string    `json:"endpoint_id"`
}

// QbitEvent is published on TopicTorrentCompleted and TopicTorrentStateChanged
// when qBittorrent's external-program-on-event hook fires for the operator-
// installed shell template.
type QbitEvent struct {
	Kind       string    `json:"kind"` // "completed" | "state_changed" | "tracker_changed"
	InfoHash   string    `json:"info_hash"`
	Name       string    `json:"name,omitempty"`
	State      string    `json:"state,omitempty"`
	Category   string    `json:"category,omitempty"`
	Tags       string    `json:"tags,omitempty"`
	SavePath   string    `json:"save_path,omitempty"`
	Tracker    string    `json:"tracker,omitempty"`
	ReceivedAt time.Time `json:"received_at"`
	EndpointID string    `json:"endpoint_id"`
}

// CrossseedEvent is published on TopicCrossseedMatchFound and
// TopicCrossseedMatchApplied.
type CrossseedEvent struct {
	Kind             string    `json:"kind"` // "match_found" | "match_applied"
	InfoHash         string    `json:"info_hash"`
	Name             string    `json:"name,omitempty"`
	SourceTracker    string    `json:"source_tracker,omitempty"`
	DestTracker      string    `json:"dest_tracker,omitempty"`
	SourceInfoHash   string    `json:"source_info_hash,omitempty"`
	DecisionReason   string    `json:"decision_reason,omitempty"`
	ReceivedAt       time.Time `json:"received_at"`
	EndpointID       string    `json:"endpoint_id"`
}

// CustomEvent wraps an operator-defined generic webhook body. Payload is the
// raw JSON decoded as a map; subscribers handle their own type assertions.
type CustomEvent struct {
	EndpointID string         `json:"endpoint_id"`
	Payload    map[string]any `json:"payload"`
	ReceivedAt time.Time      `json:"received_at"`
}

// Handler is the contract every per-source handler implements. The generic
// HMAC validation runs before Handle is called; the handler receives the
// raw body so it can parse its source-specific shape.
type Handler interface {
	// HandlerType returns the type string that matches webhook_endpoints.handler_type.
	HandlerType() string

	// Handle parses body, publishes typed events to the bus, and returns
	// the response status. An error returns 400 to the caller; a nil error
	// returns 200 with `{"received": true}`.
	Handle(ctx context.Context, r *http.Request, body []byte, endpointID string) error
}
