/*
    Hurry Curry! - a game about cooking
    Copyright (C) 2026 Hurry Curry! Contributors

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, version 3 of the License only.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

*/

use crate::Event;
use anyhow::Context;
use async_compression::tokio::write::ZstdEncoder;
use futures_util::{SinkExt, StreamExt};
use hurrycurry_protocol::{PacketC, PacketS};
use log::{debug, error, info};
use std::{
    io::{Write, stderr},
    path::Path,
    time::Instant,
};
use tokio::{
    fs::File,
    io::{AsyncWriteExt, BufWriter},
};
use tokio_tungstenite::tungstenite::{self, Message};

pub async fn record(output: &Path, url: &str) -> anyhow::Result<()> {
    let mut file = BufWriter::new(ZstdEncoder::new(BufWriter::new(
        File::create(&output).await?,
    )));
    info!("connecting to {url:?}...");
    let (mut sock, _) = tokio_tungstenite::connect_async(url).await?;
    info!("starting recording.");
    let start = Instant::now();
    let mut counter = 0;
    let mut last_keepalive = Instant::now();

    while let Some(Ok(message)) = sock.next().await {
        match message {
            tungstenite::Message::Text(line) => {
                let packet: PacketC = serde_json::from_str(&line).context("invalid packet")?;
                debug!("<- {packet:?}");

                let is_end = matches!(packet, PacketC::SetIngame { state: false, .. });
                let is_start = matches!(packet, PacketC::SetIngame { state: true, .. });

                if is_start {
                    sock.send(Message::Text(
                        serde_json::to_string(&PacketS::Ready).unwrap().into(),
                    ))
                    .await?;
                }
                if last_keepalive.elapsed().as_secs_f32() > 1. {
                    last_keepalive = Instant::now();
                    sock.send(Message::Text(
                        serde_json::to_string(&PacketS::Keepalive).unwrap().into(),
                    ))
                    .await?;
                }

                file.write_all(
                    format!(
                        "{}\n",
                        serde_json::to_string(&Event {
                            ts: start.elapsed().as_secs_f64(),
                            packet
                        })
                        .unwrap()
                    )
                    .as_bytes(),
                )
                .await?;

                counter += 1;
                eprint!("{counter} packets received\r");
                stderr().flush().unwrap();

                if is_end {
                    info!("stopping replay...");
                    break;
                }
            }
            tungstenite::Message::Close(_) => {
                error!("connection closed by server");
                break;
            }
            _ => (),
        }
    }
    file.flush().await?;
    let mut file = file.into_inner();
    file.flush().await?;
    let mut file = file.into_inner();
    file.flush().await?;
    let mut file = file.into_inner();
    file.flush().await?;
    info!("done");
    Ok(())
}
