package application

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

	formvalidators "git.coopgo.io/coopgo-apps/parcoursmob/utils/form-validators"
	"git.coopgo.io/coopgo-apps/parcoursmob/utils/identification"
	"git.coopgo.io/coopgo-apps/parcoursmob/utils/sorting"
	agenda "git.coopgo.io/coopgo-platform/agenda/grpcapi"
	agendastorage "git.coopgo.io/coopgo-platform/agenda/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"
	"github.com/gorilla/mux"
	"google.golang.org/protobuf/types/known/structpb"
	"google.golang.org/protobuf/types/known/timestamppb"
)

type EventsForm struct {
	Name           string     `json:"name" validate:"required"`
	Type           string     `json:"type" validate:"required"`
	Description    string     `json:"description"`
	Address        any        `json:"address,omitempty"`
	Allday         bool       `json:"allday"`
	Startdate      *time.Time `json:"startdate"`
	Enddate        *time.Time `json:"enddate"`
	Starttime      string     `json:"starttime"`
	Endtime        string     `json:"endtime"`
	MaxSubscribers int        `json:"max_subscribers"`
}

func (h *ApplicationHandler) AgendaHome(w http.ResponseWriter, r *http.Request) {
	resp, err := h.services.GRPC.Agenda.GetEvents(context.TODO(), &agenda.GetEventsRequest{
		Namespaces: []string{"parcoursmob_dispositifs"},
		Mindate:    timestamppb.New(time.Now().Add(-24 * time.Hour)),
	})

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

	responses := []agendastorage.Event{}

	groupids := []string{}
	for _, e := range resp.Events {
		groupids = append(groupids, e.Owners...)
		responses = append(responses, e.ToStorageType())
	}

	sort.Sort(sorting.EventsByStartdate(responses))

	groupsresp, err := h.services.GRPC.GroupsManagement.GetGroupsBatch(context.TODO(), &groupsmanagement.GetGroupsBatchRequest{
		Groupids: groupids,
	})
	groups := map[string]any{}

	if err == nil {
		for _, g := range groupsresp.Groups {
			groups[g.Id] = g.ToStorageType()
		}
	}
	h.Renderer.AgendaHome(w, r, responses, groups)
}

func (h *ApplicationHandler) AgendaHistory(w http.ResponseWriter, r *http.Request) {
	resp, err := h.services.GRPC.Agenda.GetEvents(context.TODO(), &agenda.GetEventsRequest{
		Namespaces: []string{"parcoursmob_dispositifs"},
		//Maxdate:    timestamppb.New(time.Now().Add(24 * time.Hour)),
	})

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

	responses := []agendastorage.Event{}

	groupids := []string{}
	for _, e := range resp.Events {
		groupids = append(groupids, e.Owners...)
		responses = append(responses, e.ToStorageType())
	}

	sort.Sort(sorting.EventsByStartdate(responses))

	groupsresp, err := h.services.GRPC.GroupsManagement.GetGroupsBatch(context.TODO(), &groupsmanagement.GetGroupsBatchRequest{
		Groupids: groupids,
	})
	groups := map[string]any{}

	if err == nil {
		for _, g := range groupsresp.Groups {
			groups[g.Id] = g.ToStorageType()
		}
	}
	h.Renderer.AgendaHistory(w, r, responses, groups)
}

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

		group := g.(storage.Group)

		eventForm, err := parseEventsForm(r)
		if err != nil {
			fmt.Println(err)
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		data, _ := structpb.NewStruct(map[string]any{
			"address": eventForm.Address,
		})

		request := &agenda.CreateEventRequest{
			Event: &agenda.Event{
				Namespace:      "parcoursmob_dispositifs",
				Owners:         []string{group.ID},
				Type:           eventForm.Type,
				Name:           eventForm.Name,
				Description:    eventForm.Description,
				Startdate:      timestamppb.New(*eventForm.Startdate),
				Enddate:        timestamppb.New(*eventForm.Enddate),
				Starttime:      eventForm.Starttime,
				Endtime:        eventForm.Endtime,
				Allday:         eventForm.Allday,
				MaxSubscribers: int64(eventForm.MaxSubscribers),
				Data:           data,
				Deleted:        false,
			},
		}

		resp, err := h.services.GRPC.Agenda.CreateEvent(context.TODO(), request)

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

		http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", resp.Event.Id), http.StatusFound)
		return
	}
	h.Renderer.AgendaCreateEvent(w, r)
}

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

	request := &agenda.GetEventRequest{
		Id: eventid,
	}

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

	grouprequest := &groupsmanagement.GetGroupRequest{
		Id: resp.Event.Owners[0],
	}

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

	subscribers := map[string]any{}

	accids := []string{}
	for _, v := range resp.Event.Subscriptions {
		accids = append(accids, v.Subscriber)
	}

	subscriberresp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(
		context.TODO(),
		&mobilityaccounts.GetAccountsBatchRequest{
			Accountids: accids,
		},
	)

	if err == nil {
		for _, sub := range subscriberresp.Accounts {
			subscribers[sub.Id] = sub.ToStorageType()
		}
	}

	g := r.Context().Value(identification.GroupKey)
	if g == nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	group := g.(storage.Group)

	accountids := []string{}
	for _, m := range group.Members {
		if !contains(resp.Event.Subscriptions, m) {
			accountids = append(accountids, m)
		}
	}

	accountresp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(
		context.TODO(),
		&mobilityaccounts.GetAccountsBatchRequest{
			Accountids: accountids,
		},
	)

	accounts := []any{}

	if err == nil {
		for _, acc := range accountresp.Accounts {
			accounts = append(accounts, acc)
		}
	}

	h.Renderer.AgendaDisplayEvent(w, r, resp.Event.ToStorageType(), groupresp.Group.ToStorageType(), subscribers, accounts)
}

func (h *ApplicationHandler) AgendaSubscribeEvent(w http.ResponseWriter, r *http.Request) {
	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)
	eventid := vars["eventid"]

	if err := r.ParseForm(); err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	subscriber := r.FormValue("subscriber")
	data := map[string]any{
		"subscribed_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"].(string),
			},
			"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
	}

	request := &agenda.SubscribeEventRequest{
		Eventid:    eventid,
		Subscriber: subscriber,
		Data:       datapb,
	}

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

	http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", eventid), http.StatusFound)
}

func parseEventsForm(r *http.Request) (*EventsForm, error) {
	if err := r.ParseForm(); err != nil {
		return nil, err
	}

	var startdate *time.Time
	var enddate *time.Time

	if r.PostFormValue("startdate") != "" {
		d, err := time.Parse("2006-01-02", r.PostFormValue("startdate"))
		if err != nil {
			return nil, err
		}
		startdate = &d
	}

	if r.PostFormValue("enddate") != "" {
		d, err := time.Parse("2006-01-02", r.PostFormValue("enddate"))
		if err != nil {
			return nil, err
		}
		enddate = &d
	}

	max_subscribers, err := strconv.Atoi(r.PostFormValue("max_subscribers"))
	if err != nil {
		return nil, err
	}

	formData := &EventsForm{
		Name:           r.PostFormValue("name"),
		Type:           r.PostFormValue("type"),
		Description:    r.PostFormValue("description"),
		Startdate:      startdate,
		Enddate:        enddate,
		Starttime:      r.PostFormValue("starttime"),
		Endtime:        r.PostFormValue("endtime"),
		MaxSubscribers: max_subscribers,
	}

	if r.PostFormValue("allday") == "true" {
		formData.Allday = true
	}

	if r.PostFormValue("address") != "" {
		var a any
		json.Unmarshal([]byte(r.PostFormValue("address")), &a)

		formData.Address = a
	}

	validate := formvalidators.New()
	if err := validate.Struct(formData); err != nil {
		return nil, err
	}

	return formData, nil
}

func contains(s []*agenda.Subscription, e string) bool {
	for _, a := range s {
		if a.Subscriber == e {
			return true
		}
	}
	return false
}

///////////////////////////////Update Event/////////////////////////////////////////
func (h *ApplicationHandler) AgendaUpdateEvent(w http.ResponseWriter, r *http.Request) {
	adm := strings.Split(r.URL.Path, "/")
	eventID := adm[3]
	request := &agenda.GetEventRequest{
		Id: eventID,
	}

	resp, err := h.services.GRPC.Agenda.GetEvent(context.TODO(), request)
	if err != nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	if r.Method == "POST" {
		g := r.Context().Value(identification.GroupKey)
		if g == nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		group := g.(storage.Group)

		eventForm, err := parseEventsForm(r)
		if err != nil {
			fmt.Println(err)
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		data, _ := structpb.NewStruct(map[string]any{
			"address": eventForm.Address,
		})

		request := &agenda.UpdateEventRequest{
			Event: &agenda.Event{
				Namespace:      "parcoursmob_dispositifs",
				Id:             eventID,
				Owners:         []string{group.ID},
				Type:           eventForm.Type,
				Name:           eventForm.Name,
				Description:    eventForm.Description,
				Startdate:      timestamppb.New(*eventForm.Startdate),
				Enddate:        timestamppb.New(*eventForm.Enddate),
				Starttime:      eventForm.Starttime,
				Endtime:        eventForm.Endtime,
				Allday:         eventForm.Allday,
				MaxSubscribers: int64(eventForm.MaxSubscribers),
				Data:           data,
				Subscriptions:  resp.Event.Subscriptions,
			},
		}

		resp, err := h.services.GRPC.Agenda.UpdateEvent(context.TODO(), request)

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

		http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", resp.Event.Id), http.StatusFound)
		return
	}
	h.Renderer.AgendaUpdateEvent(w, r, resp.Event.ToStorageType())
}

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

	vars := mux.Vars(r)
	eventID := vars["eventid"]
	request := &agenda.GetEventRequest{
		Id: eventID,
	}

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

	if r.Method == "POST" {

		request := &agenda.UpdateEventRequest{
			Event: &agenda.Event{
				Namespace:      resp.Event.Namespace,
				Id:             resp.Event.Id,
				Owners:         resp.Event.Owners,
				Type:           resp.Event.Type,
				Name:           resp.Event.Name,
				Description:    resp.Event.Description,
				Startdate:      resp.Event.Startdate,
				Enddate:        resp.Event.Enddate,
				Starttime:      resp.Event.Starttime,
				Endtime:        resp.Event.Endtime,
				Allday:         resp.Event.Allday,
				MaxSubscribers: int64(resp.Event.MaxSubscribers),
				Data:           resp.Event.Data,
				Subscriptions:  resp.Event.Subscriptions,
				Deleted:        true,
			},
		}

		_, err := h.services.GRPC.Agenda.UpdateEvent(context.TODO(), request)

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

		http.Redirect(w, r, "/app/agenda/", http.StatusFound)
		return
	}
	h.Renderer.AgendaDeleteEvent(w, r, resp.Event.ToStorageType())
}

///////////////////////////Delete subscriber///////////////////////////////
func (h *ApplicationHandler) AgendaDeleteSubscribeEvent(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	eventId := vars["eventid"]
	subscribeid := vars["subscribeid"]
	s_b_id := ""
	s_b_name := ""
	s_b_email := ""
	s_b_group_id := ""
	s_b_group_name := ""
	request := &agenda.GetEventRequest{
		Id: eventId,
	}

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

	for i := range resp.Event.Subscriptions {
		if resp.Event.Subscriptions[i].Subscriber == subscribeid {
			subscribed_by_id := resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["user"].GetStructValue().Fields["id"].GetStringValue()
			subscribed_by_name := resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["user"].GetStructValue().Fields["display_name"].GetStringValue()
			subscribed_by_email := resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["user"].GetStructValue().Fields["email"].GetStringValue()
			subscribed_by_group_id := resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["group"].GetStructValue().Fields["id"].GetStringValue()
			subscribed_by_group_name := resp.Event.Subscriptions[i].Data.Fields["subscribed_by"].GetStructValue().Fields["group"].GetStructValue().Fields["name"].GetStringValue()
			s_b_id = subscribed_by_id
			s_b_name = subscribed_by_name
			s_b_email = subscribed_by_email
			s_b_group_id = subscribed_by_group_id
			s_b_group_name = subscribed_by_group_name
		}
	}

	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
	}

	data := map[string]any{
		"subscribed_by": map[string]any{
			"user": map[string]any{
				"id":           s_b_id,
				"display_name": s_b_name,
				"email":        s_b_email,
			},
			"group": map[string]any{
				"id":   s_b_group_id,
				"name": s_b_group_name,
			},
		},
		"unsubscribed_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"],
			},
		},
		"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 := &agenda.DeleteSubscriptionRequest{
			Subscriber: subscribeid,
			Eventid:    eventId,
			Data:       datapb,
		}

		data := map[string]any{
			"motif":      r.FormValue("motif"),
			"user":       current_user_claims["first_name"].(string) + " " + current_user_claims["last_name"].(string),
			"subscriber": fmt.Sprintf("http://localhost:9000/app/beneficiaries/%s", subscribeid),
			"link":       fmt.Sprintf("http://localhost:9000/app/agenda/%s", eventId),
		}

		// récupérer l'adresse mail de l'utilisateur qui a créé l'événement
		mail := s_b_email
		fmt.Println(mail)

		_, err := h.services.GRPC.Agenda.DeleteSubscription(context.TODO(), request)

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

		if err := h.emailing.Send("delete_subscriber.request", mail, data); err != nil {
			fmt.Println(err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		http.Redirect(w, r, fmt.Sprintf("/app/agenda/%s", eventId), http.StatusFound)
		return
	}
	h.Renderer.AgendaDeleteSubscribeEvent(w, r, eventId)
}

////////////////////////////////////////////////////////
// /////////////////////History Event////////////////////////
func (h *ApplicationHandler) AgendaHistoryEvent(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	eventId := vars["eventid"]
	request := &agenda.GetEventRequest{
		Id: eventId,
	}

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

	grouprequest := &groupsmanagement.GetGroupRequest{
		Id: resp.Event.Owners[0],
	}

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

	subscribers := map[string]any{}

	accids := []string{}
	for _, v := range resp.Event.DeletedSubscription {
		accids = append(accids, v.Subscriber)
	}

	subscriberresp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(
		context.TODO(),
		&mobilityaccounts.GetAccountsBatchRequest{
			Accountids: accids,
		},
	)

	if err == nil {
		for _, sub := range subscriberresp.Accounts {
			subscribers[sub.Id] = sub.ToStorageType()
		}
	}

	g := r.Context().Value(identification.GroupKey)
	if g == nil {
		fmt.Println(err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	group := g.(storage.Group)

	accountids := []string{}
	for _, m := range group.Members {
		if !contains(resp.Event.DeletedSubscription, m) {
			accountids = append(accountids, m)
		}
	}

	accountresp, err := h.services.GRPC.MobilityAccounts.GetAccountsBatch(
		context.TODO(),
		&mobilityaccounts.GetAccountsBatchRequest{
			Accountids: accountids,
		},
	)

	accounts := []any{}

	if err == nil {
		for _, acc := range accountresp.Accounts {
			accounts = append(accounts, acc)
		}
	}

	h.Renderer.AgendaHistoryEvent(w, r, resp.Event.ToStorageType(), groupresp.Group.ToStorageType(), subscribers, accounts)
}
