use std::error::Error;

use plaza_lib::connect::{
    Connection, Manager, ReadMessageResult, Reader, WriteMessageResult, Writer,
};
use plaza_lib::reply::Reply;
use plaza_lib::request::Request;

/// The server's configuration, filled in from the command line by
/// `main.rs`. The autotests set these flags, so make sure your server
/// honours them.
#[derive(Debug, Clone, Copy)]
pub struct Config {
    /// Canvas width, in pixels.
    pub width: u32,
    /// Canvas height, in pixels.
    pub height: u32,
    /// After a write commits, the writer may not write again for
    /// `cooldown_ms * (number of pixels written)` Plaza-clock
    /// milliseconds. 0 disables rate limiting entirely.
    pub cooldown_ms: u64,
    /// The maximum number of diffs the server may hold queued for one
    /// subscriber. A subscriber that would exceed it gets "lagged"
    /// instead, and loses its subscription.
    pub lag_budget: usize,
}

pub fn start_server<M>(mut manager: M, config: Config) -> Result<(), Box<dyn Error>>
where
    M: Manager,
{
    // This starter code initiates a single client connection, and reads and
    // writes messages indefinitely.
    // You will need to implement multiple connections for Stage 2 onwards.
    let (mut recv, mut send) = match manager.accept_new_connection() {
        Connection::NewConnection { reader, writer } => (reader, writer),
        Connection::NoMoreConnections => {
            // There are no more new connections to accept.
            return Ok(());
        }
    };

    loop {
        match recv.read_message() {
            ReadMessageResult::Message(message) => {
                let reply = match message.parse::<Request>() {
                    Ok(request) => match request {
                        Request::Paint { cell } => {
                            todo!("paint {cell:?}, reply Reply::Painted")
                        }
                        Request::Read { position } => {
                            todo!("reply Reply::Pixel with the color at {position:?}")
                        }
                        Request::Snapshot { region } => {
                            todo!("reply Reply::Snapshot for {region:?} (None = whole canvas)")
                        }
                        Request::Stamp { region, colors } => {
                            todo!("apply {colors:?} across {region:?} atomically")
                        }
                        Request::Subscribe { region } => {
                            todo!("ack Reply::Subscribed, then send diffs for {region:?}")
                        }
                        Request::Unsubscribe => {
                            todo!("stop sending diffs, ack Reply::Unsubscribed")
                        }
                    },
                    Err(err) => Reply::Error(err.to_string()),
                };

                match send.write_message(reply) {
                    WriteMessageResult::Ok => {
                        // Message successfully sent, continue.
                    }
                    WriteMessageResult::ConnectionClosed => {
                        // The connection was closed. This is not an error,
                        // but should terminate this connection.
                        break;
                    }
                    WriteMessageResult::Err(e) => {
                        // An unexpected error was encountered.
                        return Err(Box::new(e));
                    }
                }
            }
            ReadMessageResult::ConnectionClosed => {
                // The connection was closed. This is not an error, but
                // should terminate this connection.
                break;
            }
            ReadMessageResult::Err(e) => {
                // An unexpected error was encountered.
                return Err(Box::new(e));
            }
        }
    }

    Ok(())
}
