package grpcserver

import (
	"context"
	"time"

	"git.coopgo.io/coopgo-platform/carpool-service/servers/grpc/proto"
	"github.com/paulmach/orb"
	"github.com/rs/zerolog/log"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/timestamppb"
)

func (s *CarpoolServiceServerImpl) DriverJourneys(ctx context.Context, req *proto.DriverJourneysRequest) (*proto.DriverJourneysResponse, error) {
	departure := orb.Point{req.DepartureLng, req.DepartureLat}
	arrival := orb.Point{req.ArrivalLng, req.ArrivalLat}

	log.Debug().
		Str("departure date", req.DepartureDate.String()).
		Any("departure", departure).
		Any("arrival", arrival).
		Msg("grpc server - DriverJourneys")

	td := 900 * time.Second
	if req.TimeDelta != nil {
		td = time.Duration(*req.TimeDelta) * time.Second
	}

	departureDate := req.DepartureDate.AsTime()

	minDate := departureDate.Add(-td)
	maxDate := departureDate.Add(td)

	log.Debug().
		Str("departure_date", departureDate.Format(time.RFC3339)).
		Str("mindate", minDate.Format(time.RFC3339)).
		Str("maxdate", maxDate.Format(time.RFC3339)).
		Int64("td", int64(td)).
		Msg("DriverJourneys show dates")

	journeys, err := s.Handler.GetDriverJourneys(departure, arrival, req.DepartureRadius, req.ArrivalRadius, minDate, maxDate, req.Count)
	if err != nil {
		log.Error().Err(err).Msg("error finding driver journeys")
		return nil, err
	}

	results := []*proto.CarpoolServiceDriverJourney{}

	for _, j := range journeys {
		properties := j.Route.ExtraMembers["properties"].(map[string]any)
		usermap := properties["user"].(map[string]any)
		journeyId := j.ID
		driverDepartureLat := j.Route.Features[0].Point().Lat()
		driverDepartureLng := j.Route.Features[0].Point().Lon()
		driverArrivalLat := j.Route.Features[1].Point().Lat()
		driverArrivalLng := j.Route.Features[1].Point().Lon()
		var driverDepartureAddress *string
		if dda := j.Route.Features[0].Properties.MustString("label", ""); dda != "" {
			driverDepartureAddress = &dda
		}
		var driverArrivalAddress *string
		if daa := j.Route.Features[1].Properties.MustString("label", ""); daa != "" {
			driverArrivalAddress = &daa
		}
		duration := time.Duration(0)
		var distance *int64
		if len(j.Itinerary.Legs) > 2 {
			duration = j.Itinerary.Legs[1].Duration
			dist := int64(j.Itinerary.Legs[1].Distance)
			distance = &dist
		}

		results = append(results, &proto.CarpoolServiceDriverJourney{
			Id: journeyId,
			Driver: &proto.CarpoolServiceUser{
				Id:       usermap["id"].(string),
				Operator: usermap["operator"].(string),
				Alias:    usermap["alias"].(string),
			},
			Operator:               s.Handler.InternalOperatorID,
			PassengerPickupLat:     req.DepartureLat,
			PassengerPickupLng:     req.DepartureLng,
			PassengerDropLat:       req.ArrivalLat,
			PassengerDropLng:       req.ArrivalLng,
			DriverDepartureLat:     &driverDepartureLat,
			DriverDepartureLng:     &driverDepartureLng,
			DriverArrivalLat:       &driverArrivalLat,
			DriverArrivalLng:       &driverArrivalLng,
			DriverDepartureAddress: driverDepartureAddress,
			DriverArrivalAddress:   driverArrivalAddress,
			Duration:               int64(duration.Seconds()),
			Distance:               distance,
			PassengerPickupDate:    timestamppb.New(j.DepartureDate.Add(j.Itinerary.Legs[0].Duration)),
			DriverDepartureDate:    timestamppb.New(j.DepartureDate),
			Type:                   proto.CarpoolServiceJourneyType_PLANNED,
			JourneyPolyline:        &j.Itinerary.Summary.Polyline,
		})
	}

	return &proto.DriverJourneysResponse{
		DriverJourneys: results,
	}, nil
}

func (s *CarpoolServiceServerImpl) PassengerJourneys(ctx context.Context, req *proto.PassengerJourneysRequest) (*proto.PassengerJourneysResponse, error) {
	log.Debug().
		Str("departure date", req.DepartureDate.String()).
		Msg("grpc server - PassengerJourneys")

	departure := orb.Point{req.DepartureLng, req.DepartureLat}
	arrival := orb.Point{req.ArrivalLng, req.ArrivalLat}

	td := 900 * time.Second
	if req.TimeDelta != nil {
		td = time.Duration(*req.TimeDelta) * time.Second
	}

	minDate := req.DepartureDate.AsTime().Add(-td)
	maxDate := req.DepartureDate.AsTime().Add(td)

	journeys, err := s.Handler.GetPassengerJourneys(departure, arrival, req.DepartureRadius, req.ArrivalRadius, minDate, maxDate, req.Count)
	if err != nil {
		return nil, err
	}

	results := []*proto.CarpoolServicePassengerJourney{}

	for _, j := range journeys {
		properties := j.Route.ExtraMembers["properties"].(map[string]any)
		usermap := properties["user"].(map[string]any)
		journeyId := j.ID
		passengerDepartureLat := j.Route.Features[0].Point().Lat()
		passengerDepartureLng := j.Route.Features[0].Point().Lon()
		passengerArrivalLat := j.Route.Features[1].Point().Lat()
		passengerArrivalLng := j.Route.Features[1].Point().Lon()
		passengerDepartureDate := timestamppb.New(j.DepartureDate)
		var passengerPickupAddress *string
		if ppa := j.Route.Features[0].Properties.MustString("label", ""); ppa != "" {
			passengerPickupAddress = &ppa
		}
		var passengerDropAddress *string
		if pda := j.Route.Features[1].Properties.MustString("label", ""); pda != "" {
			passengerDropAddress = &pda
		}
		driverDepartureDate := timestamppb.New(j.DepartureDate.Add(-j.Itinerary.Legs[0].Duration))
		duration := time.Duration(0)
		var distance *int64
		if len(j.Itinerary.Legs) > 2 {
			duration = j.Itinerary.Legs[1].Duration
			dist := int64(j.Itinerary.Legs[1].Distance)
			distance = &dist
		}

		results = append(results, &proto.CarpoolServicePassengerJourney{
			Id: journeyId,
			Passenger: &proto.CarpoolServiceUser{
				Id:       usermap["id"].(string),
				Operator: usermap["operator"].(string),
				Alias:    usermap["alias"].(string),
			},
			Operator:               s.Handler.InternalOperatorID,
			PassengerPickupLat:     passengerDepartureLat,
			PassengerPickupLng:     passengerDepartureLng,
			PassengerDropLat:       passengerArrivalLat,
			PassengerDropLng:       passengerArrivalLng,
			PassengerPickupAddress: passengerPickupAddress,
			PassengerDropAddress:   passengerDropAddress,
			DriverDepartureLat:     &req.DepartureLat,
			DriverDepartureLng:     &req.DepartureLng,
			DriverArrivalLat:       &req.ArrivalLat,
			DriverArrivalLng:       &req.ArrivalLng,
			Duration:               int64(duration.Seconds()),
			Distance:               distance,
			PassengerPickupDate:    passengerDepartureDate,
			DriverDepartureDate:    driverDepartureDate,
			Type:                   proto.CarpoolServiceJourneyType_PLANNED,
			JourneyPolyline:        &j.Itinerary.Summary.Polyline,
		})
	}

	return &proto.PassengerJourneysResponse{
		PassengerJourneys: results,
	}, nil
}

func (s *CarpoolServiceServerImpl) DriverRegularTrips(context.Context, *proto.DriverRegularTripsRequest) (*proto.DriverRegularTripsResponse, error) {
	return nil, status.Errorf(codes.Unimplemented, "method DriverRegularTrips not implemented")
}

func (s *CarpoolServiceServerImpl) PassengerRegularTrips(context.Context, *proto.PassengerRegularTripsRequest) (*proto.PassengerRegularTripsResponse, error) {
	return nil, status.Errorf(codes.Unimplemented, "method PassengerRegularTrips not implemented")
}