package endpoint

import (
	"context"
	"encoding/json"
	"github.com/go-chi/chi"
	"net/http"

	kitlog "github.com/go-kit/kit/log"
	"github.com/prometheus/client_golang/prometheus/promhttp"

	shipping "github.com/longjoy/micro-go-course/section19/cargo/model"
	"github.com/longjoy/micro-go-course/section19/cargo/service/booking"
	"github.com/longjoy/micro-go-course/section19/cargo/service/handling"
	"github.com/longjoy/micro-go-course/section19/cargo/service/tracking"
)

// Server holds the dependencies for a HTTP server.
type Server struct {
	Booking  booking.Service
	Tracking tracking.Service
	Handling handling.Service

	Logger kitlog.Logger

	router chi.Router
}

// New returns a new HTTP server.
func New(bs booking.Service, ts tracking.Service, hs handling.Service, logger kitlog.Logger) *Server {
	s := &Server{
		Booking:  bs,
		Tracking: ts,
		Handling: hs,
		Logger:   logger,
	}

	r := chi.NewRouter()

	r.Use(accessControl)

	r.Route("/booking", func(r chi.Router) {
		h := bookingHandler{s.Booking, s.Logger}
		r.Mount("/v1", h.router())
	})
	r.Route("/tracking", func(r chi.Router) {
		h := trackingHandler{s.Tracking, s.Logger}
		r.Mount("/v1", h.router())
	})
	r.Route("/handling", func(r chi.Router) {
		h := handlingHandler{s.Handling, s.Logger}
		r.Mount("/v1", h.router())
	})

	r.Method("GET", "/metrics", promhttp.Handler())

	s.router = r

	return s
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.router.ServeHTTP(w, r)
}

func accessControl(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type")

		if r.Method == "OPTIONS" {
			return
		}

		h.ServeHTTP(w, r)
	})
}

func encodeError(_ context.Context, err error, w http.ResponseWriter) {
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	switch err {
	case shipping.ErrUnknownCargo:
		w.WriteHeader(http.StatusNotFound)
	case tracking.ErrInvalidArgument:
		w.WriteHeader(http.StatusBadRequest)
	default:
		w.WriteHeader(http.StatusInternalServerError)
	}
	json.NewEncoder(w).Encode(map[string]interface{}{
		"error": err.Error(),
	})
}