package handlers

import (
	"bytes"
	"customServer/internal/protocol"
	"customServer/internal/state"
	"fmt"
	"net"
)

func HandleEnterLobbyRequest(conn net.Conn, requestData []byte) ([]byte, int) {
	fmt.Println("[TCP] Handling EnterLobbyRequest")

	// Pre-push the game list so it's there when the client transitions
	gameList := getMockGameListBytes()
	conn.Write(protocol.WrapPacket(609, gameList, 999))

	return []byte{}, 601
}

func HandleLobbyPlayerListRequest(conn net.Conn, requestData []byte) ([]byte, int) {
	fmt.Println("[TCP] Handling LobbyPlayerListRequest")
	mockSmallPlayer := state.GetMockSmallPlayerBytes()

	// PlayerList Message
	playerList := make([]byte, 0)
	playerList = append(playerList, 0x0a)
	playerList = append(playerList, protocol.EncodeVarint(uint64(len(mockSmallPlayer)))...)
	playerList = append(playerList, mockSmallPlayer...)

	// Deflate
	compressed := protocol.ZlibCompress(playerList)

	responsePayload := make([]byte, 0)
	responsePayload = append(responsePayload, 0x0a) // Field 1: playerList (ByteString)
	responsePayload = append(responsePayload, protocol.EncodeVarint(uint64(len(compressed)))...)
	responsePayload = append(responsePayload, compressed...)

	return responsePayload, 604
}

func HandleLobbyGameListRequest(conn net.Conn, requestData []byte) ([]byte, int) {
	fmt.Println("[TCP] Handling LobbyGameListRequest")
	return getMockGameListBytes(), 609
}

func HandleObservableGameListRequest(conn net.Conn, requestData []byte) ([]byte, int) {
	fmt.Println("[TCP] Handling ObservableGameListRequest")
	return getMockGameListBytes(), 622
}

func HandleLobbyCreateGameRequest(conn net.Conn, requestData []byte) ([]byte, int) {
	fmt.Println("[TCP] Handling LobbyCreateGameRequest")

	// 1. Scan the request data for Field 607 (LobbyCreateGameRequest)
	// Actually, the dispatcher already gives us the requestData which IS the payload of the request number?
	// In the original main.go, the Switch handled requestNumber.
	// For 607, it manually scanned payloadBytes again.

	var configBytes []byte
	msgReader := bytes.NewReader(requestData)
	for {
		tag, err := protocol.ReadVarint(msgReader)
		if err != nil {
			break
		}
		fieldNum := tag >> 3
		wireType := tag & 0x7

		if fieldNum == 607 && wireType == 2 {
			length, _ := protocol.ReadVarint(msgReader)
			createGameReqBytes := make([]byte, length)
			msgReader.Read(createGameReqBytes)

			// Extract Configuration (Field 1) from LobbyCreateGameRequest
			reqReader := bytes.NewReader(createGameReqBytes)
			for {
				rtag, rerr := protocol.ReadVarint(reqReader)
				if rerr != nil {
					break
				}
				rfieldNum := rtag >> 3
				rwireType := rtag & 0x7
				if rfieldNum == 1 && rwireType == 2 {
					rlength, _ := protocol.ReadVarint(reqReader)
					configBytes = make([]byte, rlength)
					reqReader.Read(configBytes)
					break
				} else {
					protocol.SkipField(reqReader, rwireType)
				}
			}
			break
		} else {
			protocol.SkipField(msgReader, wireType)
		}
	}

	// Update State
	newGameID := int64(4016461897007108096)
	state.GlobalManager.CreateGame(newGameID)

	// Construct Response (LobbyGameCreatedRequest 608)
	gameDetails := getMockGameDetailsBytes(newGameID, configBytes, [][]byte{state.GetMockPlayerBytes()})

	// Push updated game list
	gameList := getMockGameListBytes()
	conn.Write(protocol.WrapPacket(609, gameList, 1000))

	responsePayload := make([]byte, 0)
	responsePayload = append(responsePayload, 0x0a)
	responsePayload = append(responsePayload, protocol.EncodeVarint(uint64(len(gameDetails)))...)
	responsePayload = append(responsePayload, gameDetails...)

	return responsePayload, 608
}

func HandleLobbyJoinGameRequest(conn net.Conn, requestData []byte) ([]byte, int) {
	fmt.Println("[TCP] Handling LobbyJoinGameRequest")
	state.GlobalManager.CreateGame(4016461897007108096)

	// Construct LobbyNewPlayerRequest (611)
	gameDetails := getMockGameDetailsBytes(4016461897007108096, nil, [][]byte{state.GetMockPlayerBytes()})

	// LobbyNewPlayerRequest Message
	newPlayerReq := make([]byte, 0)
	// Field 1: GameDetails
	newPlayerReq = append(newPlayerReq, 0x0a)
	newPlayerReq = append(newPlayerReq, protocol.EncodeVarint(uint64(len(gameDetails)))...)
	newPlayerReq = append(newPlayerReq, gameDetails...)

	// Field 2: JoiningPlayer
	newPlayerReq = append(newPlayerReq, 0x10)
	newPlayerReq = append(newPlayerReq, protocol.EncodeVarint(uint64(state.MockUserID))...)

	return newPlayerReq, 611
}

// Helpers

func getMockGameDetailsBytes(gameID int64, configBytes []byte, players [][]byte) []byte {
	gameDetails := make([]byte, 0)
	gameDetails = append(gameDetails, 0x08)
	gameDetails = append(gameDetails, protocol.EncodeVarint(uint64(gameID))...)

	for _, p := range players {
		gameDetails = append(gameDetails, 0x12)
		gameDetails = append(gameDetails, protocol.EncodeVarint(uint64(len(p)))...)
		gameDetails = append(gameDetails, p...)
	}

	if len(configBytes) > 0 {
		// Field 3: Configuration (From request)
		gameDetails = append(gameDetails, 0x1a)
		gameDetails = append(gameDetails, protocol.EncodeVarint(uint64(len(configBytes)))...)
		gameDetails = append(gameDetails, configBytes...)
	} else {
		// Field 3: Configuration (Fallback)
		fallbackConfig := getFallbackConfigBytes()
		gameDetails = append(gameDetails, 0x1a)
		gameDetails = append(gameDetails, protocol.EncodeVarint(uint64(len(fallbackConfig)))...)
		gameDetails = append(gameDetails, fallbackConfig...)
	}

	return gameDetails
}

func getMockGameListBytes() []byte {
	// For the lobby list, show a game with NO players so anyone can join
	gameData := getMockGameDetailsBytes(4016461897007108096, nil, [][]byte{})

	// GameList Message
	gameList := make([]byte, 0)
	gameList = append(gameList, 0x0a)
	gameList = append(gameList, protocol.EncodeVarint(uint64(len(gameData)))...)
	gameList = append(gameList, gameData...)

	// Deflate
	compressed := protocol.ZlibCompress(gameList)

	responsePayload := make([]byte, 0)
	responsePayload = append(responsePayload, 0x0a) // Field 1: gameList (ByteString)
	responsePayload = append(responsePayload, protocol.EncodeVarint(uint64(len(compressed)))...)
	responsePayload = append(responsePayload, compressed...)
	return responsePayload
}

func getFallbackConfigBytes() []byte {
	fallbackConfig := make([]byte, 0)
	name := "Fallback Game"
	fallbackConfig = append(fallbackConfig, 0x0a)
	fallbackConfig = append(fallbackConfig, protocol.EncodeVarint(uint64(len(name)))...)
	fallbackConfig = append(fallbackConfig, name...)
	fallbackConfig = append(fallbackConfig, 0x10, 0x00, 0x18, 0x01, 0x20, 0x00)
	fallbackConfig = append(fallbackConfig, 0x28)
	fallbackConfig = append(fallbackConfig, protocol.EncodeVarint(1)...)
	fallbackConfig = append(fallbackConfig, 0x30)
	fallbackConfig = append(fallbackConfig, protocol.EncodeVarint(6)...)
	fallbackConfig = append(fallbackConfig, 0x48, 0x00, 0x50, 0x2d)

	// Field 11: Data (IronGameConfiguration)
	ironData := state.GetMockIronGameConfigurationBytes()
	fallbackConfig = append(fallbackConfig, 0x5a) // (11 << 3) | 2 = 88 | 2 = 90 = 0x5a
	fallbackConfig = append(fallbackConfig, protocol.EncodeVarint(uint64(len(ironData)))...)
	fallbackConfig = append(fallbackConfig, ironData...)

	return fallbackConfig
}
