package localserver

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

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

const (
	NEW_CHAT                          = "new_chat"
	CHAT_DELETED                      = "chat_deleted"
	CHAT_SYNCED                       = "chat_synced"
	SYNC_CHAT                         = "sync_chat"
	EXPORT_CHAT                       = "export_chat"
	CHAT_EXPORTED                     = "chat_exported"
	GET_NODE_CHATS                    = "get_node_chats"
	LEAVE_CHAT                        = "leave_chat"
	CHAT_WEBRTC_OFFER                 = "chat_webrtc_offer"
	CHAT_WEBRTC_ANSWER                = "chat_webrtc_answer"
	CHAT_WEBRTC_COUNTER_OFFER         = "chat_webrtc_counter_offer"
	CHAT_WEBRTC_RENNEGOTIATION_OFFER  = "chat_webrtc_rennegotiation_offer"
	CHAT_WEBRTC_RENNEGOTIATION_ANSWER = "chat_werbtc_rennegotiation_answer"
	CHAT_WEBRTC_CANDIDATE             = "chat_webrtc_candidate"
)

type ChatGrpcMiddleware struct {
	Manager *ChatsManager
	stream  SignalingService_LinkClient
}

func NewChatGrpcMiddleware(manager *ChatsManager) (chatGrpcMiddleware *ChatGrpcMiddleware) {
	chatGrpcMiddleware = &ChatGrpcMiddleware{
		Manager: manager,
	}
	return
}

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

func (cm *ChatGrpcMiddleware) Process(ctx context.Context, req *SignalingMessage, streram 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); e != nil {
			errCh <- e
			return
		}
		switch req.GetType() {
		case NEW_CHAT:
			if err := validateRequest(payload, "chatID", "initiator", "target", "initiatorHostId", "targetHostId"); err != nil {
				errCh <- err
				return
			}
			chat, err := NewNodeChat(payload["chatID"], payload["chatID"], payload["initiator"], payload["target"], payload["initiatorHostId"], payload["targetHostId"], false)
			if err != nil {
				errCh <- err
				return
			}
			_ = atomicallyExecute(cm.Manager.chatsFlag, func() (err error) {
				cm.Manager.Chats[chat.ID] = chat
				return
			})
		case CHAT_DELETED:
			if err := validateRequest(payload, "chatID"); err != nil {
				return
			}
			if err := cm.Manager.DeleteChat(payload["chatID"]); err != nil {
				errCh <- err
				return
			}
		case CHAT_EXPORTED:
			//todo: implement chat exported
		case EXPORT_CHAT:
			//todo: implement export chat exported
		case CHAT_SYNCED:
			//todo: implement chat synced
		case SYNC_CHAT:
			//todo: implement sync chat
		case CHAT_WEBRTC_OFFER:
			fmt.Println("got offer in chat")
			if err := validateRequest(payload, SDP); err != nil {
				errCh <- err
				return
			}
			if err := cm.Manager.HandleOffer(ctx, req.GetFrom(), req.GetTo(), payload, cm.signalCandidate); err != nil {
				errCh <- err
				return
			}
		case CHAT_WEBRTC_ANSWER:
			if err := validateRequest(payload, SDP); err != nil {
				errCh <- err
				return
			}
			if err := cm.Manager.HandleAnswer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
				errCh <- err
				return
			}
		case CHAT_WEBRTC_COUNTER_OFFER:
			fmt.Println("got counter offer in chat")
			if err := cm.Manager.HandleCounterOffer(ctx, req.GetFrom(), req.GetTo(), payload); err != nil {
				errCh <- err
				return
			}
		case CHAT_WEBRTC_RENNEGOTIATION_OFFER:
			if err := validateRequest(payload, SDP); err != nil {
				errCh <- err
				return
			}
			if err := cm.Manager.HandleRennegotiationOffer(req.GetFrom(), payload[SDP]); err != nil {
				errCh <- err
				return
			}
		case CHAT_WEBRTC_RENNEGOTIATION_ANSWER:
			if err := validateRequest(payload, SDP); err != nil {
				errCh <- err
				return
			}
			if err := cm.Manager.HandleRennegotiationAnswer(req.GetFrom(), payload[SDP]); err != nil {
				errCh <- err
				return
			}
		case CHAT_WEBRTC_CANDIDATE:
			fmt.Println("got candidate in chat")
			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 := cm.Manager.AddCandidate(&webrtc.ICECandidateInit{
				Candidate:     payload["candidate"],
				SDPMid:        &sdpMid,
				SDPMLineIndex: &SDPMLineIndex,
			}, req.GetFrom()); err != nil {
				errCh <- err
				return
			}
		default:
			fmt.Println("no request for chat grpc middleware")
			fmt.Println(req.Type)
			fmt.Println(req.Payload)
			// logger.Println("no request for chats 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
	}
}
