package network

import (
	"log"
	"net"
	"time"

	"gitea.boner.be/bdnugget/goonscape/game"
	"gitea.boner.be/bdnugget/goonscape/types"
	pb "gitea.boner.be/bdnugget/goonserver/actions"
	"google.golang.org/protobuf/proto"
)

func ConnectToServer() (net.Conn, int32, error) {
	conn, err := net.Dial("tcp", types.ServerAddr)
	if err != nil {
		log.Printf("Failed to dial server: %v", err)
		return nil, 0, err
	}

	log.Println("Connected to server. Waiting for player ID...")
	buf := make([]byte, 1024)
	n, err := conn.Read(buf)
	if err != nil {
		log.Printf("Error reading player ID from server: %v", err)
		return nil, 0, err
	}

	var response pb.ServerMessage
	if err := proto.Unmarshal(buf[:n], &response); err != nil {
		log.Printf("Failed to unmarshal server response: %v", err)
		return nil, 0, err
	}

	playerID := response.GetPlayerId()
	log.Printf("Successfully connected with player ID: %d", playerID)
	return conn, playerID, nil
}

func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Player, otherPlayers map[int32]*types.Player) {
	buf := make([]byte, 4096)

	actionTicker := time.NewTicker(types.ClientTickRate)
	defer actionTicker.Stop()

	go func() {
		for range actionTicker.C {
			player.Lock()
			if len(player.ActionQueue) > 0 {
				actions := make([]*pb.Action, len(player.ActionQueue))
				copy(actions, player.ActionQueue)

				batch := &pb.ActionBatch{
					PlayerId: playerID,
					Actions:  actions,
					Tick:     player.CurrentTick,
				}

				player.ActionQueue = player.ActionQueue[:0]
				player.Unlock()

				data, err := proto.Marshal(batch)
				if err != nil {
					log.Printf("Failed to marshal action batch: %v", err)
					continue
				}

				if _, err = conn.Write(data); err != nil {
					log.Printf("Failed to send actions to server: %v", err)
					return
				}
			} else {
				player.Unlock()
			}
		}
	}()

	for {
		n, err := conn.Read(buf)
		if err != nil {
			log.Printf("Failed to read from server: %v", err)
			return
		}

		var serverMessage pb.ServerMessage
		if err := proto.Unmarshal(buf[:n], &serverMessage); err != nil {
			log.Printf("Failed to unmarshal server message: %v", err)
			continue
		}

		player.Lock()
		player.CurrentTick = serverMessage.CurrentTick

		tickDiff := serverMessage.CurrentTick - player.CurrentTick
		if tickDiff > types.MaxTickDesync {
			for _, state := range serverMessage.Players {
				if state.PlayerId == playerID {
					player.ForceResync(state)
					break
				}
			}
		}
		player.Unlock()

		for _, state := range serverMessage.Players {
			if state.PlayerId == playerID {
				continue
			}

			if otherPlayer, exists := otherPlayers[state.PlayerId]; exists {
				otherPlayer.UpdatePosition(state, types.ServerTickRate)
			} else {
				otherPlayers[state.PlayerId] = types.NewPlayer(state)
			}
		}

		if g, ok := player.UserData.(*game.Game); ok && len(serverMessage.ChatMessages) > 0 {
			g.Chat.HandleServerMessages(serverMessage.ChatMessages)
		}
	}
}
