use std::{cell::RefCell, collections::HashSet, rc::Rc, sync::Arc};

use basic_info::PlayerBasicInfo;
use common::time_util;
use explore_tools::ExploreTools;
use location::PlayerLocation;
use player_func::PlayerFunc;
use shorekeeper_protocol::{
    message::Message, ItemPkgOpenNotify, PbGetRoleListNotify, PlayerBasicData, PlayerRoleData,
    PlayerSaveData, ProtocolUnit,
};

use crate::session::Session;

use super::{
    ecs::world::World,
    role::{Role, RoleFormation},
};

mod basic_info;
mod explore_tools;
mod in_world_player;
mod location;
mod player_func;

pub use in_world_player::InWorldPlayer;

pub struct Player {
    session: Option<Arc<Session>>,
    // Persistent
    pub basic_info: PlayerBasicInfo,
    pub role_list: Vec<Role>,
    pub formation_list: Vec<RoleFormation>,
    pub location: PlayerLocation,
    pub func: PlayerFunc,
    pub explore_tools: ExploreTools,
    // Runtime
    pub world: Rc<RefCell<World>>,
    pub last_save_time: u64,
}

impl Player {
    pub fn init(&mut self) {
        if self.role_list.is_empty() {
            self.on_first_enter();
        }

        // we need shorekeeper
        // TODO: remove this part after implementing team switch
        if !self.role_list.iter().any(|r| r.role_id == 1505) {
            let mut shorekeeper = Role::new(1505);
            shorekeeper.equip_weapon = 21050036;
            self.role_list.push(shorekeeper);
        }

        self.formation_list.clear();
        self.formation_list.push(RoleFormation {
            id: 1,
            cur_role: 1505,
            role_id_set: HashSet::from([1505]),
            is_current: true,
        });
        // End shorekeeper hardcode part

        self.ensure_current_formation();
        self.ensure_basic_unlock_func();
    }

    pub fn notify_general_data(&self) {
        self.notify(self.basic_info.build_notify());
        self.notify(self.func.build_func_open_notify());
        self.notify(self.build_role_list_notify());
        self.notify(self.explore_tools.build_explore_tool_all_notify());
        self.notify(self.explore_tools.build_roulette_update_notify());

        self.notify(ItemPkgOpenNotify {
            open_pkg: (0..8).collect(),
        });
    }

    fn on_first_enter(&mut self) {
        self.role_list.push(Self::create_main_character_role(
            self.basic_info.name.clone(),
            self.basic_info.sex,
        ));

        let role = &self.role_list[0];

        self.formation_list.push(RoleFormation {
            id: 1,
            cur_role: role.role_id,
            role_id_set: HashSet::from([role.role_id]),
            is_current: true,
        });

        self.location = PlayerLocation::default();
    }

    // Ensure basic functionality is unlocked
    // Should be handled by quest progression,
    // but as of right now, just unlock what we need
    fn ensure_basic_unlock_func(&mut self) {
        self.func.unlock(10026); // explore tools
    }

    fn ensure_current_formation(&mut self) {
        if self.formation_list.is_empty() {
            let role = &self.role_list[0];

            self.formation_list.push(RoleFormation {
                id: 1,
                cur_role: role.role_id,
                role_id_set: HashSet::from([role.role_id]),
                is_current: true,
            });
        }

        if !self.formation_list.iter().any(|rf| rf.is_current) {
            self.formation_list[0].is_current = true;
        }

        if let Some(rf) = self.formation_list.iter_mut().find(|rf| rf.is_current) {
            if rf.role_id_set.is_empty() {
                rf.role_id_set.insert(self.role_list[0].role_id);
            }

            if !rf.role_id_set.contains(&rf.cur_role) {
                rf.cur_role = *rf.role_id_set.iter().nth(0).unwrap();
            }
        }
    }

    pub fn build_in_world_player(&self) -> InWorldPlayer {
        InWorldPlayer {
            player_id: self.basic_info.id,
            player_name: self.basic_info.name.clone(),
            player_icon: 0,
            level: self.basic_info.level,
            group_type: 1,
        }
    }

    pub fn get_current_formation_role_list(&self) -> Vec<&Role> {
        self.formation_list
            .iter()
            .find(|rf| rf.is_current)
            .unwrap()
            .role_id_set
            .iter()
            .flat_map(|id| self.role_list.iter().find(|r| r.role_id == *id))
            .collect()
    }

    pub fn get_cur_role_id(&self) -> i32 {
        self.formation_list
            .iter()
            .find(|rf| rf.is_current)
            .unwrap()
            .cur_role
    }

    pub fn load_from_save(save_data: PlayerSaveData) -> Self {
        let role_data = save_data.role_data.unwrap_or_default();

        Self {
            session: None,
            basic_info: PlayerBasicInfo::load_from_save(save_data.basic_data.unwrap_or_default()),
            role_list: role_data
                .role_list
                .into_iter()
                .map(Role::load_from_save)
                .collect(),
            formation_list: role_data
                .role_formation_list
                .into_iter()
                .map(RoleFormation::load_from_save)
                .collect(),
            location: save_data
                .location_data
                .map(PlayerLocation::load_from_save)
                .unwrap_or_default(),
            func: save_data
                .func_data
                .map(PlayerFunc::load_from_save)
                .unwrap_or_default(),
            explore_tools: save_data
                .explore_tools_data
                .map(ExploreTools::load_from_save)
                .unwrap_or_default(),
            world: Rc::new(RefCell::new(World::new())),
            last_save_time: time_util::unix_timestamp(),
        }
    }

    pub fn build_save_data(&self) -> PlayerSaveData {
        PlayerSaveData {
            basic_data: Some(self.basic_info.build_save_data()),
            role_data: Some(PlayerRoleData {
                role_list: self.role_list.iter().map(|r| r.build_save_data()).collect(),
                role_formation_list: self
                    .formation_list
                    .iter()
                    .map(|rf| rf.build_save_data())
                    .collect(),
            }),
            location_data: Some(self.location.build_save_data()),
            func_data: Some(self.func.build_save_data()),
            explore_tools_data: Some(self.explore_tools.build_save_data()),
        }
    }

    pub fn set_session(&mut self, session: Arc<Session>) {
        self.session = Some(session);
    }

    pub fn build_role_list_notify(&self) -> PbGetRoleListNotify {
        PbGetRoleListNotify {
            role_list: self.role_list.iter().map(|r| r.to_protobuf()).collect(),
        }
    }

    pub fn notify(&self, content: impl ProtocolUnit) {
        if let Some(session) = self.session.as_ref() {
            session.forward_to_gateway(Message::Push {
                sequence_number: 0,
                message_id: content.get_message_id(),
                payload: Some(content.encode_to_vec().into_boxed_slice()),
            });
        }
    }

    pub fn respond(&self, content: impl ProtocolUnit, rpc_id: u16) {
        if let Some(session) = self.session.as_ref() {
            session.forward_to_gateway(Message::Response {
                sequence_number: 0,
                message_id: content.get_message_id(),
                rpc_id,
                payload: Some(content.encode_to_vec().into_boxed_slice()),
            });
        }
    }

    fn create_main_character_role(name: String, sex: i32) -> Role {
        let mut role = match sex {
            0 => Role::new(Role::MAIN_CHARACTER_FEMALE_ID),
            1 => Role::new(Role::MAIN_CHARACTER_MALE_ID),
            _ => unreachable!(),
        };

        role.name = name;
        role
    }

    pub fn create_default_save_data(id: i32, name: String, sex: i32) -> PlayerSaveData {
        PlayerSaveData {
            basic_data: Some(PlayerBasicData {
                id,
                name,
                sex,
                level: 1,
                head_photo: 1505,
                head_frame: 80060009,
                ..Default::default()
            }),
            ..Default::default()
        }
    }
}
