package reportscollector

import (
	"errors"
	"fmt"
	"log"
	"mime"
	"net/http"

	"github.com/prometheus/client_golang/prometheus"
)

var ErrNoMatch = errors.New("no match")

type Handler interface {
	Parse(string, *http.Request) ([]Event, error)
}

type Sink interface {
	Send(Event)
}

type Collector struct {
	handlers []Handler
	sink     Sink
}

func NewCollector(sink Sink, handlers ...Handler) *Collector {
	return &Collector{
		handlers: handlers,
		sink:     sink,
	}
}

func (c *Collector) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case http.MethodPost:
	case http.MethodOptions:
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "POST")
		w.Header().Set("Access-Control-Allow-Headers", "content-type")
		w.Header().Set("Access-Control-Max-Age", "86400")
		w.WriteHeader(http.StatusNoContent)
		return
	default:
		http.Error(w, "Bad method", http.StatusMethodNotAllowed)
		return
	}

	ct, _, err := mime.ParseMediaType(req.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, fmt.Sprintf("Bad Content-Type: %v", err.Error()), http.StatusBadRequest)
		log.Printf("error parsing content-type: %v", err)
		return
	}

	// Find a handler that can successfully parse the request, and
	// get a list of Events.
	var events []Event
	matched := false
hloop:
	for _, h := range c.handlers {
		var err error
		events, err = h.Parse(ct, req)
		switch err {
		case ErrNoMatch:
			continue
		case nil:
			matched = true
			w.WriteHeader(http.StatusOK)
			break hloop
		default:
			log.Printf("error parsing report (%s): %v", ct, err)
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}
	}

	if !matched {
		log.Printf("no matching handlers for \"%s\"", ct)
		http.Error(w, "No matching handlers", http.StatusBadRequest)
		return
	}

	// Augment the Events with additional information obtained
	// from the HTTP request, and send them to the forwarder.
	for _, e := range events {
		c.sink.Send(e)
		reportsByType.WithLabelValues(
			e.GetString("type"), e.GetString("domain")).Inc()
	}
}

var (
	reportsByType = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "reports_total",
			Help: "Number of reports by type.",
		},
		[]string{"type", "domain"},
	)
)

func init() {
	prometheus.MustRegister(reportsByType)
}