package datamysql

import (
	"database/sql"
	"fmt"
	"time"

	"bitbucket.org/nemt/nemt-portal-api/domain/entity"
	"bitbucket.org/nemt/nemt-portal-api/infra/errors"
	"github.com/go-sql-driver/mysql"
	uuid "github.com/satori/go.uuid"
)

// rideRepo maps methods to database
type rideRepo struct {
	conn executor
}

func newRideRepo(conn executor) *rideRepo {
	return &rideRepo{
		conn: conn,
	}
}

func (c *rideRepo) getQuery() string {
	const query = `SELECT 
											a.ride_id,
											a.ride_uuid,
											b.user_id,
											b.user_uuid,
											b.name,
											b.subscriber_id,
											c.ride_status_id,
											c.ride_status,
											c.key ride_status_key,
											d.ride_type_id,
											d.value ride_type,
											d.key ride_type_key,
											a.notes,
											e.first_name,
											e.last_name,
											e.image_url,
											e.phone_number,
											e.internal_user_id,
											IFNULL(f.name, '') driver_name,
											IFNULL(f.phone_number, '') driver_phone_number,
											IFNULL(f.image_url, '') driver_image_url,
											IFNULL(f.rating, '') driver_rating,
											IFNULL(g.color, '') vehicle_color,
											IFNULL(g.image_url, '') vehicle_image_url,
											IFNULL(g.license_plate, '') vehicle_license_plate,
											IFNULL(g.license_plate_state, '') vehicle_license_state,
											IFNULL(g.make, '') vehicle_make,
											IFNULL(g.model, '') vehicle_model,
											IFNULL(g.year, 0) vehicle_year,
											h.origin_name,
											h.origin_address,
											h.origin_lat,
											h.origin_lng,
											h.destination_name,
											h.destination_address,
											h.destination_lat,
											h.destination_lng,
											IFNULL(h.route_kml, '') route_kml,
											IFNULL(h.distance, 0) distance,
											IFNULL(h.duration, 0) duration,
											IFNULL(h.eta, 0) eta,
											a.pickup_time,
											a.visit_date,
											a.visit_time,
											a.ride_internal_id,
											a.visit_id,
											i.visit_uuid,
											IFNULL(i.pickup_id, '') visit_pickup_id,
                                            IFNULL(i.pickup_address_id, 0) pickup_address_id,
                                            IFNULL(i.address_id, 0) address_id,
                                            IFNULL(i.provider_id, 0) provider_id,
                                            IFNULL(o.provider_uuid, '') provider_uuid, 
                                            IFNULL(o.provider_internal_id, '') provider_internal_id, 
                                            IFNULL(o.provider_muk_id, '') provider_muk_id, 
                                            IFNULL(o.provider_name, '') provider_name, 
											-- i.pickup_name visit_pickup_name,
											-- i.pickup_address visit_pickup_address,
											-- i.pickup_lat visit_pickup_lat,
											-- i.pickup_lng visit_pickup_lng,
											-- IFNULL(i.destination_id, '') visit_destination_id,
											-- i.destination_name visit_destination_name,
											-- i.destination_address visit_destination_address,
											-- i.destination_lat visit_destination_lat,
											-- i.destination_lng visit_destination_lng,
											IFNULL(i.visit_external_id, '') visit_external_id,
											i.created_user_id,
											j.user_uuid created_user_uuid,
											j.name created_name,
											IFNULL(k.phone_number, '') created_user_phone_number,
											IFNULL(k.email, '') created_user_email,
											IFNULL(l.phone_number, '') user_phone_number,
											IFNULL(l.email, '') user_email,
											a.create_date,
											a.update_date,
											m.trip_type_id,
											m.trip_type_key,
											m.trip_type,
											n.trip_type_id visit_trip_type_id,
											n.trip_type_key visit_trip_type_key,
											n.trip_type visit_trip_type_key,
											i.return_date 
				FROM          tab_ride a 
			INNER JOIN     tab_user b
					ON          a.user_id = b.user_id
			INNER JOIN     tab_ride_status c
					ON          c.ride_status_id = a.ride_status_id
			INNER JOIN     tab_ride_type d
					ON          d.ride_type_id = a.ride_type_id
			INNER JOIN     tab_ride_passenger e
					ON          e.ride_id = a.ride_id
			LEFT JOIN     tab_ride_driver f
					ON          f.ride_id = a.ride_id
			LEFT JOIN     tab_ride_vehicle g
					ON          g.ride_id = a.ride_id
			INNER JOIN     tab_ride_route h
					ON          h.ride_id = a.ride_id
			INNER JOIN      tab_visit i
							ON          i.visit_id = a.visit_id
			INNER JOIN      tab_user j
							ON          j.user_id = i.created_user_id
			INNER JOIN      tab_login k
							ON          k.user_id = j.user_id
			INNER JOIN      tab_login l
							ON          l.user_id = a.user_id 
			INNER JOIN      tab_trip_type m
							ON          m.trip_type_id = a.trip_type_id
			INNER JOIN      tab_trip_type n
							ON          n.trip_type_id = i.trip_type_id 
            INNER JOIN      tab_provider o
                            ON          i.provider_id = o.provider_id `

	return query
}

func (c *rideRepo) getProfileQuery(user entity.User) (query string, where string, err error) {
	if len(user.Profiles) > 0 {
		for _, p := range user.Profiles {
			switch p.Key {
			case "AD", "BCBSIAD", "BDCAD", "PLANAD":
				return
			case "SP", "SPT", "VIRPT":
				switch p.Organization.Type.Key {
				case "techsupport", "bcbsi", "bcbsa", "plan":
					return
				case "provider":
					query = `INNER JOIN      viw_visit_provider z  ON          a.ride_id       		 = z.ride_id`
					where = fmt.Sprintf(`	AND (z.organization_uuid = '%s' OR z.parent_organization_uuid = '%s') `, p.Organization.UUID, p.Organization.UUID)
					return
				}
			}
		}

		if query == "" && where == "" {
			return "", "", fmt.Errorf("Invalid Query")
		} else {
			return
		}
	} else {
		return "", "", fmt.Errorf("User has no profile to search")
	}
}

func (c *rideRepo) Update(hook entity.WebhookResponse, user entity.User) (retVal entity.Ride, err error) {
	oldRide, err := c.GetByUUID(hook.Ride.UUID, user)
	if err != nil {
		return retVal, err
	}

	retVal, err = c.updateHeader(hook.Ride)
	if err != nil {
		return retVal, err
	}

	retVal, err = c.updateLocation(hook.Ride)
	if err != nil {
		return retVal, err
	}

	// retVal, err = c.updatePassenger(hook.Ride)
	// if err != nil {
	// 	return retVal, err
	// }

	if oldRide.Driver.Name != "" {
		retVal, err = c.updateDriver(hook.Ride)
	} else {
		retVal, err = c.saveDriver(hook.Ride)
	}
	if err != nil {
		return retVal, err
	}

	if oldRide.Vehicle.LicensePlate != "" {
		retVal, err = c.updateVehicle(hook.Ride)
	} else {
		retVal, err = c.saveVehicle(hook.Ride)
	}
	if err != nil {
		return retVal, err
	}

	go func() {
		err := c.saveHistory(hook)
		if err != nil {
			fmt.Println("Error to save history: " + err.Error())
		}
	}()

	return retVal, nil
}

func (c *rideRepo) updateVehicle(ride entity.Ride) (retVal entity.Ride, err error) {
	const (
		query = `UPDATE tab_ride_vehicle
							SET
									color = ?,
									image_url = ?,
									license_plate = ?,
									license_plate_state = ?,
									make = ?,
									model = ?,
									year = ?
							WHERE ride_id = ?;`
	)

	retVal = ride
	_, err = c.conn.Exec(query, ride.Vehicle.Color, ride.Vehicle.ImageURL, ride.Vehicle.LicensePlate, ride.Vehicle.LicensePlateState, ride.Vehicle.Make, ride.Vehicle.Model, ride.Vehicle.Year, ride.ID)
	if err != nil {
		return retVal, err
	}

	return retVal, nil
}

func (c *rideRepo) updateDriver(ride entity.Ride) (retVal entity.Ride, err error) {
	const (
		query = `UPDATE tab_ride_driver
							SET
									name = ?,
									image_url = ?,
									phone_number = ?,
									rating = ?
							WHERE ride_id = ?;`
	)

	retVal = ride
	_, err = c.conn.Exec(query, ride.Driver.Name, ride.Driver.ImageURL, ride.Driver.PhoneNumber, ride.Driver.Rating, ride.ID)
	if err != nil {
		return retVal, err
	}

	return retVal, nil
}

func (c *rideRepo) updatePassenger(ride entity.Ride) (retVal entity.Ride, err error) {
	const (
		query = `UPDATE tab_ride_passenger
								SET
										first_name = ?,
										last_name = ?,
										image_url = ?,
										phone_number = ?,
										internal_user_id = ?
								WHERE ride_id = ?;`
	)

	retVal = ride
	_, err = c.conn.Exec(query, ride.Passenger.FirstName, ride.Passenger.LastName, ride.Passenger.ImageURL, ride.Passenger.PhoneNumber, ride.Passenger.InternalID, ride.ID)
	if err != nil {
		return retVal, err
	}

	return retVal, nil
}

func (c *rideRepo) updateLocation(ride entity.Ride) (retVal entity.Ride, err error) {
	query := `UPDATE tab_ride_route
							SET
									origin_address = ?,
									origin_lat = ?,
									origin_lng = ?,
									destination_address = ?,
									destination_lat = ?,
									destination_lng = ?,
									route_kml = ?,
									distance = ?,
									duration = ?,
									eta = ?`
	where := ` WHERE ride_id = ?;`

	retVal = ride
	params := make([]interface{}, 0)
	params = append(params, ride.Route.Origin.Address, ride.Route.Origin.Latitude, ride.Route.Origin.Longitude, ride.Route.Destination.Address, ride.Route.Destination.Latitude, ride.Route.Destination.Longitude, ride.Route.RouteKML, ride.Route.Distance, ride.Route.Duration, ride.Route.ETA)

	if retVal.Route.Origin.Name != "" {
		query += " , origin_name = ? "
		params = append(params, ride.Route.Origin.Name)
	}

	if retVal.Route.Destination.Name != "" {
		query += " , destination_name = ? "
		params = append(params, ride.Route.Destination.Name)
	}
	params = append(params, ride.ID)

	_, err = c.conn.Exec(query+where, params...)
	if err != nil {
		return retVal, err
	}

	return retVal, nil
}

func (c *rideRepo) updateHeader(ride entity.Ride) (retVal entity.Ride, err error) {
	const (
		statusQuery = "SELECT ride_status_id FROM tab_ride_status WHERE `key` = ?"
		query       = `UPDATE tab_ride
						SET
							ride_status_id = ?,
							ride_type_id = ?,
							ride_internal_id = ?,
							request_date = ?,
							request_ms = ?,
							generate_date = ?,
							generate_ms = ?,
							route_kml = ?,
							notes = ?,
							primetime_percentage = ?,
							pickup_time = ?,
							visit_date = ?,
							visit_time = ?,
							visit_id = ?
						WHERE ride_uuid = ?;`
	)

	row := c.conn.QueryRow(statusQuery, ride.Status.Key)
	err = row.Scan(&ride.Status.ID)
	if err != nil {
		fmt.Println("Error to get status")
		return retVal, err
	}

	retVal = ride
	_, err = c.conn.Exec(query, ride.Status.ID, ride.Type.ID, ride.InternalID, ride.RequestDate, ride.RequestMiliseconds, ride.GenerateDate, ride.GenerateMiliseconds, ride.Route.RouteKML, ride.Note, ride.PrimetimePercentage, ride.PickupTime, ride.VisitDate, ride.VisitTime, ride.Visit.ID, ride.UUID)
	if err != nil {
		return retVal, err
	}

	return ride, nil
}

func (c *rideRepo) Save(ride entity.Ride) (retVal entity.Ride, err error) {
	retVal, err = c.saveHeader(ride)
	if err != nil {
		fmt.Println("Error to save header")
		return retVal, err
	}

	retVal, err = c.saveLocation(retVal)
	if err != nil {
		fmt.Println("Error to save location")
		return retVal, err
	}

	retVal, err = c.savePassenger(retVal)
	if err != nil {
		fmt.Println("Error to save passenger")
		return retVal, err
	}

	retVal, err = c.saveDriver(retVal)
	if err != nil {
		fmt.Println("Error to save driver")
		return retVal, err
	}

	retVal, err = c.saveVehicle(retVal)
	if err != nil {
		fmt.Println("Error to save vehicle")
		return retVal, err
	}

	go func() {
		hook := entity.WebhookResponse{
			Ride: retVal,
		}
		err = c.saveHistory(hook)
		if err != nil {
			fmt.Println("Error to save history: ", err.Error())
		}
	}()

	return retVal, err
}

func (c *rideRepo) saveHistory(hook entity.WebhookResponse) error {
	rideHistoryID, err := c.saveHistoryHeader(hook)
	if err != nil {
		fmt.Println("Error to save header history")
		return err
	}

	if err = c.saveLocationHistory(rideHistoryID, hook.Ride.Route); err != nil {
		fmt.Println("Error to save location history")
		return err
	}

	if err = c.savePassengerHistory(rideHistoryID, hook.Ride.Passenger); err != nil {
		fmt.Println("Error to save passenger history")
		return err
	}

	if err = c.saveDriverHistory(rideHistoryID, hook.Ride.Driver); err != nil {
		fmt.Println("Error to save driver history")
		return err
	}

	if err = c.saveVehicleHistory(rideHistoryID, hook.Ride.Vehicle); err != nil {
		fmt.Println("Error to save vehicle history")
		return err
	}

	return nil
}

func (c *rideRepo) saveVehicleHistory(rideHistoryID int64, vehicle entity.RideVehicle) error {
	const (
		query = "INSERT INTO tab_ride_vehicle_history(ride_history_id,color,image_url,license_plate,license_plate_state,make,model,`year`) VALUES(?,?,?,?,?,?,?,?);"
	)

	if vehicle.Make != "" && vehicle.Model != "" && vehicle.Color != "" && vehicle.Year > 0 {
		_, err := c.conn.Exec(query, rideHistoryID, vehicle.Color, vehicle.ImageURL, vehicle.LicensePlate, vehicle.LicensePlateState, vehicle.Make, vehicle.Model, vehicle.Year)
		if err != nil {
			return err
		}
	}

	return nil
}

func (c *rideRepo) saveDriverHistory(rideHistoryID int64, driver entity.RideDriver) error {
	const (
		query = "INSERT INTO tab_ride_driver_history(ride_history_id,name,image_url,phone_number,rating) VALUES(?,?,?,?,?);"
	)

	if driver.Name != "" && driver.PhoneNumber != "" {
		_, err := c.conn.Exec(query, rideHistoryID, driver.Name, driver.ImageURL, driver.PhoneNumber, driver.Rating)
		if err != nil {
			return err
		}
	}

	return nil
}

func (c *rideRepo) savePassengerHistory(rideHistoryID int64, passenger entity.RidePassenger) error {
	const (
		query = "INSERT INTO tab_ride_passenger_history(ride_history_id,first_name,last_name,image_url,phone_number,internal_user_id) VALUES(?,?,?,?,?,?)"
	)

	_, err := c.conn.Exec(query, rideHistoryID, passenger.FirstName, passenger.LastName, "", passenger.PhoneNumber, passenger.InternalID)
	if err != nil {
		return err
	}

	return nil
}

func (c *rideRepo) saveLocationHistory(rideHistoryID int64, route entity.RideRoute) error {
	const (
		query = `INSERT INTO tab_ride_route_history(ride_history_id,origin_name,origin_address,origin_lat,origin_lng,destination_name,destination_address,destination_lat,destination_lng,route_kml,distance,duration,eta)
							VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?);`
	)

	if route.Origin.Latitude != 0 && route.Origin.Longitude != 0 && route.Destination.Latitude != 0 && route.Destination.Longitude != 0 {
		_, err := c.conn.Exec(query, rideHistoryID, route.Origin.Name, route.Origin.Address, route.Origin.Latitude, route.Origin.Longitude, route.Destination.Name, route.Destination.Address, route.Destination.Latitude, route.Destination.Longitude, route.RouteKML, route.Distance, route.Duration, route.ETA)
		if err != nil {
			return err
		}
	}

	return nil
}

func (c *rideRepo) saveHistoryHeader(hook entity.WebhookResponse) (int64, error) {
	const (
		query = `INSERT INTO tab_ride_history(ride_history_uuid,ride_id,event_id,href,occurred_at,event_type,ride_internal_id,ride_status_id,ride_type_id,request_date,request_ms,generate_date,generate_ms,route_kml,notes, primetime_percentage, pickup_time, visit_date, visit_time, visit_id)
							VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);`
	)

	UUID, _ := uuid.NewV4()
	sUUID := UUID.String()

	if hook.OccurredAt.IsZero() {
		hook.OccurredAt = time.Now()
	}

	results, err := c.conn.Exec(query, sUUID, hook.Ride.ID, hook.EventID, hook.HREF, hook.OccurredAt, hook.EventType, hook.Ride.InternalID, hook.Ride.Status.ID, hook.Ride.Type.ID, hook.Ride.RequestDate, hook.Ride.RequestMiliseconds, hook.Ride.GenerateDate, hook.Ride.GenerateMiliseconds, hook.Ride.Route.RouteKML, hook.Ride.Note, hook.Ride.PrimetimePercentage, hook.Ride.PickupTime, hook.Ride.VisitDate, hook.Ride.VisitTime, hook.Ride.Visit.ID)
	if err != nil {
		return 0, err
	}

	return results.LastInsertId()
}

func (c *rideRepo) saveHeader(ride entity.Ride) (retVal entity.Ride, err error) {
	const (
		dataQuery = `SELECT 
											a.user_id user_id,
											a.name,
											a.subscriber_id,
											b.ride_status_id,
											b.ride_status,
											c.ride_type_id,
											c.value ride_type,
											d.user_id created_user_id,
											d.name created_name,
											IFNULL(d.subscriber_id, '') created_member,
											e.trip_type_id,
											e.trip_type_key,
											e.trip_type
									FROM tab_user a,
											tab_ride_status b,
											tab_ride_type c,
											tab_user d,
											tab_trip_type e
									WHERE
											a.user_uuid = ?
									AND b.key = ?
									AND c.key = ?
									AND d.user_uuid = ?
									AND e.trip_type_key = ?;`

		query = `INSERT INTO tab_ride(ride_uuid, user_id, ride_status_id, ride_type_id, ride_internal_id, request_date, request_ms, generate_date, generate_ms, notes, primetime_percentage, pickup_time, visit_date, visit_time, visit_id, created_user_id, trip_type_id)
							VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`
	)

	retVal = ride
	UUID, _ := uuid.NewV4()
	retVal.UUID = UUID.String()

	if retVal.Status.Key == "" {
		retVal.Status.Key = "accepted"
	}

	if retVal.Type.Key == "" {
		retVal.Type.Key = "lyft"
	}

	row := c.conn.QueryRow(dataQuery, retVal.User.UUID, retVal.Status.Key, retVal.Type.Key, retVal.CreatedUser.UUID, retVal.TripType.Key)
	if err = row.Scan(&retVal.User.ID, &retVal.User.Name, &retVal.User.Member, &retVal.Status.ID, &retVal.Status.Value, &retVal.Type.ID, &retVal.Type.Value, &retVal.CreatedUser.ID, &retVal.CreatedUser.Name, &retVal.CreatedUser.Member, &retVal.TripType.ID, &retVal.TripType.Key, &retVal.TripType.Value); err != nil {
		fmt.Println("Error to get base data: ", err.Error())
		return retVal, err
	}

	results, err := c.conn.Exec(query, retVal.UUID, retVal.User.ID, retVal.Status.ID, retVal.Type.ID, retVal.InternalID, retVal.RequestDate, retVal.RequestMiliseconds, retVal.GenerateDate, retVal.GenerateMiliseconds, retVal.Note, retVal.PrimetimePercentage, retVal.PickupTime, retVal.VisitDate, retVal.VisitTime, retVal.Visit.ID, retVal.CreatedUser.ID, retVal.TripType.ID)
	if err != nil {
		fmt.Println("Error: ", err.Error())
		return retVal, err
	}

	var ID int64
	ID, err = results.LastInsertId()
	if err != nil {
		fmt.Println("Error Getting the ID: ", err.Error())
		return retVal, err
	}

	retVal.ID = ID
	return retVal, nil
}

func (c *rideRepo) saveLocation(ride entity.Ride) (retVal entity.Ride, err error) {
	const (
		query = `INSERT INTO tab_ride_route(ride_id,origin_name,origin_address,origin_lat,origin_lng,destination_name,destination_address,destination_lat,destination_lng,route_kml,distance,duration,eta)
							VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?);`
	)

	retVal = ride
	results, err := c.conn.Exec(query, retVal.ID, retVal.Route.Origin.Name, retVal.Route.Origin.Address, retVal.Route.Origin.Latitude, retVal.Route.Origin.Longitude, retVal.Route.Destination.Name, retVal.Route.Destination.Address, retVal.Route.Destination.Latitude, retVal.Route.Destination.Longitude, retVal.Route.RouteKML, retVal.Route.Distance, retVal.Route.Duration, retVal.Route.ETA)
	if err != nil {
		return retVal, err
	}

	var ID int64
	ID, err = results.LastInsertId()
	if err != nil {
		fmt.Println("Error Getting the ID: ", err.Error())
		return retVal, err
	}

	retVal.Route.ID = ID
	return retVal, nil
}

func (c *rideRepo) savePassenger(ride entity.Ride) (retVal entity.Ride, err error) {
	const (
		query = `INSERT INTO tab_ride_passenger(ride_id,first_name,last_name,image_url,phone_number,internal_user_id)
							VALUES(?,?,?,?,?,?);`
	)

	retVal = ride
	results, err := c.conn.Exec(query, retVal.ID, ride.Passenger.FirstName, ride.Passenger.LastName, ride.Passenger.ImageURL, ride.Passenger.PhoneNumber, ride.Passenger.InternalID)
	if err != nil {
		return retVal, err
	}

	var ID int64
	ID, err = results.LastInsertId()
	if err != nil {
		fmt.Println("Error Getting the ID: ", err.Error())
		return retVal, err
	}

	retVal.Passenger.ID = ID
	return retVal, nil
}

func (c *rideRepo) saveDriver(ride entity.Ride) (retVal entity.Ride, err error) {
	const (
		query = "INSERT INTO tab_ride_driver(ride_id,`name`,image_url,phone_number,rating) VALUES(?,?,?,?,?);"
	)

	retVal = ride
	if retVal.Driver.Name != "" && retVal.Driver.PhoneNumber != "" {
		results, err := c.conn.Exec(query, retVal.ID, ride.Driver.Name, ride.Driver.ImageURL, ride.Driver.PhoneNumber, ride.Driver.Rating)
		if err != nil {
			return retVal, err
		}

		var ID int64
		ID, err = results.LastInsertId()
		if err != nil {
			fmt.Println("Error Getting the ID: ", err.Error())
			return retVal, err
		}
		retVal.Driver.ID = ID
	}
	return retVal, nil
}

func (c *rideRepo) saveVehicle(ride entity.Ride) (retVal entity.Ride, err error) {
	const (
		query = "INSERT INTO tab_ride_vehicle(ride_id,color,image_url,license_plate,license_plate_state,make,model,`year`) VALUES(?,?,?,?,?,?,?,?);"
	)

	retVal = ride
	if retVal.Vehicle.Color != "" && retVal.Vehicle.LicensePlate != "" && retVal.Vehicle.Make != "" && retVal.Vehicle.Model != "" && retVal.Vehicle.Year > 0 {
		results, err := c.conn.Exec(query, retVal.ID, ride.Vehicle.Color, ride.Vehicle.ImageURL, ride.Vehicle.LicensePlate, ride.Vehicle.LicensePlateState, ride.Vehicle.Make, ride.Vehicle.Model, ride.Vehicle.Year)
		if err != nil {
			return retVal, err
		}

		var ID int64
		ID, err = results.LastInsertId()
		if err != nil {
			fmt.Println("Error Getting the ID: ", err.Error())
			return retVal, err
		}

		retVal.Vehicle.ID = ID
	}
	return retVal, nil
}

func (c *rideRepo) GetByUUID(uuid string, user entity.User) (entity.Ride, error) {
	query, where, err := c.getProfileQuery(user)
	if err != nil {
		return entity.Ride{}, err
	}

	query = c.getQuery() + query + " WHERE a.ride_uuid = ? " + where

	return c.parseEntity(c.conn.QueryRow(query, uuid))
}

func (c *rideRepo) GetByID(id int64, user entity.User) (entity.Ride, error) {
	query, where, err := c.getProfileQuery(user)
	if err != nil {
		return entity.Ride{}, err
	}

	query = c.getQuery() + query + " WHERE a.ride_id = ? " + where
	return c.parseEntity(c.conn.QueryRow(query, id))
}

func (c *rideRepo) GetByUUIDAndUserUUID(UUID string, userUUID string) (entity.Ride, error) {
	return c.parseEntity(c.conn.QueryRow(c.getQuery()+" WHERE a.ride_uuid = ? AND b.user_uuid = ?", UUID, userUUID))
}

func (c *rideRepo) GetLastRideByPhoneNumber(phoneNumber string) (entity.Ride, error) {
	query := c.getQuery() + ` WHERE       c.ride_status IN ('accepted', 'arrived', 'scheduled', 'pending')
															AND       l.phone_number = ?
														ORDER BY    a.create_date DESC
														LIMIT 1;`

	return c.parseEntity(c.conn.QueryRow(query, phoneNumber))
}

func (c *rideRepo) GetLastRideByDriversNumber(phoneNumber string) (entity.Ride, error) {
	query := c.getQuery() + ` WHERE       c.ride_status IN ('accepted', 'arrived', 'scheduled', 'pending')
															AND       IFNULL(f.phone_number, '') = ?
														ORDER BY    a.create_date DESC
														LIMIT 1;`

	return c.parseEntity(c.conn.QueryRow(query, phoneNumber))
}

func (c *rideRepo) GetByInternalID(internalID string) (entity.Ride, error) {
	return c.parseEntity(c.conn.QueryRow(c.getQuery()+" WHERE a.ride_internal_id = ?", internalID))
}

func (c *rideRepo) GetByInternalPassengerID(internalPassengerID string) (entity.Ride, error) {
	return c.parseEntity(c.conn.QueryRow(c.getQuery()+" WHERE e.internal_user_id = ?", internalPassengerID))
}

func (c *rideRepo) GetByUserID(userID int64, user entity.User) ([]entity.Ride, error) {
	query, where, err := c.getProfileQuery(user)
	if err != nil {
		return nil, err
	}

	query = c.getQuery() + query + " WHERE b.user_id = ? " + where

	return c.parseSet(c.conn.Query(query, userID))
}

func (c *rideRepo) GetByUserUUID(userUUID string, user entity.User) ([]entity.Ride, error) {
	query, where, err := c.getProfileQuery(user)
	if err != nil {
		return nil, err
	}

	query = c.getQuery() + query + " WHERE b.user_uuid = ? " + where

	return c.parseSet(c.conn.Query(query, userUUID))
}

func (c *rideRepo) GetByVisitUUID(visitUUID string, user entity.User) ([]entity.Ride, error) {
	query, where, err := c.getProfileQuery(user)
	if err != nil {
		return nil, err
	}

	query = c.getQuery() + query + " WHERE b.user_id = ? " + where

	return c.parseSet(c.conn.Query(query, visitUUID))
}

func (c *rideRepo) GetByVisitUUIDAndTripType(visitUUID string, tripTypeKey string, user entity.User) (entity.Ride, error) {
	query, where, err := c.getProfileQuery(user)
	if err != nil {
		return entity.Ride{}, err
	}

	query = c.getQuery() + query + " WHERE i.visit_uuid = ? AND m.trip_type_key = ? " + where
	return c.parseEntity(c.conn.QueryRow(query, visitUUID, tripTypeKey))
}

func (c *rideRepo) parseSet(rows *sql.Rows, err error) ([]entity.Ride, error) {
	if err != nil {
		return nil, errors.Wrap(err)
	}
	result := make([]entity.Ride, 0)
	for rows.Next() {
		entity, err := c.parseEntity(rows)
		if err != nil {
			return nil, errors.Wrap(err)
		}
		result = append(result, entity)
	}
	return result, nil
}

func (c *rideRepo) parseEntity(row scanner) (r entity.Ride, err error) {
	var returnDate mysql.NullTime

	err = row.Scan(&r.ID, &r.UUID, &r.User.ID, &r.User.UUID, &r.User.Name, &r.User.Member, &r.Status.ID, &r.Status.Value, &r.Status.Key, &r.Type.ID, &r.Type.Value, &r.Type.Key, &r.Note, &r.Passenger.FirstName, &r.Passenger.LastName, &r.Passenger.ImageURL, &r.Passenger.PhoneNumber, &r.Passenger.InternalID, &r.Driver.Name, &r.Driver.PhoneNumber, &r.Driver.ImageURL, &r.Driver.Rating, &r.Vehicle.Color, &r.Vehicle.ImageURL, &r.Vehicle.LicensePlate, &r.Vehicle.LicensePlateState, &r.Vehicle.Make, &r.Vehicle.Model, &r.Vehicle.Year, &r.Route.Origin.Name, &r.Route.Origin.Address, &r.Route.Origin.Latitude, &r.Route.Origin.Longitude, &r.Route.Destination.Name, &r.Route.Destination.Address, &r.Route.Destination.Latitude, &r.Route.Destination.Longitude, &r.Route.RouteKML, &r.Route.Distance, &r.Route.Duration, &r.Route.ETA, &r.PickupTime, &r.VisitDate, &r.VisitTime, &r.InternalID, &r.Visit.ID, &r.Visit.UUID, &r.Visit.Pickup.ID, &r.Visit.PickupAddressID, &r.Visit.DestinationAddressID, &r.Visit.Provider.ProviderID, &r.Visit.Provider.ProviderUUID, &r.Visit.Provider.InternalID, &r.Visit.Provider.MukID, &r.Visit.Provider.Name, &r.Visit.ExternalID, &r.Visit.CreatedUser.ID, &r.Visit.CreatedUser.UUID, &r.Visit.CreatedUser.Name, &r.Visit.CreatedUser.PhoneNumber, &r.Visit.CreatedUser.Email, &r.User.PhoneNumber, &r.User.Email, &r.Created, &r.Updated, &r.TripType.ID, &r.TripType.Key, &r.TripType.Value, &r.Visit.TripType.ID, &r.Visit.TripType.Key, &r.Visit.TripType.Value, &returnDate)
	if err != nil {
		return r, errors.Wrap(err)
	}

	r.CreatedUser = r.Visit.CreatedUser
	if returnDate.Valid {
		r.Visit.ReturnDate = &returnDate.Time
	}

	return r, nil
}

func (c *rideRepo) UpdateStatus(rideUUID string, status string) error {
	const (
		statusQuery = "SELECT ride_status_id FROM tab_ride_status WHERE `key` = ?"
		query       = "UPDATE tab_ride SET ride_status_id = ? WHERE ride_uuid = ?"
	)

	var rideStatusID int64
	row := c.conn.QueryRow(statusQuery, status)
	err := row.Scan(&rideStatusID)
	if err != nil {
		return err
	}

	_, err = c.conn.Exec(query, rideStatusID, rideUUID)
	if err != nil {
		return err
	}

	return nil
}

func (c *rideRepo) GetAll(user entity.User) ([]entity.Ride, error) {
	query, where, err := c.getProfileQuery(user)
	if err != nil {
		return nil, err
	}

	query = c.getQuery() + query + " WHERE 1 = 1 AND b.active = 1 " + where

	return c.parseSet(c.conn.Query(query))
}
