use anyhow::{Result, anyhow};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;
use tokio::time::{sleep, Duration};
use tracing::{info, error, debug};
use std::net::SocketAddr;
use std::path::PathBuf;
use std::fs::File;
use crate::reader::{EventReader, Event};
use similar::TextDiff;

pub async fn run(
    listen: SocketAddr,
    file: PathBuf,
    no_assert: bool,
    delay: bool,
    abort: bool,
    once: bool,
    text_diff: bool,
) -> Result<()> {
    let listener = TcpListener::bind(&listen).await?;
    info!("Server listening on {}", listen);

    loop {
        let (socket, peer_addr) = listener.accept().await?;
        info!("Accepted connection from {}", peer_addr);

        let file = file.clone();
        tokio::spawn(async move {
            if let Err(e) = handle_connection(socket, file, no_assert, delay, abort, text_diff, false).await {
                error!("Error handling connection from {}: {}", peer_addr, e);
            }
        });

        if once {
            break;
        }
    }

    Ok(())
}

pub async fn handle_connection(
    mut socket: tokio::net::TcpStream,
    file: PathBuf,
    no_assert: bool,
    delay: bool,
    abort: bool,
    text_diff: bool,
    role_reverse: bool,
) -> Result<()> {
    let f = File::open(&file)?;
    let mut reader = EventReader::new(f);

    let mut pending_delay: Option<Duration> = None;

    while let Some(mut event) = reader.read_event()? {
        if role_reverse {
            event = event.role_reverse();
        }

        match event {
            Event::ClientToServer(expected) => {
                debug!("Expecting {} bytes from client", expected.len());
                
                let mut received = vec![0; expected.len()];
                if let Err(e) = socket.read_exact(&mut received).await {
                    if e.kind() == std::io::ErrorKind::UnexpectedEof {
                        return Err(anyhow!("unexpected EOF from client, expected {} bytes", expected.len()));
                    }
                    return Err(e.into());
                }

                if !no_assert && received != expected {
                    let expected_str = String::from_utf8_lossy(&expected);
                    let received_str = String::from_utf8_lossy(&received);

                    eprintln!("Assertion failed: received data mismatch");
                    eprintln!("Expected ({} bytes):\n{}", expected.len(), hex::encode(&expected));
                    eprintln!("Received ({} bytes):\n{}", received.len(), hex::encode(&received));

                    if text_diff {
                        let diff = TextDiff::from_lines(&expected_str, &received_str);
                        eprintln!("\nDiff:");
                        for change in diff.iter_all_changes() {
                            eprint!("{}", change);
                        }
                    }

                    if abort {
                        std::process::exit(3);
                    }
                    return Err(anyhow!("data mismatch"));
                }

                debug!("Verified {} bytes from client", expected.len());
            }
            Event::ServerToClient(data) => {
                if let Some(d) = pending_delay.take() {
                    if delay {
                        debug!("Applying delay of {}ms before sending", d.as_millis());
                        sleep(d).await;
                    }
                }
                debug!("Sending {} bytes to client", data.len());
                socket.write_all(&data).await?;
            }
            Event::ServerToClientEof => {
                debug!("Closing connection to client");
                socket.shutdown().await?;
                break;
            }
            Event::ClientToServerEof => {
                debug!("Verifying client closed connection");
                let mut test_buf = [0u8; 1];
                match socket.read(&mut test_buf).await {
                    Ok(0) => debug!("Confirmed EOF from client"),
                    Ok(_) => return Err(anyhow!("expected EOF from client, but received data")),
                    Err(e) => return Err(e.into()),
                }
                break;
            }
            Event::Delay(ms) => {
                pending_delay = Some(Duration::from_millis(ms as u64));
                debug!("Caching delay of {}ms", ms);
            }
            Event::Noop => {}
        }
    }

    Ok(())
}
