package application

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"sort"
	"strings"
	"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"
	"git.coopgo.io/coopgo-platform/fleets/storage"
	groupsmanagement "git.coopgo.io/coopgo-platform/groups-management/grpcapi"
	groupsmanagementstorage "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/google/uuid"
	"github.com/gorilla/mux"
	"google.golang.org/protobuf/types/known/structpb"
	"google.golang.org/protobuf/types/known/timestamppb"
)

func (h ApplicationHandler) VehiclesSearch(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	fmt.Println("invoked")
	var beneficiary mobilityaccountsstorage.Account

	beneficiarydocuments := []filestorage.FileInfo{}

	vehicles := []any{}
	searched := false
	start := r.FormValue("startdate")
	end := r.FormValue("enddate")

	startdate, _ := time.Parse("2006-01-02", start)
	enddate, _ := time.Parse("2006-01-02", end)
	automatic := (r.FormValue("automatic") == "on")

	administrators := []string{}

	if r.FormValue("beneficiaryid") != "" {
		// Handler form
		searched = true

		requestbeneficiary := &mobilityaccounts.GetAccountRequest{
			Id: r.FormValue("beneficiaryid"),
		}

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

		beneficiary = respbeneficiary.Account.ToStorageType()

		request := &fleets.GetVehiclesRequest{
			Namespaces:       []string{"parcoursmob"},
			AvailabilityFrom: timestamppb.New(startdate),
			AvailabilityTo:   timestamppb.New(enddate),
		}

		if r.FormValue("type") != "" {
			request.Types = []string{r.FormValue("type")}
		}

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

		for _, vehicle := range resp.Vehicles {
			v := vehicle.ToStorageType()

			if r.FormValue("type") == "Voiture" && automatic {
				fmt.Println(v.Data["automatic"])
				if auto, ok := v.Data["automatic"].(bool); !ok || !auto {
					fmt.Println(v.Data["automatic"])
					continue
				}
			}

			adminfound := false
			for _, a := range administrators {
				if a == v.Administrators[0] {
					adminfound = true
					break
				}
			}
			if !adminfound {
				administrators = append(administrators, v.Administrators[0])
			}

			vehicles = append(vehicles, v)
		}

		beneficiarydocuments = h.filestorage.List(filestorage.PREFIX_BENEFICIARIES + "/" + beneficiary.ID)
	}

	accounts, err := h.beneficiaries(r)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	groups := map[string]any{}

	if len(administrators) > 0 {
		admingroups, err := h.services.GRPC.GroupsManagement.GetGroupsBatch(context.TODO(), &groupsmanagement.GetGroupsBatchRequest{
			Groupids: administrators,
		})
		if err != nil {
			fmt.Println(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		for _, g := range admingroups.Groups {
			groups[g.Id] = g.ToStorageType()
		}

	}
	sort.Sort(sorting.BeneficiariesByName(accounts))

	mandatory_documents := h.config.GetStringSlice("modules.fleets.booking_documents.mandatory")
	file_types_map := h.config.GetStringMapString("storage.files.file_types")
	vehicles_types := h.config.GetStringSlice("modules.fleets.vehicle_types")

	h.Renderer.VehiclesSearch(w, r, accounts, searched, vehicles, beneficiary, r.FormValue("startdate"), r.FormValue("enddate"), mandatory_documents, file_types_map, beneficiarydocuments, r.FormValue("type"), automatic, vehicles_types, groups)
}

func (h ApplicationHandler) Book(w http.ResponseWriter, r *http.Request) {
	fmt.Println("Book")
	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
	}

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

	vehicle, err := h.services.GRPC.Fleets.GetVehicle(context.TODO(), &fleets.GetVehicleRequest{
		Vehicleid: vehicleid,
	})
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("Vehicle not found"))
		w.Write([]byte(err.Error()))
		return
	}

	r.ParseMultipartForm(100 * 1024 * 1024)

	start := r.FormValue("startdate")
	end := r.FormValue("enddate")

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

	data := map[string]any{
		"booked_by": map[string]any{
			"user": map[string]any{
				"id":           current_user_token.Subject,
				"display_name": fmt.Sprintf("%s %s", current_user_claims["first_name"], current_user_claims["last_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,
		Driver:          beneficiaryid,
		Startdate:       timestamppb.New(startdate),
		Enddate:         timestamppb.New(enddate),
		Unavailablefrom: timestamppb.New(startdate),
		Unavailableto:   timestamppb.New(enddate.Add(72 * time.Hour)),
		Data:            datapb,
	}

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

	for _, v := range h.config.GetStringSlice("modules.fleets.booking_documents.mandatory") {
		existing_file := r.FormValue("type-" + v)
		if existing_file == "" {
			file, header, err := r.FormFile("doc-" + v)
			if err != nil {
				fmt.Println(err)
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte("Document manquant : " + v))
				return
			}
			defer file.Close()

			fileid := uuid.NewString()

			metadata := map[string]string{
				"type": v,
				"name": header.Filename,
			}

			if err := h.filestorage.Put(file, filestorage.PREFIX_BOOKINGS, fmt.Sprintf("%s/%s_%s", booking.Id, fileid, header.Filename), header.Size, metadata); err != nil {
				fmt.Println(err)
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
		} else {
			path := strings.Split(existing_file, "/")

			if err := h.filestorage.Copy(existing_file, fmt.Sprintf("%s/%s/%s", filestorage.PREFIX_BOOKINGS, booking.Id, path[len(path)-1])); err != nil {
				fmt.Println(err)
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
		}
	}

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

	//NOTIFY GROUP MEMBERS
	members, _, err := h.groupmembers(vehicle.Vehicle.Administrators[0])
	if err != nil {
		fmt.Println(err)
	} else {
		for _, m := range members {
			if email, ok := m.Data["email"].(string); ok {
				h.emailing.Send("fleets.bookings.creation_admin_alert", email, map[string]string{
					"bookingid": booking.Id,
				})
			}
		}
	}

	http.Redirect(w, r, fmt.Sprintf("/app/vehicles/bookings/%s", booking.Id), http.StatusFound)

}

func (h ApplicationHandler) VehicleBookingDisplay(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
	}

	booking := resp.Booking.ToStorageType()

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

	beneficiaryresp, err := h.services.GRPC.MobilityAccounts.GetAccount(context.TODO(), beneficiaryrequest)
	if err != nil {
		beneficiaryresp = &mobilityaccounts.GetAccountResponse{
			Account: &mobilityaccounts.Account{},
		}
	}

	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
	}

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

	h.Renderer.VehicleBookingDisplay(w, r, booking, booking.Vehicle, beneficiaryresp.Account.ToStorageType(), groupresp.Group.ToStorageType(), documents, file_types_map)
}

func (h ApplicationHandler) VehiclesBookingsList(w http.ResponseWriter, r *http.Request) {

	g := r.Context().Value(identification.GroupKey)
	if g == nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	group := g.(groupsmanagementstorage.Group)

	request := &fleets.GetBookingsRequest{}
	resp, err := h.services.GRPC.Fleets.GetBookings(context.TODO(), request)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusNotFound)
		return
	}

	bookings := []storage.Booking{}

	for _, b := range resp.Bookings {
		booking := b.ToStorageType()
		if b1, ok := booking.Data["booked_by"].(map[string]any); ok {
			if b2, ok := b1["group"].(map[string]any); ok {
				if b2["id"] == group.ID {
					bookings = append(bookings, booking)
				}
			}
		}

	}

	sort.Sort(sorting.BookingsByStartdate(bookings))

	vehicles, _ := h.services.GetVehiclesMap()
	groups, _ := h.services.GetGroupsMap()

	// fmt.Println("bookings : ", bookings)
	// fmt.Println("vehicles : ", vehicles)
	// fmt.Println("groups : ", groups)
	h.Renderer.VehicleBookingsList(w, r, bookings, vehicles, groups)
}

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

	fmt.Println(fmt.Sprintf("%s/%s", bookingid, document))

	file, info, err := h.filestorage.Get(filestorage.PREFIX_BOOKINGS, fmt.Sprintf("%s/%s", bookingid, document))
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", info.ContentType)
	if _, err = io.Copy(w, file); err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

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

}
