Documentation
use std::io;
use std::path::Path;
use std::time::Duration;

use rand::{thread_rng, Rng};
use ricq::{LoginResponse, RQError};
use tokio::fs;
use tokio::io::AsyncWriteExt;
use tracing::{error, info, warn};

use crate::client::BotConfiguration;
use crate::config::login::{LoginConfig, DEFAULT_CONFIG};
use crate::error::AtriResult;
use crate::{config, global_status, Client};

pub async fn login_clients() -> Result<(), RQError> {
    let login_conf_dir = {
        let path = config::service_config_dir_path();
        if !path.is_dir() {
            fs::create_dir_all(&path).await?;
        }
        path
    }
    .join("login.toml");

    let login_conf = {
        async fn default_config_write<P: AsRef<Path>>(path: P) -> io::Result<LoginConfig> {
            fs::write(path, DEFAULT_CONFIG).await?;

            let default_config = LoginConfig::default();
            Ok(default_config)
        }

        if login_conf_dir.is_file() {
            let s = fs::read_to_string(&login_conf_dir).await?;

            match toml::from_str(&s) {
                Ok(conf) => conf,
                Err(e) => {
                    error!("读取登陆配置文件失败: {}", e);

                    let cp = config::service_config_dir_path().join("login.toml.bak");

                    fs::copy(&login_conf_dir, cp).await?;
                    default_config_write(&login_conf_dir).await?
                }
            }
        } else {
            default_config_write(login_conf_dir).await?
        }
    };

    let clients_path = config::clients_dir_path();
    if !clients_path.is_dir() {
        fs::create_dir(&clients_path).await?;
    }
    let mut logins = vec![];
    for client in login_conf.clients {
        if !client.auto_login {
            continue;
        }

        let account = client.account;
        let pwd = client.password;

        let bot_path = clients_path.join(account.to_string()).join("device.json");
        if !bot_path.is_file() {
            warn!("未找到Client({})的登陆信息,跳过登陆", account);
            continue;
        }

        let handle = tokio::spawn(async move {
            match login_client(
                account,
                &pwd,
                BotConfiguration {
                    work_dir: None,
                    version: client
                        .protocol
                        .unwrap_or(login_conf.default_protocol)
                        .as_version(),
                },
            )
            .await
            {
                Ok(client) => {
                    global_status().add_client(client.clone());
                    info!("{}登陆成功", client);
                    if let Err(e) = client.refresh_friend_list().await {
                        warn!("{}刷新好友列表失败: {:?}", client, e);
                    }
                    if let Err(e) = client.refresh_group_list().await {
                        warn!("{}刷新群列表失败: {:?}", client, e);
                    }
                    Ok(client)
                }
                Err(e) => {
                    global_status().remove_client(account);
                    error!("Client({})登录失败: {}", account, e);
                    Err(e)
                }
            }
        });
        logins.push(handle);

        let random = { thread_rng().gen_range(0..44) as f32 / 11.2f32 };
        tokio::time::sleep(Duration::from_secs_f32(random)).await;
    }

    for result in logins {
        let _ = result.await;
    }

    Ok(())
}

async fn login_client(
    account: i64,
    password: &Option<String>,
    conf: BotConfiguration,
) -> AtriResult<Client> {
    let client = Client::new(account, conf).await;
    client.start().await?;

    info!("Client({})登陆中", account);
    match client.try_login().await {
        Ok(_) => Ok(client),
        Err(e) => {
            if let Some(pwd) = password {
                info!("{}尝试密码登陆", client);
                let mut resp = client.request_client().password_login(account, pwd).await?;

                loop {
                    match resp {
                        LoginResponse::DeviceLockLogin(..) => {
                            resp = client.request_client().device_lock_login().await?;
                        }
                        LoginResponse::Success(..) => {
                            let tokenp = client.work_dir().join("token.json");

                            if let Ok(mut f) = fs::File::create(&tokenp).await {
                                let token = client.request_client().gen_token().await;
                                let s = serde_json::to_string_pretty(&token)
                                    .expect("Cannot serialize token");
                                let _ = f.write_all(s.as_bytes()).await;
                            }

                            break;
                        }
                        LoginResponse::UnknownStatus(ref s) => {
                            error!("{}登陆失败: {}", client, s.message);
                            return Err(e);
                        }
                        LoginResponse::AccountFrozen => {
                            error!("{}登陆失败: 账号被冻结", client);
                            return Err(e);
                        }
                        or => {
                            error!("{}登陆失败, 服务器返回: {:?}", client, or);
                            return Err(e);
                        }
                    }
                }

                Ok(client)
            } else {
                error!("{}登陆失败: {:?}", client, e);
                Err(e)
            }
        }
    }
}