/*
    Hurry Curry! - a game about cooking
    Copyright 2024 metamuffin
    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 .
*/
use crate::server::Server;
use anyhow::{bail, Result};
use hurrycurry_protocol::{registry::Submission, VERSION};
use log::{info, warn};
use rand::random;
use reqwest::{header::USER_AGENT, Client, Url};
use std::{
    net::{IpAddr, Ipv4Addr, Ipv6Addr},
    str::FromStr,
    sync::Arc,
    time::Duration,
};
use tokio::{sync::RwLock, time::interval};
const REGISTRY_URI: &'static str = "https://hurrycurry-registry.metamuffin.org";
pub struct Register {
    name: String,
    port: u16,
    register_uri: Option,
    state: Arc>,
    inet_client: Client,
    ip4_client: Client,
    ip6_client: Client,
    secret: u128,
    players: usize,
}
impl Register {
    pub fn new(
        name: String,
        port: u16,
        register_uri: Option,
        state: Arc>,
    ) -> Self {
        Self {
            name,
            register_uri,
            players: 0,
            port,
            secret: random(),
            state,
            inet_client: Client::new(),
            ip4_client: Client::builder()
                .local_address(IpAddr::V4(Ipv4Addr::UNSPECIFIED))
                .build()
                .unwrap(),
            ip6_client: Client::builder()
                .local_address(IpAddr::V6(Ipv6Addr::UNSPECIFIED))
                .build()
                .unwrap(),
        }
    }
    pub async fn register_loop(mut self) {
        let mut interval = interval(Duration::from_secs(60));
        loop {
            interval.tick().await;
            self.players = self.state.read().await.count_chefs();
            if let Err(e) = self.register().await {
                warn!("register error: {e}")
            }
        }
    }
    pub async fn register(&self) -> Result<()> {
        info!("register update");
        if let Some(uri) = &self.register_uri {
            self.register_with("uri", &self.inet_client, uri.to_owned())
                .await?;
        } else {
            let (v4, v6) = tokio::join!(
                self.register_with(
                    "ip4",
                    &self.ip4_client,
                    format!("ws://0.0.0.0:{}", self.port),
                ),
                self.register_with(
                    "ip6",
                    &self.ip6_client,
                    format!("ws://0.0.0.0:{}", self.port),
                )
            );
            info!("v4: {v4:?}");
            info!("v6: {v6:?}");
        }
        Ok(())
    }
    // TODO ip v6
    pub async fn register_with(&self, label: &str, client: &Client, uri: String) -> Result<()> {
        let res = client
            .post(Url::from_str(&format!("{REGISTRY_URI}/v1/register")).unwrap())
            .header(
                USER_AGENT,
                format!("hurrycurry-server {}", env!("CARGO_PKG_VERSION")),
            )
            .json(&Submission {
                last_game: 0,
                name: self.name.clone(),
                uri,
                players: self.players,
                secret: self.secret,
                version: VERSION,
            })
            .send()
            .await?;
        let r = res.text().await?;
        if r == "ok" {
            info!("register ok ({label})");
            Ok(())
        } else {
            bail!("{r}");
        }
    }
}