use crate::{
    core::{Skill, MAX_SKILL, NUM_GAME_POSITIONS},
    game_engine::MIN_TIREDNESS_FOR_ROLL_DECLINE,
    types::{PlanetId, TeamId, Tick},
};
use std::sync::LazyLock;
use uuid::uuid;

// DEBUG_TIME_MULTIPLIER should be between 1 and 1000;
pub const DEBUG_TIME_MULTIPLIER: Tick = 1;
pub const MILLISECONDS: Tick = 1;
pub const SECONDS: Tick = 1000 * MILLISECONDS / DEBUG_TIME_MULTIPLIER;
pub const MINUTES: Tick = 60 * SECONDS;
pub const HOURS: Tick = 60 * MINUTES;
pub const DAYS: Tick = 24 * HOURS;
pub const WEEKS: Tick = 7 * DAYS;

// A Kilometer represents a unit of distance in the game world.
// It corresponds to a kilometer in the real world.
pub type KILOMETER = u64;
pub const KILOMETERS: KILOMETER = 1;
pub const AU: KILOMETER = 149_597_870_700 * KILOMETERS;
pub const LIGHT_YEAR: KILOMETER = 9_460_730_472_580_800 * KILOMETERS;

pub const SATOSHI_PER_BITCOIN: u32 = 100_000_000;

// The CALENDAR_OFFSET is the number of years to add to the current year
// to get the year in the game world.
pub const CALENDAR_OFFSET: i32 = 77;

pub const MIN_PLAYERS_PER_GAME: usize = 5;
// Max number of players that can participate in a game. Other crew members beyond this limit are not part of the team during the game.
pub const MAX_PLAYERS_PER_GAME: usize = MIN_PLAYERS_PER_GAME + 2;
// The following value is never directly used, but we should ensure that no spaceship allows for a crew size larger than this.
pub const MAX_CREW_SIZE: usize = MIN_PLAYERS_PER_GAME + 5;

// Try to keep these constants balanced.
// By playing a game, a player can get at most 5*40*60 = 12000 (5 because of the role, times 40 minutes in seconds).
// Then, with potential, focus, and doctor bonus, per game a skill can improve at most
// by 12000 * 8 * EXPERIENCE_PER_SKILL_MULTIPLIER ~ 0.4.
// More realistically, one should take a factor 10 less.
pub const EXPERIENCE_PER_SKILL_MULTIPLIER: f32 = 0.0000085;
pub const MAX_SKILL_INCREASE_PER_LONG_TICK: f32 = 0.3;
pub const SKILL_DECREMENT_PER_LONG_TICK: f32 = -0.05;
pub const GAME_POSITION_DECREMENT_PER_LONG_TICK: f32 = -0.025;

pub const AVG_SKILL_TO_POTENTIAL_MODIFIER: f32 = 1.3;

pub const TOURNAMENT_GAME_TRAINING_BONUS_MODIFIER: f32 = 0.45;

pub const REPUTATION_PER_EXPERIENCE: f32 = 0.0001;
pub const REPUTATION_DECREASE_PER_LONG_TICK: f32 = -0.15;
pub const MIN_REPUTATION_PER_HONOUR: f32 = 1.0;

pub const AGE_INCREASE_PER_LONG_TICK: f32 = 0.1; // 1 year every 10 LONG_TICK

pub const BASE_ATTENDANCE: f32 = 60.0;
pub const BASE_INCOME: u32 = 500;
pub const INCOME_PER_ATTENDEE: u32 = 24;
pub const INITIAL_RANDOM_TEAM_BALANCE: u32 = 25_000;
pub const INITIAL_OWN_TEAM_BALANCE: u32 = 100_000;

pub const AUTO_GENERATE_GAMES_NUMBER: usize = 24;
pub const MAX_AVG_TIREDNESS_PER_AUTO_GAME: f32 = 4.5;
pub const MAX_AVG_TIREDNESS_PER_CHALLENGED_GAME: f32 = MIN_TIREDNESS_FOR_ROLL_DECLINE;
pub const SPACE_ADVENTURE_TIREDNESS_COST: f32 = TirednessCost::CRITICAL;
pub const MAX_AVG_TIREDNESS_PER_SPACE_ADVENTURE: f32 = MAX_SKILL - SPACE_ADVENTURE_TIREDNESS_COST;

pub const BASE_DISTANCES: [u64; 5] = [
    LIGHT_YEAR,
    AU,
    400_000 * KILOMETERS,
    80_000 * KILOMETERS,
    1_000 * KILOMETERS,
];
pub const BASE_TANK_CAPACITY: u32 = 60;
pub const SPACESHIP_BASE_COST_MULTIPLIER: f32 = 1.2;

pub const LIGHT_SPEED: f32 = 1_079_252_848.8 * KILOMETERS as f32 / HOURS as f32;
pub const BASE_SPEED: f32 = 0.55 * LIGHT_SPEED; // Very fast ;)
pub const BASE_FUEL_CONSUMPTION: f32 = 1.05 / HOURS as f32; // TONNES per HOURS
pub const FUEL_CONSUMPTION_PER_UNIT_STORAGE: f32 = 1.0 / 3_000.0; // 3_000 storage units double the fuel consumption
pub const SPEED_PENALTY_PER_UNIT_STORAGE: f32 = 1.0 / 6_000.0; // 6_000 storage units halves the speed

pub const LANDING_TIME_OVERHEAD: Tick = 10 * MINUTES;
pub const EXPLORATION_DURATION: Tick = 8 * HOURS;

pub const ASTEROID_DISCOVERY_PROBABILITY: f64 = 0.15;
pub const TRAIT_PROBABILITY: f64 = 0.25;

pub const PORTAL_TRAVEL_DURATION: Tick = 10 * SECONDS;
// This is used to check if a travel was done using a portal or teleporting.
pub const TELEPORT_TRAVEL_DURATION: Tick = 0;

pub const MAX_NUM_ASTEROID_PER_TEAM: usize = 7;

pub struct TickInterval;
impl TickInterval {
    pub const SHORT: Tick = SECONDS;
    pub const MEDIUM: Tick = MINUTES;
    pub const LONG: Tick = 24 * HOURS;
}

pub const GAME_START_DELAY: Tick = 20 * SECONDS;
pub const NETWORK_GAME_START_DELAY: Tick = 30 * SECONDS;

static GALAXY_ROOT_STR: &str = "71a43700-0000-0000-0000-000000000000";
static DEFAULT_PLANET_STR: &str = "71a43700-0000-0000-0002-000000000000";
static SOL_STR: &str = "71a43700-0000-0000-0001-000000000000";
pub static GALAXY_ROOT_ID: LazyLock<PlanetId> = LazyLock::new(|| {
    PlanetId::try_parse(GALAXY_ROOT_STR).expect("GALAXY_ROOT_STR must be a valid PlanetId")
});
pub static DEFAULT_PLANET_ID: LazyLock<PlanetId> = LazyLock::new(|| {
    PlanetId::try_parse(DEFAULT_PLANET_STR).expect("DEFAULT_PLANET_STR must be a valid PlanetId")
});
pub static SOL_ID: LazyLock<PlanetId> =
    LazyLock::new(|| PlanetId::try_parse(SOL_STR).expect("SOL_STR must be a valid PlanetId"));

pub struct TirednessCost;
impl TirednessCost {
    pub const NONE: f32 = 0.0;
    pub const LOW: f32 = 0.005;
    pub const MEDIUM: f32 = 0.2;
    pub const HIGH: f32 = 0.5;
    pub const SEVERE: f32 = 2.5;
    pub const CRITICAL: f32 = 5.0;
    pub const MAX: f32 = 20.0;
}

pub struct MoraleModifier;
impl MoraleModifier {
    pub const CRITICAL_MALUS: f32 = -10.0;
    pub const SEVERE_MALUS: f32 = -5.0;
    pub const HIGH_MALUS: f32 = -2.5;
    pub const MEDIUM_MALUS: f32 = -1.0;
    pub const SMALL_MALUS: f32 = -0.5;
    pub const NONE: f32 = 0.0;
    pub const SMALL_BONUS: f32 = 0.5;
    pub const MEDIUM_BONUS: f32 = 1.0;
    pub const HIGH_BONUS: f32 = 2.5;
    pub const SEVERE_BONUS: f32 = 5.0;
    pub const CRITICAL_BONUS: f32 = 10.0;
}

pub struct ReputationModifier;
impl ReputationModifier {
    pub const SEVERE_MALUS: f32 = -1.5;
    pub const HIGH_MALUS: f32 = -0.5;
    pub const MEDIUM_MALUS: f32 = -0.2;
    pub const SMALL_MALUS: f32 = -0.1;
    pub const NONE: f32 = 0.0;
    pub const SMALL_BONUS: f32 = 0.1;
    pub const MEDIUM_BONUS: f32 = 0.2;
    pub const HIGH_BONUS: f32 = 0.5;
    pub const SEVERE_BONUS: f32 = 1.5;
    pub const BONUS_PER_DISTANCE: f32 = 0.000_002;
}

pub const MORALE_INCREASE_PER_GAME: f32 = MoraleModifier::SEVERE_BONUS;
pub const MORALE_RELEASE_MALUS: f32 = MoraleModifier::SEVERE_MALUS;
pub const SATISFACTION_THRESHOLD_FOR_LEAVING: f32 = 4.0;
pub const SATISFACTION_MALUS_FOR_MORALE_DROP: f32 = -0.25;
pub const SATISFACTION_MALUS_FOR_SITTING_OUT: f32 = -0.1;
pub const SATISFACTION_DECREASE_PER_LONG_TICK: f32 = -0.1;
pub const SATISFACTION_PER_OPINION_EVENT: f32 = 0.275;
pub const SATISFACTION_OPINION_RECOVERY_TIME: Tick = TickInterval::LONG;
pub const SATISFACTION_OPINION_MODIFIER_WEIGHT: f32 = 2.5;
pub const LEAVING_PROBABILITY_SATISFACTION_MODIFIER: f64 =
    0.01 / SATISFACTION_THRESHOLD_FOR_LEAVING as f64;
pub const SATISFACTION_MALUS_RELEASE_FROM_TEAM: Skill = -5.0;
pub const SATISFACTION_MALUS_UNPAID_SALARIES: Skill = -1.5;

pub const MORALE_DEMOTION_MALUS: Skill = MoraleModifier::MEDIUM_MALUS;
pub const MORALE_GAME_POPULATION_MODIFIER: Skill = 0.5;
pub const MORALE_DRINK_BONUS: Skill = MoraleModifier::SEVERE_BONUS;
pub const MORALE_GOLD_BONUS: Skill = MoraleModifier::CRITICAL_BONUS;
pub const MORALE_HIRE_BONUS: Skill = MoraleModifier::CRITICAL_BONUS;

pub const OPINION_NEUTRAL_VALUE: Skill = 0.5 * MAX_SKILL;
pub const PLAYER_OPINION_ON_HIRE: Skill = 3.0;
pub const SPACE_ADVENTURE_OPINION_MOD: f32 = 2.0;

pub const BASE_DRUNK_PROBABILITY: f64 = 0.725;
pub const DRUNKENNESS_PER_DRINK: f32 = 2.0;
pub const DRUNKENNESS_ON_GETTING_DRUNK: f32 = -3.0;
// Spugna players hold their liquor: they accrue drunkenness more slowly and are
// less affected once they finally get wasted.
pub const SPUGNA_DRUNKENNESS_PER_DRINK: f32 = 0.1 * DRUNKENNESS_PER_DRINK;
pub const SPUGNA_DRUNKENNESS_ON_GETTING_DRUNK: f32 = 0.5 * DRUNKENNESS_ON_GETTING_DRUNK;
pub const RECOVERING_DRUNKENNESS_PER_SHORT_TICK: f32 = TirednessCost::LOW;
pub const TIREDNESS_DECREASE_BEFORE_TOURNAMENT_GAME: f32 = 2.0 * TirednessCost::CRITICAL;

pub const MIN_RELATIVE_RETIREMENT_AGE: f32 = 0.96;
pub const PEAK_PERFORMANCE_RELATIVE_AGE: f32 = 0.7;
pub const BASE_FITNESS: [Skill; NUM_GAME_POSITIONS as usize] = [22.0, 15.5, 12.5, 10.0, 6.0];

pub const POLOSIUS_TEAM_ID: TeamId = uuid!("7eb1bb55-3bac-49ea-a480-44b581680167");

// Used for running averages for player previous skills
pub const TREND_WINDOW_DAYS: f32 = 3.0;
pub const TREND_SMOOTHING: f32 = 1.0 / (TREND_WINDOW_DAYS + 1.0);

pub const BASE_TEAM_DRINKING_PROBABILITY: f64 = 0.0005;

pub const MAX_TAVERN_POPULATION: u32 = 3;

pub const PLAYER_SCOUTING_PER_GAME: Skill = 0.85;

pub const TOURNAMENT_ORGANIZATION_GOLD_COST: u32 = 1;
