package localserver

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/dgraph-io/badger/v3"
	"github.com/pion/webrtc/v3"
)

type NodeChatConfig struct {
	ID            string `json:"id"`
	Name          string `json:"name"`
	Initiator     string `json:"initiator"`
	Target        string `json:"target"`
	InitiatorHost string `json:"initiatorHost"`
	TargetHost    string `json:"targetHost"`
}

type NodeChatChannel struct {
	ID            string              `json:"id"`
	Name          string              `json:"name"`
	Initiator     string              `json:"initiator"`
	Target        string              `json:"target"`
	InitiatorHost string              `json:"initiatorHost"`
	TargetHost    string              `json:"targetHost"`
	LastReadIndex uint                `json:"lastReadIndex"`
	Unread        uint                `json:"unread"`
	DB            *NodeChatDBHandler  `json:"-"`
	Tracking      *NodeChatTrackingDB `json:"-"`
}

type NodeChatChannelsHandler[T ZippytalFSInstance] struct {
	ChatID               string
	ChatFSInstance       T
	DataChannels         map[string]*DataChannel
	ChatDataChannels     map[string]*DataChannel
	ChatDataChannelsFlag *uint32
	Flag                 *uint32
	ChatFSInstanceFlag   *uint32
	ChatFlag             *uint32
	Chats                map[string]*NodeChatChannel
	reqChans             []chan<- *ChatRequest
	init                 bool
}

func NewNodeChatChannelsHandler(chatID, chatName, initiator, target, initiatorHostId, targetHostId string, dataChannels map[string]*DataChannel, flag *uint32) (nodeChatsHandler *NodeChatChannelsHandler[*NodeChatFSInstance], err error) {

	_, err = os.ReadDir(filepath.Join(dataPath, "data", "chats", chatID))
	if err != nil {
		if os.IsNotExist(err) {
			// logger.Printf("creating chat directory for chat %s...\n", chatID)
			mkdirErr := os.MkdirAll(filepath.Join(dataPath, "data", "chats", chatID), 0700)
			if mkdirErr != nil {
				return nil, mkdirErr
			}
			file, ferr := os.Create(filepath.Join(dataPath, "data", "chats", chatID, "chatConfig.json"))
			if ferr != nil {
				return nil, ferr
			}
			baseConfig := NodeChatConfig{
				ID:            chatID,
				Name:          chatName,
				Initiator:     initiator,
				InitiatorHost: initiatorHostId,
				Target:        target,
				TargetHost:    targetHostId,
			}
			bs, jsonErr := json.Marshal(baseConfig)
			if jsonErr != nil {
				return nil, jsonErr
			}
			if _, writeErr := file.WriteString(string(bs)); writeErr != nil {
				return nil, writeErr
			}
			_ = file.Close()
			_, err = os.ReadDir(filepath.Join(dataPath, "data", "chats"))
			if err != nil {
				return nil, err
			}
		} else {
			return
		}
	}
	chats := make(map[string]*NodeChatChannel)

	nodeChatDBHandler, err := NewNodeChatDBHandler(chatID)
	if err != nil {
		return nil, err
	}
	var bs []byte
	bs, err = os.ReadFile(filepath.Join(dataPath, "data", "chats", chatID, "chatConfig.json"))
	if err != nil {
		return nil, err
	}
	// logger.Println(string(bs))
	var c NodeChatChannel
	if err = json.Unmarshal(bs, &c); err != nil {
		return nil, err
	}
	nodeChatTracking, err := NewNodeChatTracking(chatID, initiator, target)
	if err != nil {
		return nil, err
	}
	// logger.Println("chats data :", c.ID, c.Initiator, c.InitiatorHost, c.Target)
	c.DB = nodeChatDBHandler
	c.Tracking = nodeChatTracking
	chats[c.ID] = &c

	chatFlag := uint32(0)
	chatFSFlag := uint32(0)
	chatDCFlag := uint32(0)
	nodeChatsHandler = &NodeChatChannelsHandler[*NodeChatFSInstance]{
		ChatID:               chatID,
		ChatFSInstance:       NewNodeChatFSInstance(chatID),
		ChatFSInstanceFlag:   &chatFSFlag,
		DataChannels:         dataChannels,
		ChatDataChannels:     make(map[string]*DataChannel),
		ChatDataChannelsFlag: &chatDCFlag,
		Flag:                 flag,
		Chats:                chats,
		ChatFlag:             &chatFlag,
		init:                 false,
	}
	return
}

func (zch *NodeChatChannelsHandler[T]) sendChatRequest(reqType string, from string, payload map[string]interface{}) {
	go func() {
		for _, rc := range zch.reqChans {
			rc <- &ChatRequest{
				ReqType: reqType,
				From:    from,
				Payload: payload,
			}
		}
	}()
}

func (zch *NodeChatChannelsHandler[T]) sendDataChannelMessage(reqType string, from string, to string, payload map[string]interface{}) (<-chan struct{}, <-chan error) {
	done, errCh := make(chan struct{}), make(chan error)
	go func() {
		if err := atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) {
			if _, ok := zch.ChatDataChannels[to]; ok {
				bs, jsonErr := json.Marshal(&ZoneResponse{
					Type:    reqType,
					From:    from,
					To:      to,
					Payload: payload,
				})
				if jsonErr != nil {
					return jsonErr
				}
				err = zch.ChatDataChannels[to].DataChannel.SendText(string(bs))
			} else {
				err = fmt.Errorf("no corresponding dataChannel")
			}
			return
		}); err != nil {
			errCh <- err
			return
		}
		done <- struct{}{}
	}()
	return done, errCh
}

func (zch *NodeChatChannelsHandler[T]) Init(ctx context.Context, initiator, target, initiatorNodeID, targetNodeID string) (err error) {
	// for _, member := range authorizedMembers {
	// 	if serr := zch.SetAllPublicChatForUser(member); serr != nil {
	// 		// logger.Println(serr)
	// 	}
	// }
	zch.init = true
	return
}

func (zch *NodeChatChannelsHandler[T]) Subscribe(ctx context.Context, publisher <-chan *ChatRequest) (reqChan chan *ChatRequest, done chan struct{}, errCh chan error) {
	reqChan, done, errCh = make(chan *ChatRequest), make(chan struct{}), make(chan error)
	fmt.Println("subscribing...")
	fmt.Println(zch)
	zch.reqChans = append(zch.reqChans, reqChan)
	go func() {
		for {
			select {
			case <-ctx.Done():
				done <- struct{}{}
				return
			case req := <-publisher:
				if err := zch.handleChatRequest(ctx, req); err != nil {
					errCh <- err
				}
			}
		}
	}()
	fmt.Println(zch.reqChans)
	return
}

func (zch *NodeChatChannelsHandler[T]) GetChats(userId string) (err error) {
	chats := make([]*NodeChatChannel, 0)
	for _, chat := range zch.Chats {
		if chat.Initiator == userId || chat.Target == userId {
			chats = append(chats, chat)
		}
	}
	for _, chat := range chats {
		index, err := chat.Tracking.GetUserLastIndex(userId)
		if err != nil {
			return err
		}
		unread, err := chat.DB.calculateNewChatCount(uint64(index))
		if err != nil {
			return err
		}
		chat.LastReadIndex = index
		chat.Unread = unread
	}
	done, e := zch.sendDataChannelMessage(GET_CHATS_RESPONSE, "node", userId, map[string]interface{}{
		"chats": chats,
	})
	select {
	case <-done:
	case err = <-e:
	}
	return
}

func (zch *NodeChatChannelsHandler[T]) ListChats() (chats []*NodeChatChannel, err error) {
	chats = make([]*NodeChatChannel, 0)
	_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
		for _, chat := range zch.Chats {
			chats = append(chats, chat)
		}
		return
	})
	return
}

func (zch *NodeChatChannelsHandler[T]) AddNewChat(chatID, initiator, target, initiatorHostId, targetHostId string) (err error) {
	if chatID == "" {
		err = fmt.Errorf("not a valid chat name provided")
		return
	}
	if _, ok := zch.Chats[chatID]; ok {
		err = fmt.Errorf("a chat with this name already exist")
		return
	}
	mkdirErr := os.MkdirAll(filepath.Join(dataPath, "data", "chats", chatID), 0700)
	if mkdirErr != nil {
		return mkdirErr
	}
	mkdirErr = os.Mkdir(filepath.Join(dataPath, "data", "chats", chatID, "__tracking__"), 0700)
	if mkdirErr != nil {
		return mkdirErr
	}
	file, ferr := os.Create(filepath.Join(dataPath, "data", "chats", chatID, "chatConfig.json"))
	defer func() {
		_ = file.Close()
	}()
	if ferr != nil {
		return ferr
	}
	baseConfig := &NodeChatConfig{
		ID: chatID,
	}
	bs, jsonErr := json.Marshal(baseConfig)
	if jsonErr != nil {
		return jsonErr
	}
	if _, writeErr := file.WriteString(string(bs)); writeErr != nil {
		return writeErr
	}
	err = file.Close()
	if err != nil {
		return err
	}
	nodeChatDBHandler, err := NewNodeChatDBHandler(chatID)
	if err != nil {
		return err
	}
	nodeChatTrackingDB, err := NewNodeChatTracking(chatID, initiator, target)
	if err != nil {
		return err
	}
	if err = nodeChatTrackingDB.Initialize(1, initiator, target); err != nil {
		return err
	}
	var c NodeChatChannel
	if err = json.Unmarshal(bs, &c); err != nil {
		return err
	}
	c.DB = nodeChatDBHandler
	c.Tracking = nodeChatTrackingDB
	_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
		zch.Chats[c.ID] = &c
		return
	})
	return
}

func (zch *NodeChatChannelsHandler[T]) DeleteChat(chatID string) (err error) {
	if err = atomicallyExecute(zch.ChatFlag, func() (err error) {
		defer delete(zch.Chats, chatID)
		if _, ok := zch.Chats[chatID]; !ok {
			err = fmt.Errorf("no corresponding chat")
			return
		}
		return
	}); err != nil {
		return
	}
	if err = os.RemoveAll(filepath.Join(dataPath, "data", "chats", chatID)); err != nil {
		return
	}
	return
}

func (zch *NodeChatChannelsHandler[T]) ReadLastMessage(userId, chatID string) (err error) {
	err = atomicallyExecute(zch.ChatFlag, func() (err error) {
		if chat, ok := zch.Chats[chatID]; ok {
			err = chat.Tracking.SetUserLastIndex(userId, chat.DB.PreviousId)
		}
		return
	})
	return
}

func (zch *NodeChatChannelsHandler[T]) ListLatestChatMessages(userId, chatID string, lastIndex, limit float64) (err error) {
	var list []*ChatMessage
	var i int
	var done bool
	if err = atomicallyExecute(zch.ChatFlag, func() (err error) {
		if chat, ok := zch.Chats[chatID]; ok {
			list, i, done, err = zch.Chats[chatID].DB.ListChatMessages(int(lastIndex), int(limit))
			if err != nil {
				return
			}
			err = chat.Tracking.SetUserLastIndex(userId, chat.DB.PreviousId)
		}
		return
	}); err != nil {
		return
	}
	success, e := zch.sendDataChannelMessage(CHAT_MESSAGE_LIST, "node", userId, map[string]interface{}{
		"done":         done || i <= 0,
		"lastIndex":    i - 1,
		"chatId":       chatID,
		"chatMessages": list,
	})
	select {
	case <-success:
		fmt.Println("done getting latest messages")
	case err = <-e:
	}
	go func() {
		_ = zch.signalUnreadCount(context.Background(), userId, 0)
	}()
	return
}

func (zch *NodeChatChannelsHandler[T]) ListLatestChatFiles(userId, chatID string, lastIndex, limit float64) (err error) {
	var list []*ChatFile
	var i int
	if err = atomicallyExecute(zch.ChatFlag, func() (err error) {
		if _, ok := zch.Chats[chatID]; ok {
			list, i, err = zch.Chats[chatID].DB.ListChatFiles(int(lastIndex), int(limit))
		}
		return
	}); err != nil {
		return
	}
	done, e := zch.sendDataChannelMessage(CHAT_FILES_LIST, "node", userId, map[string]interface{}{
		"done":         i <= 0,
		"lastIndex":    i,
		"chatId":       chatID,
		"chatMessages": list,
	})
	select {
	case <-done:
	case err = <-e:
	}
	return
}

func (zch *NodeChatChannelsHandler[T]) AddChatMessage(userId, chatID, content string, isResponse bool, chatResponseId uint64, file *ChatFile) (err error) {
	if _, ok := zch.Chats[chatID]; !ok {
		err = fmt.Errorf("no such chat")
		return
	}
	chat := zch.Chats[chatID]
	dateTime := time.Now().Format(time.RFC3339)
	chatMessage := &ChatMessage{
		Content:    content,
		From:       userId,
		IsResponse: isResponse,
		ResponseOf: nil,
		File:       file,
		Tags:       make([]string, 0),
		Date:       dateTime,
	}
	if err = atomicallyExecute(zch.ChatFlag, func() (err error) {
		if chat, ok := zch.Chats[chatID]; ok {
			if isResponse {
				parentMessage, getErr := chat.DB.GetChatMessage(chatResponseId)
				if err != nil {
					if getErr != badger.ErrKeyNotFound {
						return getErr
					}
				}
				chatMessage.ResponseOf = parentMessage
			}
			if err = zch.Chats[chatID].DB.AddNewChatMessage(chatMessage); err != nil {
				return
			}
			chatMessage.ID = zch.Chats[chatID].DB.PreviousId
			_ = chat.Tracking.SetUserLastIndex(userId, chatMessage.ID)
		} else {
			err = fmt.Errorf("no corresponding chats")
		}
		return
	}); err != nil {
		return
	}

	notifyActivity := func(done <-chan struct{}, e <-chan error, destination string) {
		select {
		case <-done:
			fmt.Println(destination, "is connected to this chat")
		case <-e:
			_ = atomicallyExecute(zch.ChatFlag, func() (err error) {
				if chat, ok := zch.Chats[chatID]; ok {
					li, err := chat.Tracking.GetUserLastIndex(destination)
					if err != nil {
						return err
					}
					count, err := chat.DB.calculateNewChatCount(uint64(li))
					if err != nil {
						return err
					}
					fmt.Println("********_**_*")
					fmt.Println(count)
					bs, err := json.Marshal(map[string]any{
						"chatId":   chatID,
						"chatHost": NodeID,
					})
					if err != nil {
						return err
					}
					fmt.Println("noooo errrrrooooorr")
					fmt.Println(destination)
					fmt.Println(userId)
					zch.sendChatRequest(CREATE_NOTIFICATION, "node", map[string]interface{}{
						"type":       "new_chat_message",
						"title":      fmt.Sprintf("%d Unread messages 👀", count),
						"body":       fmt.Sprintf("%d new messages from %s", count, userId),
						"isPushed":   true,
						"payload":    string(bs),
						"recipients": []string{destination},
					})
					go func() {
						if e := zch.signalUnreadCount(context.Background(), destination, count); e != nil {
							fmt.Println(e)
						}
					}()
					fmt.Println("done without error 1")
					fmt.Println("done without error 2")

					fmt.Println("sending notification")
				}
				return
			})
		}
	}
	d1, e1 := zch.sendDataChannelMessage(NEW_CHAT_MESSAGE, "node", chat.Target, map[string]interface{}{
		"chatMessage": chatMessage,
		"chatId":      chatID,
	})
	d2, e2 := zch.sendDataChannelMessage(NEW_CHAT_MESSAGE, "node", chat.Initiator, map[string]interface{}{
		"chatMessage": chatMessage,
		"chatId":      chatID,
	})
	go func() {
		if e := zch.signalLastInteractionTime(context.Background(), dateTime); e != nil {
			fmt.Println(e)
		}
	}()
	if userId == chat.Target {
		go notifyActivity(d2, e2, chat.Initiator)
	} else {
		go notifyActivity(d1, e1, chat.Target)
	}
	return
}

func (zch *NodeChatChannelsHandler[T]) RemoveAllUserChat(userId string) (err error) {
	chats, err := zch.ListChats()
	if err != nil {
		return
	}
	for _, chat := range chats {
		if chat.Initiator == userId || chat.Target == userId {
			if derr := zch.DeleteChat(chat.ID); derr != nil {
				// logger.Println("**************", derr)
			}
			// logger.Println("deleted chat", chat.ID)
		}
	}
	return
}

func (zch *NodeChatChannelsHandler[T]) DeleteChatMessage(key uint64, chatID string) (err error) {
	err = atomicallyExecute(zch.ChatFlag, func() (err error) {
		if _, ok := zch.Chats[chatID]; !ok {
			err = fmt.Errorf("no file corresponding to id %s", chatID)
			return
		}
		chat := zch.Chats[chatID]
		if err = chat.DB.DeleteChatMessage(key); err != nil {
			return
		}
		for _, member := range [2]string{chat.Initiator, chat.Target} {
			d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_DELETED, "node", member, map[string]interface{}{
				"chatId":    chatID,
				"messageId": key,
			})
			select {
			case <-d:
			case <-e:
				// logger.Println(tempErr)
			}
		}

		previousId := zch.Chats[chatID].DB.PreviousId
		if previousId == key {
			if err = zch.Chats[chatID].DB.revertPreviousId(); err != nil {
				return
			}
			previousId = zch.Chats[chatID].DB.PreviousId
			if previousId == 1 {
				previousId = 0
			}
			if err = zch.Chats[chatID].Tracking.RevertTrackingLastIndex(previousId); err != nil {
				return
			}
		}
		return
	})
	return
}

func (zch *NodeChatChannelsHandler[T]) UpdateChatMessage(key uint64, chatID, newContent string) (err error) {
	err = atomicallyExecute(zch.ChatFlag, func() (err error) {
		if _, ok := zch.Chats[chatID]; !ok {
			err = fmt.Errorf("no chat corresponding to id %s", chatID)
			return
		}
		chat := zch.Chats[chatID]
		if err = chat.DB.ModifyChatMessage(key, newContent); err != nil {
			return
		}
		for _, member := range [2]string{chat.Target, chat.Initiator} {
			d, e := zch.sendDataChannelMessage(CHAT_MESSAGE_EDITED, "node", member, map[string]interface{}{
				"chatId":     chatID,
				"messageId":  key,
				"newContent": newContent,
			})
			select {
			case <-d:
			case <-e:
				// logger.Println(tempErr)
			}
		}

		return
	})
	return
}

func (zch *NodeChatChannelsHandler[T]) DeleteChatFile(key uint64, fileName, chatID string) (err error) {
	err = atomicallyExecute(zch.ChatFlag, func() (err error) {
		if _, ok := zch.Chats[chatID]; !ok {
			err = fmt.Errorf("no file corresponding to id %s", chatID)
			return
		}
		chat := zch.Chats[chatID]
		if err = chat.DB.DeleteChatFile(fileName, key); err != nil {
			return
		}
		for _, member := range [2]string{chat.Target, chat.Initiator} {
			d, e := zch.sendDataChannelMessage(CHAT_FILE_DELETED, "node", member, map[string]interface{}{
				"chatId":   chatID,
				"fileId":   key,
				"fileName": fileName,
			})
			select {
			case <-d:
			case <-e:
				// logger.Println(tempErr)
			}
		}

		return
	})
	return
}

func (zch *NodeChatChannelsHandler[T]) chatFileUpload(chatID string, filename string, userId string, dc *DataChannel) {
	var writePipe chan<- []byte
	var done bool
	uploadDone := func() {
		if writePipe != nil {
			close(writePipe)
			writePipe = nil
		}
		done = true
	}
	dc.DataChannel.OnError(func(err error) {
		// logger.Println(err)
		// logger.Println("abort...")
		if !done {
			uploadDone()
		}
		if fufErr := zch.ChatFSInstance.FileUploadFailed(chatID, filename, userId); fufErr != nil {
			// logger.Println(fufErr)
		}
	})
	dc.DataChannel.OnClose(func() {
		if done {
			// logger.Println("closing gracefully...")
		} else {
			// logger.Println("abort...")
			uploadDone()
			if fufErr := zch.ChatFSInstance.FileUploadFailed(chatID, filename, userId); fufErr != nil {
				// logger.Println(fufErr)
			}
		}
	})
	dc.DataChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
		if msg.IsString {
			if string(msg.Data) == "init_upload" {
				// logger.Println("init upload....")
				var initErr error
				if writePipe, initErr = zch.ChatFSInstance.SetupFileUpload(chatID, filename, userId, dc.DataChannel); initErr != nil {
					_ = dc.DataChannel.SendText("abort")
					_ = dc.DataChannel.Close()
					return
				}
				// logger.Println("upload ready !")
				_ = dc.DataChannel.SendText("upload_ready")
			} else if string(msg.Data) == "upload_done" {
				uploadDone()
			}
		} else {
			writePipe <- msg.Data
		}
	})
	dc.DataChannel.OnOpen(func() {
		_ = dc.DataChannel.SendText("channel_ready")
	})
}

func (zch *NodeChatChannelsHandler[T]) chatFileDownload(chatID string, filename string, userId string, dc *DataChannel) {
	var done bool
	dc.DataChannel.OnError(func(err error) {
		if !done {
			// logger.Println("abort...")
			if fdf := zch.ChatFSInstance.FileDownloadFailed(chatID, filename, userId); fdf != nil {
				// logger.Println(fdf)
			}
		}
	})
	dc.DataChannel.OnClose(func() {
		if !done {
			// logger.Println("abort...")
			if fdf := zch.ChatFSInstance.FileDownloadFailed(chatID, filename, userId); fdf != nil {
				// logger.Println(fdf)
			}
		}
	})
	dc.DataChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
		if msg.IsString {
			if string(msg.Data) == "init_download" {
				// logger.Println("init download....")
				var initErr error
				if initErr = zch.ChatFSInstance.SetupFileDownload(chatID, filename, userId, dc.DataChannel); initErr != nil {
					_ = dc.DataChannel.SendText("abort")
					_ = dc.DataChannel.Close()
					return
				}
				// logger.Println("download started !")
			} else if string(msg.Data) == "download_done" {
				done = true
			}
		}
	})
	dc.DataChannel.OnOpen(func() {
		_ = dc.DataChannel.SendText("channel_ready")
	})
}

func (zch *NodeChatChannelsHandler[T]) handleDataChannel(ctx context.Context, dc *DataChannel) (catched bool) {
	var label string
	_ = atomicallyExecute(dc.l, func() (err error) {
		label = dc.DataChannel.Label()
		return
	})
	if strings.Contains(label, "chat_upload") {
		command := strings.Split(label, "|")
		if len(command) == 4 {
			catched = true
			go zch.chatFileUpload(command[1], command[2], command[3], dc)
		}
		// logger.Println(command)
	} else if strings.Contains(label, "chat_download") {
		command := strings.Split(label, "|")
		catched = true
		go zch.chatFileDownload(command[1], command[2], command[3], dc)
		// logger.Println(command)
	} else if strings.Contains(label, "chat_data") {
		command := strings.Split(label, "|")
		catched = true
		_ = atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) {
			zch.ChatDataChannels[command[1]] = dc
			return
		})
		dc.DataChannel.OnOpen(func() {
			bs, err := json.Marshal(map[string]any{
				"type": "chat_init",
				"from": NodeID,
				"to":   command[1],
				"payload": map[string]any{
					"chatId": zch.ChatID,
				},
			})
			if err != nil {
				fmt.Println(err)
			}
			_ = dc.DataChannel.SendText(string(bs))
		})
		dc.DataChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
			fmt.Println("received message there")
			fmt.Println(msg.Data)
		})
		dc.DataChannel.OnClose(func() {
			fmt.Println("closing gratefully chat dc...")
			_ = atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) {
				delete(zch.ChatDataChannels, command[1])
				return
			})
		})
		dc.DataChannel.OnError(func(err error) {
			fmt.Println("error in chat dc...")
			_ = atomicallyExecute(zch.ChatDataChannelsFlag, func() (err error) {
				delete(zch.ChatDataChannels, command[1])
				return
			})
		})
	}
	return
}

func (zch *NodeChatChannelsHandler[T]) signalUnreadCount(ctx context.Context, userId string, count uint) (err error) {
	fmt.Println("this function has been called")
	em := NewEncryptionManager()
	sig := em.SignRequestHMAC(NodeID)
	body, err := json.Marshal(map[string]interface{}{
		"type":     UPDATE_UNREAD_COUNT,
		"mac":      sig,
		"from":     NodeID,
		"peerType": "node",
		"payload": map[string]string{
			"chatId": zch.ChatID,
			"userId": userId,
			"count":  fmt.Sprintf("%d", count),
		},
	})
	if err != nil {
		return
	}
	_, err = http.Post("https://dev.zippytal.com/req", "application/json", bytes.NewReader(body))
	if err != nil {
		// logger.Println("error come from there inn chat manager")
		return
	}
	return
}

func (zch *NodeChatChannelsHandler[T]) signalLastInteractionTime(ctx context.Context, dateTime string) (err error) {
	fmt.Println("this function has been called too")
	em := NewEncryptionManager()
	sig := em.SignRequestHMAC(NodeID)
	body, err := json.Marshal(map[string]interface{}{
		"type":     UPDATE_LAST_INTERACTION_TIME,
		"mac":      sig,
		"from":     NodeID,
		"peerType": "node",
		"payload": map[string]string{
			"chatId":              zch.ChatID,
			"lastInteractionTime": dateTime,
		},
	})
	if err != nil {
		return
	}
	res, err := http.Post("https://dev.zippytal.com/req", "application/json", bytes.NewReader(body))
	if err != nil {
		// logger.Println("error come from there inn chat manager")
		return
	}
	_, err = io.ReadAll(res.Body)
	if err != nil {
		return
	}
	return
}

func (zch *NodeChatChannelsHandler[T]) handleChatRequest(ctx context.Context, req *ChatRequest) (err error) {
	// logger.Println("got request in zone chat handler", req)

	fmt.Println(req)
	switch req.ReqType {
	case LEAVE_CHAT:
		if err = VerifyFieldsString(req.Payload, "userId"); err != nil {
			return
		}
		// err = zch.LeaveChatFSInstance(req.Payload["userId"].(string))
	case DELETE_CHAT:
		if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
			return
		}
		err = zch.DeleteChat(req.Payload["chatId"].(string))
	case CREATE_CHAT:
		if err = VerifyFieldsString(req.Payload, "chatId", "owner", "chatType"); err != nil {
			return
		}
		if err = VerifyFieldsSliceInterface(req.Payload, "members"); err != nil {
			return
		}
		err = zch.AddNewChat(req.Payload["chatId"].(string), req.Payload["initiator"].(string), req.Payload["target"].(string), req.Payload["initiatorHostId"].(string), req.Payload["targetHostId"].(string))
	case GET_CHATS:
		err = zch.GetChats(req.From)
	case LIST_LATEST_CHATS:
		if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
			return
		}
		if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil {
			return
		}
		err = zch.ListLatestChatMessages(req.From, req.Payload["chatId"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64))
		return
	case LIST_LATEST_FILES:
		if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
			return
		}
		if err = VerifyFieldsFloat64(req.Payload, "lastIndex", "limit"); err != nil {
			return
		}
		err = zch.ListLatestChatFiles(req.From, req.Payload["chatId"].(string), req.Payload["lastIndex"].(float64), req.Payload["limit"].(float64))
		return
	case READ_LATEST_MESSAGE:
		if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
			return
		}
		err = zch.ReadLastMessage(req.From, req.Payload["chatId"].(string))
	case ADD_CHAT_MESSAGE:
		// logger.Println("got request in zone chat handler", req)
		if err = VerifyFieldsString(req.Payload, "chatId", "content"); err != nil {
			return
		}
		if err = VerifyFieldsBool(req.Payload, "isResponse"); err != nil {
			return
		}
		var parentChatId uint64
		if req.Payload["isResponse"].(bool) {
			if err = VerifyFieldsFloat64(req.Payload, "parentChatId"); err != nil {
				return
			}
			parentChatId = uint64(req.Payload["parentChatId"].(float64))
		}
		var file *ChatFile = nil
		if _, ok := req.Payload["file"]; ok {
			bs, jsonErr := json.Marshal(req.Payload["file"])
			if jsonErr != nil {
				return jsonErr
			}
			var f ChatFile
			if err = json.Unmarshal(bs, &f); err != nil {
				err = fmt.Errorf("the file payload dont match ChatFile struct pattern : %v", err)
				return
			}
			file = &f
		}
		err = zch.AddChatMessage(req.From, req.Payload["chatId"].(string), req.Payload["content"].(string), req.Payload["isResponse"].(bool), parentChatId, file)
	case DELETE_CHAT_MESSAGE:
		if err = VerifyFieldsString(req.Payload, "chatId"); err != nil {
			return
		}
		if err = VerifyFieldsFloat64(req.Payload, "messageId"); err != nil {
			return
		}
		err = zch.DeleteChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatId"].(string))
	case EDIT_CHAT_MESSAGE:
		if err = VerifyFieldsString(req.Payload, "chatId", "newContent"); err != nil {
			return
		}
		if err = VerifyFieldsFloat64(req.Payload, "messageId"); err != nil {
			return
		}
		err = zch.UpdateChatMessage(uint64(req.Payload["messageId"].(float64)), req.Payload["chatId"].(string), req.Payload["newContent"].(string))
	case DELETE_CHAT_FILE:
		if err = VerifyFieldsString(req.Payload, "chatId", "fileName"); err != nil {
			return
		}
		if err = VerifyFieldsFloat64(req.Payload, "fileId"); err != nil {
			return
		}
		err = zch.DeleteChatFile(uint64(req.Payload["fileId"].(float64)), req.Payload["fileName"].(string), req.Payload["chatId"].(string))
	}
	return
}
