package application

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"sort"
	"time"

	"git.coopgo.io/coopgo-apps/parcoursmob/utils/identification"
	"git.coopgo.io/coopgo-apps/parcoursmob/utils/sorting"
	filestorage "git.coopgo.io/coopgo-apps/parcoursmob/utils/storage"
	fleets "git.coopgo.io/coopgo-platform/fleets/grpcapi"
	fleetsstorage "git.coopgo.io/coopgo-platform/fleets/storage"
	groupsmanagement "git.coopgo.io/coopgo-platform/groups-management/grpcapi"
	"git.coopgo.io/coopgo-platform/groups-management/storage"
	mobilityaccounts "git.coopgo.io/coopgo-platform/mobility-accounts/grpcapi"
	mobilityaccountsstorage "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
	"github.com/coreos/go-oidc"
	"github.com/google/uuid"
	"github.com/gorilla/mux"
	"google.golang.org/protobuf/types/known/structpb"
	"google.golang.org/protobuf/types/known/timestamppb"
)

func (h *ApplicationHandler) VehiclesManagementOverview(w http.ResponseWriter, r *http.Request) {
	//Get Vehicles
	request := &fleets.GetVehiclesRequest{
		Namespaces: []string{"parcoursmob"},
	}
	resp, err := h.services.GRPC.Fleets.GetVehicles(context.TODO(), request)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
	}

	vehicles := []fleetsstorage.Vehicle{}
	bookings := []fleetsstorage.Booking{}
	vehicles_map := map[string]fleetsstorage.Vehicle{}

	for _, vehicle := range resp.Vehicles {
		if filterVehicle(r, vehicle) {
			v := vehicle.ToStorageType()
			vehicleBookings := []fleetsstorage.Booking{}
			for _, b := range v.Bookings {
				if b.Status() != fleetsstorage.StatusOld {
					if deleted, ok := b.Data["Deleted"].(bool); !ok && !deleted {
						bookings = append(bookings, b)
					}
				}
				if b.Unavailableto.After(time.Now()) {
					vehicleBookings = append(vehicleBookings, b)
				}
			}
			v.Bookings = vehicleBookings
			vehicles = append(vehicles, v)
			vehicles_map[v.ID] = v

		}
	}

	sort.Sort(sorting.VehiclesByLicencePlate(vehicles))
	sort.Sort(sorting.BookingsByStartdate(bookings))
	h.Renderer.VehiclesManagementOverview(w, r, vehicles, vehicles_map, bookings)
}

func (h *ApplicationHandler) VehiclesManagementBookingsList(w http.ResponseWriter, r *http.Request) {
	//Get Vehicles
	request := &fleets.GetVehiclesRequest{
		Namespaces: []string{"parcoursmob"},
	}
	resp, err := h.services.GRPC.Fleets.GetVehicles(context.TODO(), request)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
	}

	bookings := []fleetsstorage.Booking{}
	vehicles_map := map[string]fleetsstorage.Vehicle{}

	for _, vehicle := range resp.Vehicles {
		if filterVehicle(r, vehicle) {
			v := vehicle.ToStorageType()
			vehicles_map[v.ID] = v
			// bookings = append(bookings, v.Bookings...)
			for _, b := range v.Bookings {
				if v, ok := b.Data["administrator_unavailability"].(bool); !ok || !v {
					bookings = append(bookings, b)
				}
			}
		}
	}

	sort.Sort(sorting.BookingsByStartdate(bookings))

	cacheid := uuid.NewString()
	h.cache.PutWithTTL(cacheid, bookings, 1*time.Hour)

	h.Renderer.VehiclesManagementBookingsList(w, r, vehicles_map, bookings, cacheid)
}

func (h *ApplicationHandler) VehiclesFleetAdd(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		g := r.Context().Value(identification.GroupKey)
		if g == nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		group := g.(storage.Group)

		if err := r.ParseForm(); err != nil {
			fmt.Println(err)
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		dataMap := map[string]any{}
		if v := r.FormValue("name"); v != "" {
			dataMap["name"] = v
		}
		if v := r.FormValue("address"); v != "" {
			var address map[string]any
			err := json.Unmarshal([]byte(v), &address)
			if err != nil {
				fmt.Println(err)
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			dataMap["address"] = address
		}
		if v := r.FormValue("informations"); v != "" {
			dataMap["informations"] = v
		}
		if v := r.FormValue("licence_plate"); v != "" {
			dataMap["licence_plate"] = v
		}
		if v := r.FormValue("automatic"); v != "" {
			fmt.Println(v)
			dataMap["automatic"] = (v == "on")
		}

		data, err := structpb.NewValue(dataMap)
		if err != nil {
			fmt.Println(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		vehicle := &fleets.Vehicle{
			Id:             uuid.NewString(),
			Namespace:      "parcoursmob",
			Type:           r.FormValue("type"),
			Administrators: []string{group.ID},
			Data:           data.GetStructValue(),
		}

		request := &fleets.AddVehicleRequest{
			Vehicle: vehicle,
		}

		_, err = h.services.GRPC.Fleets.AddVehicle(context.TODO(), request)

		if err != nil {
			fmt.Println(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		http.Redirect(w, r, fmt.Sprintf("/app/vehicles-management/fleet/%s", vehicle.Id), http.StatusFound)
		return
	}

	vehicles_types := h.config.GetStringSlice("modules.fleets.vehicle_types")
	h.Renderer.VehiclesFleetAdd(w, r, vehicles_types)
}

func (h *ApplicationHandler) VehiclesFleetDisplay(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	vehicleid := vars["vehicleid"]

	request := &fleets.GetVehicleRequest{
		Vehicleid: vehicleid,
	}

	resp, err := h.services.GRPC.Fleets.GetVehicle(context.TODO(), request)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	h.Renderer.VehiclesFleetDisplay(w, r, resp.Vehicle.ToStorageType())
}

func filterVehicle(r *http.Request, v *fleets.Vehicle) bool {
	g := r.Context().Value(identification.GroupKey)
	if g == nil {
		return false
	}

	group := g.(storage.Group)

	for _, n := range v.Administrators {
		if n == group.ID {
			return true
		}
	}

	return false
}

func (h ApplicationHandler) VehicleManagementBookingDisplay(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	bookingid := vars["bookingid"]

	booking, err := h.services.GetBooking(bookingid)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	if r.Method == "POST" {
		r.ParseForm()

		newbooking, _ := fleets.BookingFromStorageType(&booking)

		startdate := r.FormValue("startdate")
		if startdate != "" {
			newstartdate, _ := time.Parse("2006-01-02", startdate)
			newbooking.Startdate = timestamppb.New(newstartdate)

			if newstartdate.Before(newbooking.Unavailablefrom.AsTime()) {
				newbooking.Unavailablefrom = timestamppb.New(newstartdate)
			}
		}

		enddate := r.FormValue("enddate")
		if enddate != "" {
			newenddate, _ := time.Parse("2006-01-02", enddate)
			newbooking.Enddate = timestamppb.New(newenddate)

			if newenddate.After(newbooking.Unavailableto.AsTime()) || newenddate.Equal(newbooking.Unavailableto.AsTime()) {
				newbooking.Unavailableto = timestamppb.New(newenddate.Add(24 * time.Hour))
			}
		}

		unavailablefrom := r.FormValue("unavailablefrom")
		if unavailablefrom != "" {
			newunavailablefrom, _ := time.Parse("2006-01-02", unavailablefrom)
			newbooking.Unavailablefrom = timestamppb.New(newunavailablefrom)
		}

		unavailableto := r.FormValue("unavailableto")
		if unavailableto != "" {
			newunavailableto, _ := time.Parse("2006-01-02", unavailableto)
			newbooking.Unavailableto = timestamppb.New(newunavailableto)
		}

		request := &fleets.UpdateBookingRequest{
			Booking: newbooking,
		}

		_, err := h.services.GRPC.Fleets.UpdateBooking(context.TODO(), request)
		if err != nil {
			fmt.Println(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		booking = newbooking.ToStorageType()
	}

	beneficiary := mobilityaccountsstorage.Account{}

	if booking.Driver != "" {
		beneficiaryrequest := &mobilityaccounts.GetAccountRequest{
			Id: booking.Driver,
		}

		beneficiaryresp, err := h.services.GRPC.MobilityAccounts.GetAccount(context.TODO(), beneficiaryrequest)
		if err != nil {
			fmt.Println(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		beneficiary = beneficiaryresp.Account.ToStorageType()
	}

	grouprequest := &groupsmanagement.GetGroupRequest{
		Id: booking.Vehicle.Administrators[0],
	}

	groupresp, err := h.services.GRPC.GroupsManagement.GetGroup(context.TODO(), grouprequest)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	alternativerequest := &fleets.GetVehiclesRequest{
		Namespaces:       []string{"parcoursmob"},
		Types:            []string{booking.Vehicle.Type},
		Administrators:   booking.Vehicle.Administrators,
		AvailabilityFrom: timestamppb.New(booking.Startdate),
		AvailabilityTo:   timestamppb.New(booking.Enddate.Add(24 * time.Hour)),
	}

	alternativeresp, err := h.services.GRPC.Fleets.GetVehicles(context.TODO(), alternativerequest)
	if err != nil {
		fmt.Println(err)
	}

	alternatives := []any{}

	for _, a := range alternativeresp.Vehicles {
		alternatives = append(alternatives, a.ToStorageType())
	}

	documents := h.filestorage.List(filestorage.PREFIX_BOOKINGS + "/" + bookingid)
	file_types_map := h.config.GetStringMapString("storage.files.file_types")

	h.Renderer.VehicleManagementBookingDisplay(w, r, booking, booking.Vehicle, beneficiary, groupresp.Group.ToStorageType(), documents, file_types_map, alternatives)
}

func (h ApplicationHandler) VehicleManagementBookingChangeVehicle(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	bookingid := vars["bookingid"]

	r.ParseForm()

	newvehicle := r.FormValue("vehicle")

	booking, err := h.services.GetBooking(bookingid)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	booking.Vehicleid = newvehicle

	b, _ := fleets.BookingFromStorageType(&booking)

	request := &fleets.UpdateBookingRequest{
		Booking: b,
	}

	_, err = h.services.GRPC.Fleets.UpdateBooking(context.TODO(), request)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	http.Redirect(w, r, fmt.Sprintf("/app/vehicles-management/bookings/%s", bookingid), http.StatusFound)

}

func (h ApplicationHandler) VehiclesFleetMakeUnavailable(w http.ResponseWriter, r *http.Request) { // Get Group
	g := r.Context().Value(identification.GroupKey)
	if g == nil {
		fmt.Println("no current group")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	current_group := g.(storage.Group)

	// Get current user ID
	u := r.Context().Value(identification.IdtokenKey)
	if u == nil {
		fmt.Println("no current user")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	current_user_token := u.(*oidc.IDToken)

	// Get current user claims
	c := r.Context().Value(identification.ClaimsKey)
	if c == nil {
		fmt.Println("no current user claims")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	current_user_claims := c.(map[string]any)

	vars := mux.Vars(r)
	vehicleid := vars["vehicleid"]

	r.ParseForm()

	start := r.FormValue("unavailablefrom")
	end := r.FormValue("unavailableto")
	comment := r.FormValue("comment")

	unavailablefrom, _ := time.Parse("2006-01-02", start)
	unavailableto, _ := time.Parse("2006-01-02", end)

	data := map[string]any{
		"comment":                      comment,
		"administrator_unavailability": true,
		"booked_by": map[string]any{
			"user": map[string]any{
				"id":           current_user_token.Subject,
				"display_name": current_user_claims["display_name"],
			},
			"group": map[string]any{
				"id":   current_group.ID,
				"name": current_group.Data["name"],
			},
		},
	}

	datapb, err := structpb.NewStruct(data)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	booking := &fleets.Booking{
		Id:              uuid.NewString(),
		Vehicleid:       vehicleid,
		Unavailablefrom: timestamppb.New(unavailablefrom),
		Unavailableto:   timestamppb.New(unavailableto),
		Data:            datapb,
	}

	request := &fleets.CreateBookingRequest{
		Booking: booking,
	}

	_, err = h.services.GRPC.Fleets.CreateBooking(context.TODO(), request)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	http.Redirect(w, r, fmt.Sprintf("/app/vehicles-management/fleet/%s", vehicleid), http.StatusFound)
}

//	func (h *ApplicationHandler) UnbookingVehicles(w http.ResponseWriter, r *http.Request) {
//		request := &fleets.GetVehiclesRequest{
//			Namespaces: []string{"parcoursmob"},
//		}
//		resp, err := h.services.GRPC.Fleets.GetVehicles(context.TODO(), request)
//		if err != nil {
//			fmt.Println(err)
//			w.WriteHeader(http.StatusInternalServerError)
//		}
//		vehicles := []fleetsstorage.Vehicle{}
//		fmt.Println(resp.Vehicles[0].Bookings)
//		for i, vehicle := range resp.Vehicles {
//			if len(resp.Vehicles[i].Bookings) == 0 {
//				v := vehicle.ToStorageType()
//				vehicles = append(vehicles, v)
//			}
//		}
//		// if len(resp.Vehicle.ToStorageType().Bookings) == 0 {
//		// 	h.Renderer.UnbookingVehicles(w, r, resp.Vehicle.ToStorageType())
//		// }
//		// fmt.Println(resp.Vehicle.ToStorageType().Bookings)
//		fmt.Println(vehicles)
//		h.Renderer.UnbookingVehicles(w, r, vehicles)
//	}
func (h *ApplicationHandler) UnbookingVehicle(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	bookingid := vars["bookingid"]

	request := &fleets.GetBookingRequest{
		Bookingid: bookingid,
	}

	resp, err := h.services.GRPC.Fleets.GetBooking(context.TODO(), request)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	now := time.Now()
	date := now.Format("2006-01-02")
	unavailableto, _ := time.Parse("2006-01-02", date)

	current_group, err := h.currentGroup(r)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	current_user_token, current_user_claims, err := h.currentUser(r)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	booked_by_id := resp.Booking.Data.Fields["booked_by"].GetStructValue().Fields["user"].GetStructValue().Fields["id"].GetStringValue()
	booked_by_name := resp.Booking.Data.Fields["booked_by"].GetStructValue().Fields["user"].GetStructValue().Fields["display_name"].GetStringValue()
	booked_by_email := resp.Booking.Data.Fields["booked_by"].GetStructValue().Fields["user"].GetStructValue().Fields["email"].GetStringValue()
	booked_by_group_id := resp.Booking.Data.Fields["booked_by"].GetStructValue().Fields["group"].GetStructValue().Fields["id"].GetStringValue()
	booked_by_group_name := resp.Booking.Data.Fields["booked_by"].GetStructValue().Fields["group"].GetStructValue().Fields["name"].GetStringValue()

	data := map[string]any{
		"booked_by": map[string]any{
			"user": map[string]any{
				"id":           booked_by_id,
				"display_name": booked_by_name,
				"email":        booked_by_email,
			},
			"group": map[string]any{
				"id":   booked_by_group_id,
				"name": booked_by_group_name,
			},
		},
		"unbooked_by": map[string]any{
			"user": map[string]any{
				"id":           current_user_token.Subject,
				"display_name": current_user_claims["first_name"].(string) + " " + current_user_claims["last_name"].(string),
				"email":        current_user_claims["email"],
			},
			"group": map[string]any{
				"id":   current_group.ID,
				"name": current_group.Data["name"],
			},
		},
		"Deleted": true,
		"motif":   r.FormValue("motif"),
	}

	datapb, err := structpb.NewStruct(data)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	if r.Method == "POST" {

		request := &fleets.UpdateBookingRequest{
			Booking: &fleets.Booking{
				Id:              resp.Booking.Id,
				Vehicleid:       resp.Booking.Vehicleid,
				Driver:          resp.Booking.Driver,
				Startdate:       resp.Booking.Startdate,
				Enddate:         resp.Booking.Enddate,
				Unavailablefrom: resp.Booking.Unavailablefrom,
				Unavailableto:   timestamppb.New(unavailableto),
				Data:            datapb,
			},
		}

		_, err := h.services.GRPC.Fleets.UpdateBooking(context.TODO(), request)

		if err != nil {
			fmt.Println(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		http.Redirect(w, r, "/app/vehicles-management/", http.StatusFound)
		return
	}
	h.Renderer.UnbookingVehicle(w, r, resp.Booking.ToStorageType())
}

////////////////////////UpdateVehicle///////////////////////

func (h *ApplicationHandler) VehiclesFleetUpdate(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	vehicleID := vars["vehicleid"]
	request := &fleets.GetVehicleRequest{
		Vehicleid: vehicleID,
	}

	resp, err := h.services.GRPC.Fleets.GetVehicle(context.TODO(), request)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	namespaceV := resp.Vehicle.Namespace
	//typeV := resp.Vehicle.Type
	administratorsV := resp.Vehicle.Administrators

	if r.Method == "POST" {
		fmt.Print(r.FormValue("vehicle_type"))
		if err := r.ParseForm(); err != nil {
			fmt.Println(err)
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		dataMap := map[string]any{}
		if v := r.FormValue("name"); v != "" {
			dataMap["name"] = v
		}
		if v := r.FormValue("address"); v != "" {
			var address map[string]any
			err := json.Unmarshal([]byte(v), &address)
			if err != nil {
				fmt.Println(err)
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			dataMap["address"] = address
		}
		if v := r.FormValue("informations"); v != "" {
			dataMap["informations"] = v
		}
		if v := r.FormValue("licence_plate"); v != "" {
			dataMap["licence_plate"] = v
		}
		if v := r.FormValue("automatic"); v != "" {
			fmt.Println(v)
			dataMap["automatic"] = (v == "on")
		}

		data, err := structpb.NewValue(dataMap)
		if err != nil {
			fmt.Println(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		request := &fleets.UpdateVehicleRequest{
			Vehicle: &fleets.Vehicle{
				Id:             vehicleID,
				Namespace:      namespaceV,
				Type:           r.FormValue("type"),
				Administrators: administratorsV,
				Data:           data.GetStructValue(),
			},
		}

		resp, err := h.services.GRPC.Fleets.UpdateVehicle(context.TODO(), request)

		if err != nil {
			fmt.Println(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		http.Redirect(w, r, fmt.Sprintf("/app/vehicles-management/fleet/%s", resp.Vehicle.Id), http.StatusFound)
		return
	}
	vehicles_types := h.config.GetStringSlice("modules.fleets.vehicle_types")
	h.Renderer.VehiclesFleetUpdate(w, r, resp.Vehicle.ToStorageType(), vehicles_types)
}
