use std::time::SystemTime;
use log::{info};

use elseware::patch::patch::{PatchServerState, generate_patch_tree, load_config, load_motd};
use elseware::login::login::LoginServerState;
use elseware::login::character::CharacterServerState;
use elseware::ship::ship::ShipServerState;
use elseware::entity::account::{NewUserAccountEntity, NewUserSettingsEntity};
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::entity::character::NewCharacterEntity;
use elseware::entity::item::{NewItemEntity, ItemDetail, ItemLocation};

use elseware::entity::item;

fn setup_logger() {
    let colors = fern::colors::ColoredLevelConfig::new()
        .error(fern::colors::Color::Red)
        .warn(fern::colors::Color::Yellow)
        .info(fern::colors::Color::Green)
        .debug(fern::colors::Color::White)
        .trace(fern::colors::Color::BrightBlack);
    let stdio = fern::Dispatch::new()
        .level(log::LevelFilter::Debug)
        .format(move |out, message, record| {
            out.finish(format_args!(
                "\x1B[{}m[{}][{}][{}] {}\x1B[0m",
                colors.get_color(&record.level()).to_fg_str(),
                chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
                record.target(),
                record.level(),
                message,
            ))
        })
        .chain(std::io::stdout());
    let fileout = fern::Dispatch::new()
        .level(log::LevelFilter::Trace)
        .chain(fern::log_file(format!("log/elseware-{}.log", chrono::Local::now().format("%Y-%m-%d_%H-%M-%S"))).unwrap());
    fern::Dispatch::new()
        .chain(stdio)
        .chain(fileout)
        .apply().unwrap();
}


fn main() {
    setup_logger();
    async_std::task::block_on(async move {
        let mut entity_gateway = InMemoryGateway::new();

        for i in 0..5 {
            let fake_user = NewUserAccountEntity {
                username: if i == 0 { "hi".to_string() } else { format!("hi{}", i+1) },
                password: bcrypt::hash("qwer", 5).unwrap(),
                guildcard: i + 1,
                team_id: None,
                banned: false,
                muted_until: SystemTime::now(),
                created_at: SystemTime::now(),
                flags: 0,
            };
            let fake_user = entity_gateway.create_user(fake_user).await.unwrap();
            entity_gateway.create_user_settings(NewUserSettingsEntity::new(fake_user.id)).await;
            let mut character = NewCharacterEntity::new(fake_user.id);
            character.name = format!("Test Char {}", i*2);
            entity_gateway.create_character(character).await;
            let mut character = NewCharacterEntity::new(fake_user.id);
            character.slot = 2;
            character.name = "ItemRefactor".into();
            character.exp = 80000000;
            character.meseta = 999999;
            let character = entity_gateway.create_character(character).await.unwrap();

            entity_gateway.create_item(
                NewItemEntity {
                    item: ItemDetail::Weapon(
                        item::weapon::Weapon {
                            weapon: item::weapon::WeaponType::Raygun,
                            grind: 5,
                            special: Some(item::weapon::WeaponSpecial::Hell),
                            attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}),
                                    Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
                                    None,],
                            tekked: true,
                        }
                    ),
                    location: ItemLocation::Inventory {
                        character_id: character.id,
                        slot: 0,
                        equipped: false,
                    }
                }).await;
            entity_gateway.create_item(
                NewItemEntity {
                    item: ItemDetail::Weapon(
                        item::weapon::Weapon {
                            weapon: item::weapon::WeaponType::Handgun,
                            grind: 5,
                            special: Some(item::weapon::WeaponSpecial::Charge),
                            attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}),
                                    Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
                                    None,],
                            tekked: true,
                        }
                    ),
                    location: ItemLocation::Inventory {
                        character_id: character.id,
                        slot: 1,
                        equipped: false,
                    }
                }).await;
            entity_gateway.create_item(
                NewItemEntity {
                    item: ItemDetail::Weapon(
                        item::weapon::Weapon {
                            weapon: item::weapon::WeaponType::Vjaya,
                            grind: 5,
                            special: Some(item::weapon::WeaponSpecial::Charge),
                            attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}),
                                    Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
                                    None,],
                            tekked: true,
                        }
                    ),
                    location: ItemLocation::Inventory {
                        character_id: character.id,
                        slot: 2,
                        equipped: true,
                    }
                }).await;
            entity_gateway.create_item(
                NewItemEntity {
                    item: ItemDetail::Weapon(
                        item::weapon::Weapon {
                            weapon: item::weapon::WeaponType::Vulcan,
                            grind: 5,
                            special: Some(item::weapon::WeaponSpecial::Charge),
                            attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}),
                                    Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
                                    None,],
                            tekked: true,
                        }
                    ),
                    location: ItemLocation::Inventory {
                        character_id: character.id,
                        slot: 3,
                        equipped: true,
                    }
                }).await;
            entity_gateway.create_item(
                NewItemEntity {
                    item: ItemDetail::Weapon(
                        item::weapon::Weapon {
                            weapon: item::weapon::WeaponType::DarkFlow,
                            grind: 5,
                            special: Some(item::weapon::WeaponSpecial::Charge),
                            attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}),
                                    Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
                                    Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
                            tekked: true,
                        }
                    ),
                    location: ItemLocation::Inventory {
                        character_id: character.id,
                        slot: 4,
                        equipped: true,
                    }
                }).await;
            entity_gateway.create_item(
                NewItemEntity {
                    item: ItemDetail::Weapon(
                        item::weapon::Weapon {
                            weapon: item::weapon::WeaponType::Autogun,
                            grind: 5,
                            special: Some(item::weapon::WeaponSpecial::Hell),
                            attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 70}),
                                    Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 80}),
                                    None,],
                            tekked: false,
                        }
                    ),
                    location: ItemLocation::Bank {
                        character_id: character.id,
                        name: item::BankName("".to_string()),
                    }
                }).await;
        }

        let patch = async_std::task::spawn(async {
            info!("[patch] starting server");
            let patch_config = load_config();
            let patch_motd = load_motd();
            let (patch_file_tree, patch_file_lookup) = generate_patch_tree(patch_config.path.as_str());
            let patch_state = PatchServerState::new(patch_file_tree, patch_file_lookup, patch_motd);

            elseware::common::mainloop::mainloop_async(patch_state, patch_config.port).await;
        });

        let thread_entity_gateway = entity_gateway.clone();
        let auth = async_std::task::spawn(async {
            info!("[auth] starting server");
            let auth_state = LoginServerState::new(thread_entity_gateway);

            elseware::common::mainloop::mainloop_async(auth_state, elseware::login::login::LOGIN_PORT).await;
        });

        let thread_entity_gateway = entity_gateway.clone();
        let character = async_std::task::spawn(async {
            info!("[character] starting server");
            let char_state = CharacterServerState::new(thread_entity_gateway);

            elseware::common::mainloop::mainloop_async(char_state, elseware::login::character::CHARACTER_PORT).await;
        });

        let thread_entity_gateway = entity_gateway.clone();
        let ship = async_std::task::spawn(async {
            info!("[ship] starting server");
            let ship_state = ShipServerState::new(thread_entity_gateway);
            elseware::common::mainloop::mainloop_async(ship_state, elseware::ship::ship::SHIP_PORT).await;
        });

        futures::join!(patch, auth, character, ship);
    });
}