"""
Formato REAL de CLoginChannelListPacket (assinatura 02 06), decodificado byte a byte a partir do
packet #38 da captura real (docs/../captures/real_success_full_flow_parsed.txt, conexao de HOP 2 --
o "servidor de mundo/canal" em 85.119.149.224:10199, NAO o servidor de login em 10001).

Layout do payload (sem o prefixo de 4 bytes de tamanho do frame):

    offset 0   sig            = 02 06
    offset 2   unknown6       = 6 bytes (constante na captura: 00 00 00 03 00 00)
    offset 8   server_count   = u16 BIG-ENDIAN -- quantidade de SERVIDORES (nao canais!)
    offset 10  total_channels = u16 BIG-ENDIAN -- soma de canais de TODOS os servidores
    offset 12  unknown4       = 4 bytes (constante na captura: 00 00 00 00)
    offset 16  server_ids[]   = server_count * u16 BIG-ENDIAN -- IDs dos servidores
                                 (valores reais capturados: 42, 41, 28, 22, 1 -- NAO sao indices
                                 sequenciais 0..N! sao os IDs de verdade que o client usa pra
                                 procurar o NOME/icone na tabela local, ver findings.md)
    offset 16+2*server_count
               channel records, um por canal, 16 bytes cada, agrupados por servidor (contiguos,
               na MESMA ordem de server_ids[]):
                   +0  server_id    u16 LITTLE-ENDIAN (mesmo ID da lista acima, repetido)
                   +2  channel_idx  u16 LITTLE-ENDIAN (1-based, sequencial dentro do servidor)
                   +4  status_flag  u16 LITTLE-ENDIAN (varia 1/2 na captura -- populacao/lotacao?)
                   +6  ip           4 bytes, ordem de rede normal (dotted quad, ex 55 77 95 e0 = 85.119.149.224)
                   +10 port         u16 LITTLE-ENDIAN (ex d7 27 = 0x27d7 = 10199... na verdade os
                                     canais internos usam portas tipo 10101-10106, LE)
                   +12 padding      4 bytes, zero na captura

Confirmado com o parser abaixo reproduzindo os mesmos 5 servidores / 17 canais da captura real:
    server_ids = [42, 41, 28, 22, 1]
    canais por servidor = [3, 2, 3, 3, 6]  (na ordem acima)
"""

from __future__ import annotations

import struct
from dataclasses import dataclass, field


@dataclass
class ChannelEntry:
    server_id: int
    channel_idx: int
    status_flag: int
    ip: str
    port: int


@dataclass
class ChannelListPacket:
    server_ids: list[int]
    channels: list[ChannelEntry] = field(default_factory=list)
    unknown6: bytes = bytes.fromhex("000000030000")
    unknown4: bytes = bytes.fromhex("00000000")

    def encode_payload(self) -> bytes:
        server_count = len(self.server_ids)
        total_channels = len(self.channels)
        out = bytearray()
        out += b"\x02\x06"
        out += self.unknown6
        out += struct.pack(">H", server_count)
        out += struct.pack(">H", total_channels)
        out += self.unknown4
        for sid in self.server_ids:
            out += struct.pack(">H", sid)
        for ch in self.channels:
            out += struct.pack("<H", ch.server_id)
            out += struct.pack("<H", ch.channel_idx)
            out += struct.pack("<H", ch.status_flag)
            out += bytes(int(x) for x in ch.ip.split("."))
            out += struct.pack("<H", ch.port)
            out += b"\x00\x00\x00\x00"
        return bytes(out)


def parse_payload(payload: bytes) -> ChannelListPacket:
    assert payload[0:2] == b"\x02\x06", f"assinatura inesperada: {payload[0:2].hex()}"
    unknown6 = payload[2:8]
    server_count = struct.unpack(">H", payload[8:10])[0]
    total_channels = struct.unpack(">H", payload[10:12])[0]
    unknown4 = payload[12:16]
    offs = 16
    server_ids = []
    for _ in range(server_count):
        server_ids.append(struct.unpack(">H", payload[offs:offs + 2])[0])
        offs += 2
    channels = []
    for _ in range(total_channels):
        rec = payload[offs:offs + 16]
        sid, cidx, flag = struct.unpack("<HHH", rec[0:6])
        ip = ".".join(str(b) for b in rec[6:10])
        port = struct.unpack("<H", rec[10:12])[0]
        channels.append(ChannelEntry(sid, cidx, flag, ip, port))
        offs += 16
    pkt = ChannelListPacket(server_ids=server_ids, channels=channels, unknown6=unknown6, unknown4=unknown4)
    return pkt


if __name__ == "__main__":
    with open(r"D:\RoshEmulator\captures\packet38.bin", "rb") as f:
        data = f.read()
    payload = data[4:]
    pkt = parse_payload(payload)
    print("server_ids:", pkt.server_ids)
    for ch in pkt.channels:
        print(ch)

    # round-trip check
    re_encoded = pkt.encode_payload()
    print("round-trip identico:", re_encoded == payload)
