package localserver

import (
	"context"
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/pion/webrtc/v3"
)

const (
	HOSTED_SQUAD_OFFER                        ReqType = "hosted_squad_offer"
	HOSTED_SQUAD_ANSWER                       ReqType = "hosted_squad_answer"
	HOSTED_SQUAD_COUNTER_OFFER                ReqType = "hosted_squad_counter_offer"
	JOIN_SQUAD                                ReqType = "join_hosted_squad"
	HOSTED_SQUAD_ACCESS_DENIED                ReqType = "hosted_squad_hosted_squad_access_denied"
	QUIT_SQUAD                                ReqType = "hosted_squad_stop_call"
	HOSTED_SQUAD_ACCESS_GRANTED               ReqType = "hosted_squad_access_granted"
	INCOMING_HOSTED_SQUAD_MEMBER              ReqType = "incoming_hosted_squad_member"
	LEAVING_HOSTED_SQUAD_MEMBER               ReqType = "leaving_hosted_squad_member"
	HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER  ReqType = "hosted_squad_rennegotiation_offer"
	HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER ReqType = "hosted_squad_rennegotiation_answer"
	HOSTED_SQUAD_WEBRTC_CANDIDATE             ReqType = "hosted_squad_webrtc_candidate"
	NEW_SQUAD                                 ReqType = "new_hosted_squad"
	NEW_AUTHORIZED_SQUAD_MEMBER                       = "new_authorized_squad_member"
	REMOVED_SQUAD_AUTHORIZED_MEMBER                   = "removed_squad_authorized_member"
	DELETE_SQUAD                                      = "delete_hosted_squad"
	DISCONNECT_SQUAD_MEMBER                           = "disconnect_squad_member"
	UPDATE_SQUAD_LAST_INTERACTION_TIME                = "update_squad_last_interaction_time"
	NEW_SQUAD_CHAT_ACTIVITY                           = "new_squad_chat_activity"
)

type SquadGrpcMiddleware struct {
	Manager *SquadManager
	stream  SignalingService_LinkClient
}

func NewSquadGrpcMiddleware(manager *SquadManager) (squadGrpcMiddleware *SquadGrpcMiddleware) {
	squadGrpcMiddleware = &SquadGrpcMiddleware{
		Manager: manager,
	}
	return
}

func (zm *SquadGrpcMiddleware) signalCandidate(to string, candidate *webrtc.ICECandidate) (err error) {
	bs, err := json.Marshal(map[string]string{
		"from":          zm.Manager.ID,
		"to":            to,
		"candidate":     candidate.ToJSON().Candidate,
		"sdpMid":        *candidate.ToJSON().SDPMid,
		"sdpMLineIndex": strconv.Itoa(int(*candidate.ToJSON().SDPMLineIndex)),
	})
	if err != nil {
		return
	}
	err = zm.stream.Send(&SignalingMessage{
		Type:    string(HOSTED_SQUAD_WEBRTC_CANDIDATE),
		From:    zm.Manager.ID,
		To:      to,
		Payload: bs,
	})
	return
}

func (zm *SquadGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) {
	done, errCh := make(chan struct{}), make(chan error)
	go func() {
		defer func() {
			done <- struct{}{}
		}()
		var payload map[string]string
		if e := json.Unmarshal(req.Payload, &payload); e != nil {
			errCh <- e
			return
		}
		switch req.Type {
		case string(INCOMING_HOSTED_SQUAD_MEMBER):
		case string(DELETE_SQUAD):
			if err := validateRequest(payload, "squadId"); err != nil {
				return
			}
			if err := zm.Manager.DeleteSquad(payload["squadId"]); err != nil {
				errCh <- err
				return
			}
		case string(LEAVING_HOSTED_SQUAD_MEMBER):
			if err := validateRequest(payload, "squadId"); err != nil {
				return
			}
			if err := zm.Manager.HandleLeavingMember(req.GetFrom(), payload["squadId"], false); err != nil {
				errCh <- err
				return
			}
		case string(REMOVED_SQUAD_AUTHORIZED_MEMBER):
			fmt.Println("got there biatch")
			if err := validateRequest(payload, "squadId", "userId"); err != nil {
				return
			}
			if _, ok := zm.Manager.Squads[payload["squadId"]]; !ok {
				err = fmt.Errorf("no such squad")
				return
			}
			reqChan := make(chan *SquadRequest)
			d, e := zm.Manager.Squads[payload["squadId"]].SquadRequestScheduler.Schedule(reqChan)
			go func() {
				defer func() {
					if _, closed := <-reqChan; !closed {
						close(reqChan)
					}
				}()
				reqChan <- &SquadRequest{
					ReqType: string(REMOVED_SQUAD_AUTHORIZED_MEMBER),
					From:    payload["userId"],
					Payload: map[string]interface{}{
						"userId": payload["userId"],
					},
				}
			}()
			select {
			case <-d:
				fmt.Println("uwuwuwuwuwuwuw donnnnee")
			case ee := <-e:
				fmt.Println("uuuuuuwuuuuuu", ee)
			}
			return
		case string(NEW_AUTHORIZED_SQUAD_MEMBER):
			if err := validateRequest(payload, "squadId", "userId"); err != nil {
				return
			}
			if _, ok := zm.Manager.Squads[payload["squadId"]]; !ok {
				err = fmt.Errorf("no such squad")
				return
			}
			reqChan := make(chan *SquadRequest)
			d, e := zm.Manager.Squads[payload["squadId"]].SquadRequestScheduler.Schedule(reqChan)
			go func() {
				defer func() {
					if _, closed := <-reqChan; !closed {
						close(reqChan)
					}
				}()
				reqChan <- &SquadRequest{
					ReqType: string(NEW_AUTHORIZED_SQUAD_MEMBER),
					From:    payload["userId"],
					Payload: map[string]interface{}{
						"userId": payload["userId"],
					},
				}
			}()
			select {
			case <-d:
			case <-e:
			}
			return
		case string(NEW_SQUAD):
			// logger.Println(payload)
			if err := validateRequest(payload, "squadId", "squadName", "squadImageURL", "squadOwner", "squadCreationDate"); err != nil {
				errCh <- err
				return
			}
			squad, err := NewSquad(zm.Manager.ID, payload["squadId"], payload["squadName"], payload["squadImageURL"], payload["squadOwner"], payload["squadCreationDate"], true, []string{payload["squadOwner"]})
			if err != nil {
				errCh <- err
				return
			}
			_ = atomicallyExecute(zm.Manager.squadFlag, func() (err error) {
				zm.Manager.Squads[squad.ID] = squad
				return
			})
		case string(HOSTED_SQUAD_OFFER):
			if err := validateRequest(payload, SDP); err != nil {
				errCh <- err
				return
			}
			if err := zm.Manager.HandleOffer(ctx, req.GetFrom(), req.GetTo(), payload, zm.signalCandidate); err != nil {
				errCh <- err
				return
			}
		case string(HOSTED_SQUAD_ANSWER):
			if err := validateRequest(payload, SDP); err != nil {
				errCh <- err
				return
			}
			if err := zm.Manager.HandleAnswer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
				errCh <- err
				return
			}
		case string(HOSTED_SQUAD_COUNTER_OFFER):
			if err := zm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
				errCh <- err
				return
			}
		case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_OFFER):
			// logger.Println("received negotiation offer")
			if err := validateRequest(payload, SDP); err != nil {
				errCh <- err
				return
			}
			if err := zm.Manager.HandleRennegotiationOffer(req.GetFrom(), payload[SDP]); err != nil {
				errCh <- err
				return
			}
		case string(HOSTED_SQUAD_WEBRTC_RENNEGOTIATION_ANSWER):
			// logger.Println("received negotiation answer")
			if err := validateRequest(payload, SDP); err != nil {
				errCh <- err
				return
			}
			if err := zm.Manager.HandleRennegotiationAnswer(req.GetFrom(), payload[SDP]); err != nil {
				errCh <- err
				return
			}
		case string(HOSTED_SQUAD_WEBRTC_CANDIDATE):
			if err := validateRequest(payload, "candidate", "sdpMLineIndex", "sdpMid"); err != nil {
				errCh <- err
				return
			}
			// logger.Println(payload)
			i, err := strconv.Atoi(payload["sdpMLineIndex"])
			if err != nil {
				errCh <- err
				return
			}
			SDPMLineIndex := uint16(i)
			sdpMid := payload["sdpMid"]
			// logger.Println(sdpMid, SDPMLineIndex)
			if err := zm.Manager.AddCandidate(&webrtc.ICECandidateInit{
				Candidate:     payload["candidate"],
				SDPMid:        &sdpMid,
				SDPMLineIndex: &SDPMLineIndex,
			}, req.GetFrom()); err != nil {
				errCh <- err
				return
			}
		default:
			// logger.Println("no request for squad grpc middleware")
			// logger.Println(payload)
			// logger.Println(req.Type)
		}
	}()
	select {
	case <-ctx.Done():
		return ctx.Err()
	case err = <-errCh:
		return
	case <-done:
		return
	}
}
