import hashlib
import logging
import time

import threading

from scapy.config import conf
from scapy.layers.inet import TCP, IP
from scapy.packet import Packet
from scapy.sendrecv import send, sniff

from random import randrange

log = logging.getLogger(__name__)
TCP_CLIENTS = {}  # ((IP, port) -> [sent_packets])

SERVER_IP = '131.159.15.68' # don't use the domain name in this case
# SERVER_IP = '192.168.1.4' # don't use the domain name in this case
SERVER_PORT = 20102
COOKIE_SECRET = 'TASTY_COOKIES123'
INITIAL_SEQ = 1337
SRC_PORT = randrange(10000, 50000)
first_try = 0
def generate_syn_cookie(client_ip: str, client_port: int, server_secret: str):
    hash_input = f'{client_ip}{client_port}{server_secret}'.encode()
    return int(hashlib.sha256(hash_input).hexdigest(), 16) % (2**32)

def handle_packet(packet: Packet):
    # TODO: please implement me!
    packet.show()
    if packet.haslayer(TCP) and packet[TCP].sport == SERVER_PORT and packet[TCP].dport == SRC_PORT and packet[TCP].flags == "SA":
        print("received SA packett")
        ip = IP(dst=SERVER_IP)
        syn = TCP(sport=SRC_PORT, dport=SERVER_PORT, flags='A', seq=COOKIE, ack=packet[TCP].seq)
        resp = (ip / syn)
        send(resp)
    if packet.haslayer(TCP) and packet[TCP].sport == SERVER_PORT and packet[TCP].dport == SRC_PORT and packet[TCP].flags == "A":
        print("received acknowledgement")
        payload = bytes(packet[TCP].payload).decode(errors='ignore')
        print("Extracted flag:", payload)  # This should print "hello world"
        exit()


# Function to start the packet sniffing
def start_sniffing():    
    sniff(
        filter=f'tcp port {SERVER_PORT}', # this should filter all packets relevant for this challenge.
        prn=handle_packet,
        store=False,
        monitor=True,
        iface='enX0', # set to your interface. IMPORTANT: SET TO enX0 FOR AUTOGRADER!!!
    )

COOKIE = generate_syn_cookie(SERVER_IP, SERVER_PORT, COOKIE_SECRET)

# Run the server in a separate thread
def main():
    conf.use_pcap = False
    server_thread = threading.Thread(target=start_sniffing)
    server_thread.start()

    time.sleep(1) # wait for the sniffer to start.

    # TODO: send intial first byte 
        # Create and send SYN packet to initiate the handshake
    ip_layer = IP(dst=SERVER_IP)
    syn_packet = TCP(sport=SRC_PORT, dport=SERVER_PORT, flags="S", seq=COOKIE)
    send(ip_layer / syn_packet)


if __name__ == '__main__':
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s %(levelname)s [%(module)s:%(lineno)d] %(message)s',
    )

    logging.getLogger('asyncio').setLevel(logging.WARNING)

    main()
