/*
 * Solidarity Mobility API
 *
 * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
  *
 * API version: 1.0.0
 * Generated by: OpenAPI Generator (https://openapi-generator.tech)
*/

package openapi

import (
	"context"
	"errors"
	"fmt"
	"github.com/paulmach/orb"
	"github.com/paulmach/orb/geojson"
	"github.com/spf13/viper"
	"net/http"
	"solidarity-service/handler"
	"solidarity-service/internal"
	"solidarity-service/storage"
	"solidarity-service/utils"
	"strings"
)

// InteractAPIService is a service that implements the logic for the InteractAPIServicer
// This service should implement the business logic for every endpoint for the InteractAPI API.
// Include any external packages or services that will be required by this service.
type InteractAPIService struct {
	config  *viper.Viper
	handler *handler.SolidarityServiceHandler
	storage storage.Storage
}

// NewInteractAPIService creates a default api service
func NewInteractAPIService(config *viper.Viper, handler *handler.SolidarityServiceHandler, storage storage.Storage) InteractAPIServicer {
	return &InteractAPIService{
		config:  config,
		handler: handler,
		storage: storage,
	}
}

// GetBookings - Retrieves an existing Booking request.
func (s *InteractAPIService) GetBookings(ctx context.Context, operator string, bookingId string) (ImplResponse, error) {
	booking, err := s.storage.GetBooking(bookingId)
	if err != nil {
		if strings.Contains(err.Error(), " no rows in result set") {
			return Response(http.StatusBadRequest, "no related data recheck your parameters"), nil
		} else {
			return Response(http.StatusInternalServerError, nil), errors.New("DriverPunctualAvailabilitiesPost internal server error")
		}
	}
	response := Booking{
		Id: booking.ID,
		Driver: User{
			Id:               booking.Driver.ID,
			Operator:         booking.Driver.Operator,
			Alias:            booking.Driver.Alias,
			FirstName:        booking.Driver.FirstName,
			LastName:         booking.Driver.LastName,
			Grade:            int32(booking.Driver.Grade),
			Picture:          booking.Driver.Picture,
			Gender:           booking.Driver.Gender,
			VerifiedIdentity: booking.Driver.VerifiedIdentity,
		},
		Passenger: User{
			Id:               booking.Passenger.ID,
			Operator:         booking.Passenger.Operator,
			Alias:            booking.Passenger.Alias,
			FirstName:        booking.Passenger.FirstName,
			LastName:         booking.Passenger.LastName,
			Grade:            int32(booking.Passenger.Grade),
			Picture:          booking.Passenger.Picture,
			Gender:           booking.Passenger.Gender,
			VerifiedIdentity: booking.Passenger.VerifiedIdentity,
		},
		PassengerPickupDate:    int32(booking.Pickup_date),
		PassengerPickupLat:     booking.PassengerPickupAddress.Point().X(),
		PassengerPickupLng:     booking.PassengerPickupAddress.Point().Y(),
		PassengerPickupAddress: booking.PassengerPickupAddress.Properties.MustString("name"),
		PassengerDropLat:       booking.PassengerDropAddress.Point().X(),
		PassengerDropLng:       booking.PassengerDropAddress.Point().Y(),
		PassengerDropAddress:   booking.PassengerDropAddress.Properties.MustString("name"),
		Status:                 BookingStatus(booking.Status),
		Duration:               int32(booking.Duration),
		Distance:               int32(booking.Distance),
		Price: Price{
			Type: "FREE",
		},
	}
	return Response(200, response), nil
}

// PatchBookings - Updates status of an existing Booking request.
func (s *InteractAPIService) PatchBookings(ctx context.Context, operator string, bookingId string, status BookingStatus, message string) (ImplResponse, error) {
	err := s.storage.UpdateBookingStatus(bookingId, internal.BookingStatus(status))
	fmt.Println(err)
	if err != nil {
		if strings.Contains(err.Error(), " no rows in result set") {
			return Response(http.StatusBadRequest, "no related data recheck your params"), nil
		}
		return Response(http.StatusInternalServerError, nil), nil
	}
	return Response(200, "Successful operation"), nil
}

// PostBookings - Create a punctual outward Booking request.
func (s *InteractAPIService) PostBookings(ctx context.Context, bookingRequest BookingRequest) (ImplResponse, error) {
	booking := internal.BookingRequest{}
	booking.ID = bookingRequest.Id
	booking.Status = internal.BookingStatus(bookingRequest.Status)
	booking.Driver_id = bookingRequest.DriverId
	booking.Passenger_id = bookingRequest.PassengerId
	booking.Operator = bookingRequest.Operator
	booking.Departure_address = &geojson.Feature{
		Type:     "Feature",
		Geometry: orb.Geometry(orb.Point{bookingRequest.DepartureAddressLat, bookingRequest.DepartureAddressLong}),
		Properties: geojson.Properties{
			"name": bookingRequest.DepartureAddress,
		},
	}
	booking.Destination_address = &geojson.Feature{
		Type:     "Feature",
		Geometry: orb.Geometry(orb.Point{bookingRequest.DestinationAddressLat, bookingRequest.DestinationAddressLong}),
		Properties: geojson.Properties{
			"name": bookingRequest.DestinationAddress,
		},
	}
	booking.Pickup_date = int64(bookingRequest.Pickup_date)
	err := s.storage.CreateBooking(booking)
	if err != nil {
		if strings.Contains(err.Error(), utils.SQL_DUPLICATE) {
			return Response(http.StatusBadRequest, "ID already in use"), nil
		}
		return Response(http.StatusInternalServerError, nil), errors.New("PostBookings internal server error")
	}
	passenger, err := s.storage.GetPassenger(booking.Passenger_id)
	if err != nil {
		return Response(http.StatusInternalServerError, nil), errors.New("PostBookings internal server error")
	}
	driver, err := s.storage.GetDriver(booking.Driver_id)
	if err != nil {
		return Response(http.StatusInternalServerError, nil), errors.New("PostBookings internal server error")
	}
	duration, err := s.handler.CalculateDurationBetweenFeatures(booking.Departure_address, booking.Destination_address)
	if err != nil {
		duration = 0
	}
	response := Booking{
		Id: bookingRequest.Id,
		Driver: User{
			Id:               driver.Driver.ID,
			Operator:         driver.Driver.Operator,
			Alias:            driver.Driver.Alias,
			FirstName:        driver.Driver.FirstName,
			LastName:         driver.Driver.LastName,
			Grade:            int32(driver.Driver.Grade),
			Picture:          driver.Driver.Picture,
			Gender:           driver.Driver.Gender,
			VerifiedIdentity: driver.Driver.VerifiedIdentity,
		},
		Passenger: User{
			Id:               passenger.Passenger.ID,
			Operator:         passenger.Passenger.Operator,
			Alias:            passenger.Passenger.Alias,
			FirstName:        passenger.Passenger.FirstName,
			LastName:         passenger.Passenger.LastName,
			Grade:            int32(passenger.Passenger.Grade),
			Picture:          passenger.Passenger.Picture,
			Gender:           passenger.Passenger.Gender,
			VerifiedIdentity: passenger.Passenger.VerifiedIdentity,
		},
		PassengerPickupDate:    int32(booking.Pickup_date),
		PassengerPickupLat:     booking.Departure_address.Point().X(),
		PassengerPickupLng:     booking.Departure_address.Point().Y(),
		PassengerPickupAddress: booking.Departure_address.Properties.MustString("name"),
		PassengerDropLat:       booking.Destination_address.Point().X(),
		PassengerDropLng:       booking.Destination_address.Point().Y(),
		PassengerDropAddress:   booking.Destination_address.Properties.MustString("name"),
		Status:                 bookingRequest.Status,
		Duration:               int32(duration),
		Distance:               int32(s.handler.CalculateDistanceBetweenFeatures(booking.Departure_address, booking.Destination_address)),
		Car: Car{
			Model: driver.Car.Model,
			Brand: driver.Car.Brand,
		},
		Price: Price{
			Type: "FREE",
		},
	}
	return Response(201, response), nil
}

// PostConnections - Send a message to the owner of a retrieved journey.
func (s *InteractAPIService) PostConnections(ctx context.Context, postConnectionsRequest PostConnectionsRequest) (ImplResponse, error) {
	return Response(http.StatusNotImplemented, nil), errors.New("PostConnections method not implemented")
}
