Documentation
pub mod robot;

use crate::robot_service::robot::Robot;
use crate::services::LotteryInfo;
use crate::static_def::{game_slot_bet_ratios, get_game_setting, CONFIG, DATABASE};
use anyhow::Result;
use aqueue::Actor;
use rand::prelude::IndexedRandom;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha20Rng;
use std::time::Instant;

/// 机器人服务调用接口
pub trait IRobotService {
    /// 机器人坐位置
    async fn robot_seat(
        &self,
        robot_id: i32,
        nickname: String,
        avatar_id: i32,
    ) -> Option<(usize, usize)>;
    /// 机器人离开位置
    async fn robot_remove_seat(&self, table_id: usize, seat_index: usize) -> Result<()>;
    /// 获取彩金信息
    async fn get_lottery_info(&self) -> Vec<LotteryInfo>;
}

/// 机器人管理器
pub struct RobotManager {
    /// 随机数
    rand: ChaCha20Rng,
    /// 机器人容器
    robots: Vec<Robot>,
}

/// 为 `RobotManager` 实现 `Default` trait,允许通过 `RobotManager::default()` 创建默认实例。
impl Default for RobotManager {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl RobotManager {
    pub fn new() -> Self {
        let rand = ChaCha20Rng::from_os_rng();
        Self {
            rand,
            robots: vec![],
        }
    }

    /// 装载机器人
    async fn init(&mut self, robot_service: &impl IRobotService) -> Result<()> {
        if get_game_setting().robot_max == 0 {
            log::info!("not enable robot_service");
            return Ok(());
        }

        if get_game_setting().robot_max < get_game_setting().robot_min {
            log::error!("robot_service max < min");
            return Ok(());
        }

        let robot_size = self
            .rand
            .random_range(get_game_setting().robot_min..=get_game_setting().robot_max);

        let max_vip_level = DATABASE.get_max_vip_level().await?;

        self.robots = (0..robot_size)
            .map(|_| {
                let id = self.rand.random_range(10000000..100000000);
                let seating_time = self.rand.random_range(
                    CONFIG.game.robot_seating_min_time..=CONFIG.game.robot_seating_max_time,
                );
                let leave_time = self.rand.random_range(
                    CONFIG.game.robot_leave_min_time..=CONFIG.game.robot_seating_max_time,
                );
                let spin_time = self.rand.random_range(
                    CONFIG.game.robot_spin_min_time..=CONFIG.game.robot_spin_max_time,
                );
                Robot {
                    id,
                    nickname: id.to_string(),
                    avatar_id: self.rand.random_range(0..10),
                    vip_level: self.rand.random_range(0..=max_vip_level),
                    bet_info: game_slot_bet_ratios()
                        .choose(&mut self.rand)
                        .unwrap()
                        .clone(),
                    seating_time,
                    leave_time,
                    spin_time,
                    last_seating_time: Instant::now(),
                    last_leave_time: Instant::now(),
                    last_spin_time_time: Instant::now(),
                    state: Default::default(),
                }
            })
            .collect();

        if robot_service.get_lottery_info().await.is_empty() {
            log::debug!(
                "robot_service not found lottery id by:{}",
                get_game_setting().game_id
            );
        }

        log::debug!("robot_service init ok size:{robot_size}");
        Ok(())
    }

    /// 运行机器人
    #[inline]
    async fn update(&mut self, robot_service: &impl IRobotService) -> Result<()> {
        let lottery_info = robot_service.get_lottery_info().await;
        for robot in self.robots.iter_mut() {
            robot
                .update(&mut self.rand, &lottery_info, robot_service)
                .await?;
        }
        Ok(())
    }
}

pub trait IRobotManager {
    ///装载机器人
    async fn init(&self, robot_service: &impl IRobotService) -> Result<()>;
    ///帧触发
    async fn update(&self, robot_service: &impl IRobotService) -> Result<()>;
}

impl IRobotManager for Actor<RobotManager> {
    #[inline]
    async fn init(&self, robot_service: &impl IRobotService) -> Result<()> {
        self.inner_call(|inner| async move { inner.get_mut().init(robot_service).await })
            .await
    }

    #[inline]
    async fn update(&self, robot_service: &impl IRobotService) -> Result<()> {
        self.inner_call(|inner| async move { inner.get_mut().update(robot_service).await })
            .await
    }
}