package localserver

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

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

const (
	ZONE_OFFER                        ReqType = "zone_offer"
	ZONE_ANSWER                       ReqType = "zone_answer"
	ZONE_COUNTER_OFFER                ReqType = "zone_counter_offer"
	JOIN_ZONE                         ReqType = "join_hosted_squad"
	ZONE_ACCESS_DENIED                ReqType = "zone_squad_access_denied"
	QUIT_ZONE                         ReqType = "zone_stop_call"
	ZONE_ACCESS_GRANTED               ReqType = "zone_access_granted"
	INCOMING_ZONE_MEMBER              ReqType = "incoming_zone_member"
	LEAVING_ZONE_MEMBER               ReqType = "leaving_zone_member"
	ZONE_WEBRTC_RENNEGOTIATION_OFFER  ReqType = "zone_rennegotiation_offer"
	ZONE_WEBRTC_RENNEGOTIATION_ANSWER ReqType = "zone_rennegotiation_answer"
	ZONE_WEBRTC_CANDIDATE             ReqType = "zone_webrtc_candidate"
	NEW_ZONE                          ReqType = "new_zone"
	NEW_AUTHORIZED_ZONE_MEMBER        ReqType = "new_authorized_zone_member"
	REMOVED_ZONE_AUTHORIZED_MEMBER    ReqType = "removed_zone_authorized_member"
	DELETE_ZONE                               = "delete_zone"
	DISCONNECT_ZONE_MEMBER                    = "disconnect_zone_member"
)

type ZoneGrpcMiddleware struct {
	Manager *ZoneManager
	stream  SignalingService_LinkClient
}

func NewZoneGrpcMiddleware(manager *ZoneManager) (zoneGrpcMiddleware *ZoneGrpcMiddleware) {
	zoneGrpcMiddleware = &ZoneGrpcMiddleware{
		Manager: manager,
	}
	return
}

func (zm *ZoneGrpcMiddleware) 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(ZONE_WEBRTC_CANDIDATE),
		From:    zm.Manager.ID,
		To:      to,
		Payload: bs,
	})
	return
}

func (zm *ZoneGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage, stream SignalingService_LinkClient) (err error) {
	done, errCh := make(chan struct{}), make(chan error)
	go func() {
		var payload map[string]string
		if e := json.Unmarshal(req.Payload, &payload); err != nil {
			errCh <- e
			return
		}
		switch req.Type {
		case string(INCOMING_ZONE_MEMBER):
		case string(DELETE_ZONE):
			if err := validateRequest(payload, "zoneId"); err != nil {
				return
			}
			if err := zm.Manager.DeleteZone(payload["zoneId"]); err != nil {
				errCh <- err
				return
			}
		case string(LEAVING_ZONE_MEMBER):
			if err := validateRequest(payload, "zoneId"); err != nil {
				return
			}
			if err := zm.Manager.HandleLeavingMember(req.GetFrom(), payload["zoneId"], false); err != nil {
				errCh <- err
				return
			}
		case string(REMOVED_ZONE_AUTHORIZED_MEMBER):
			if err := validateRequest(payload, "zoneId", "userId"); err != nil {
				return
			}
			if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) {
				reqChan := make(chan *ZoneRequest)
				done, e := zm.Manager.Zones[payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan)
				go func() {
					defer close(reqChan)
					reqChan <- &ZoneRequest{
						ReqType: string(REMOVE_USER),
						From:    payload["userId"],
						Payload: map[string]interface{}{
							"userId": payload["userId"],
						},
					}
					reqChan <- &ZoneRequest{
						ReqType: string(REMOVED_ZONE_AUTHORIZED_MEMBER),
						From:    payload["userId"],
						Payload: map[string]interface{}{
							"userId": payload["userId"],
						},
					}
				}()
				select {
				case <-done:
				case err = <-e:
				}
				return
			}); err != nil {
				errCh <- err
				return
			}
		case string(NEW_AUTHORIZED_ZONE_MEMBER):
			if err := validateRequest(payload, "zoneId", "userId"); err != nil {
				return
			}
			if err = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) {
				reqChan := make(chan *ZoneRequest)
				done, e := zm.Manager.Zones[payload["zoneId"]].ZoneRequestScheduler.Schedule(reqChan)
				go func() {
					defer close(reqChan)
					reqChan <- &ZoneRequest{
						ReqType: string(ADD_USER),
						From:    payload["userId"],
						Payload: map[string]interface{}{
							"userId": payload["userId"],
						},
					}
					reqChan <- &ZoneRequest{
						ReqType: string(NEW_AUTHORIZED_ZONE_MEMBER),
						From:    payload["userId"],
						Payload: map[string]interface{}{
							"userId": payload["userId"],
						},
					}
				}()
				select {
				case <-done:
				case err = <-e:
				}
				return
			}); err != nil {
				errCh <- err
				return
			}
		case string(NEW_ZONE):
			logger.Println(payload)
			if err := validateRequest(payload, "zoneId", "zoneName", "zoneImageURL", "zoneOwner", "zoneCreationDate"); err != nil {
				errCh <- err
				return
			}
			zone, err := NewZone(zm.Manager.ID, payload["zoneId"], payload["zoneName"], payload["zoneImageURL"], payload["zoneOwner"], payload["zoneCreationDate"], true, []string{payload["zoneOwner"]})
			if err != nil {
				errCh <- err
				return
			}
			_ = atomicallyExecute(zm.Manager.zoneFlag, func() (err error) {
				zm.Manager.Zones[zone.ID] = zone
				return
			})
		case string(ZONE_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(ZONE_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(ZONE_COUNTER_OFFER):
			if err := zm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
				errCh <- err
				return
			}
		case string(ZONE_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(ZONE_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(ZONE_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 zon grpc middleware")
			logger.Println(payload)
			logger.Println(req.Type)
		}
		done <- struct{}{}
	}()
	select {
	case <-ctx.Done():
		return ctx.Err()
	case err = <-errCh:
		return
	case <-done:
		return
	}
}
