package endpoint

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

	kitlog "github.com/go-kit/kit/log"

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

type bookingHandler struct {
	s booking.Service

	logger kitlog.Logger
}

func (h *bookingHandler) router() chi.Router {
	r := chi.NewRouter()

	r.Route("/cargos", func(r chi.Router) {
		r.Post("/", h.bookCargo)
		r.Get("/", h.listCargos)
		r.Route("/{trackingID}", func(r chi.Router) {
			r.Get("/", h.loadCargo)
			r.Get("/request_routes", h.requestRoutes)
			r.Post("/assign_to_route", h.assignToRoute)
			r.Post("/change_destination", h.changeDestination)
		})

	})
	r.Get("/locations", h.listLocations)

	r.Method("GET", "/docs", http.StripPrefix("/booking/v1/docs", http.FileServer(http.Dir("booking/docs"))))

	return r
}

func (h *bookingHandler) bookCargo(w http.ResponseWriter, r *http.Request) {
	ctx := context.Background()

	var request struct {
		Origin          shipping.UNLocode
		Destination     shipping.UNLocode
		ArrivalDeadline time.Time
	}

	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		h.logger.Log("error", err)
		encodeError(ctx, err, w)
		return
	}

	id, err := h.s.BookNewCargo(request.Origin, request.Destination, request.ArrivalDeadline)
	if err != nil {
		encodeError(ctx, err, w)
		return
	}

	var response = struct {
		ID shipping.TrackingID `json:"tracking_id"`
	}{
		ID: id,
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	if err := json.NewEncoder(w).Encode(response); err != nil {
		h.logger.Log("error", err)
		encodeError(ctx, err, w)
		return
	}
}

func (h *bookingHandler) loadCargo(w http.ResponseWriter, r *http.Request) {
	ctx := context.Background()

	trackingID := shipping.TrackingID(chi.URLParam(r, "trackingID"))

	c, err := h.s.LoadCargo(trackingID)
	if err != nil {
		encodeError(ctx, err, w)
		return
	}

	var response = struct {
		Cargo booking.Cargo `json:"cargo"`
	}{
		Cargo: c,
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	if err := json.NewEncoder(w).Encode(response); err != nil {
		h.logger.Log("error", err)
		encodeError(ctx, err, w)
		return
	}
}

func (h *bookingHandler) requestRoutes(w http.ResponseWriter, r *http.Request) {
	ctx := context.Background()

	trackingID := shipping.TrackingID(chi.URLParam(r, "trackingID"))

	itin := h.s.RequestPossibleRoutesForCargo(trackingID)

	var response = struct {
		Routes []shipping.Itinerary `json:"routes"`
	}{
		Routes: itin,
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	if err := json.NewEncoder(w).Encode(response); err != nil {
		h.logger.Log("error", err)
		encodeError(ctx, err, w)
		return
	}
}

func (h *bookingHandler) assignToRoute(w http.ResponseWriter, r *http.Request) {
	ctx := context.Background()

	trackingID := shipping.TrackingID(chi.URLParam(r, "trackingID"))

	var request struct {
		Itinerary shipping.Itinerary `json:"route"`
	}

	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		h.logger.Log("error", err)
		encodeError(ctx, err, w)
		return
	}

	_, err := h.s.AssignCargoToRoute(trackingID, request.Itinerary)
	if err != nil {
		encodeError(ctx, err, w)
		return
	}
}

func (h *bookingHandler) changeDestination(w http.ResponseWriter, r *http.Request) {
	ctx := context.Background()

	trackingID := shipping.TrackingID(chi.URLParam(r, "trackingID"))

	var request struct {
		Destination shipping.UNLocode `json:"destination"`
	}

	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		h.logger.Log("error", err)
		encodeError(ctx, err, w)
		return
	}

	_, err := h.s.ChangeDestination(trackingID, request.Destination)
	if err != nil {
		encodeError(ctx, err, w)
		return
	}
}

func (h *bookingHandler) listCargos(w http.ResponseWriter, r *http.Request) {
	ctx := context.Background()

	cs := h.s.Cargos()

	var response = struct {
		Cargos []booking.Cargo `json:"cargos"`
	}{
		Cargos: cs,
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	if err := json.NewEncoder(w).Encode(response); err != nil {
		h.logger.Log("error", err)
		encodeError(ctx, err, w)
		return
	}
}

func (h *bookingHandler) listLocations(w http.ResponseWriter, r *http.Request) {
	ctx := context.Background()

	ls := h.s.Locations()

	var response = struct {
		Locations []booking.Location `json:"locations"`
	}{
		Locations: ls,
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	if err := json.NewEncoder(w).Encode(response); err != nil {
		h.logger.Log("error", err)
		encodeError(ctx, err, w)
		return
	}
}