"""
Bombs away

Ported from BASIC to Python3 by Bernard Cooke (bernardcooke53)
Tested with Python 3.8.10, formatted with Black and type checked with mypy.
"""
import random
from typing import Iterable


def _stdin_choice(prompt: str, *, choices: Iterable[str]) -> str:
    ret = input(prompt)
    while ret not in choices:
        print("TRY AGAIN...")
        ret = input(prompt)
    return ret


def player_survived() -> None:
    print("YOU MADE IT THROUGH TREMENDOUS FLAK!!")


def player_death() -> None:
    print("* * * * BOOM * * * *")
    print("YOU HAVE BEEN SHOT DOWN.....")
    print("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR")
    print("LAST TRIBUTE...")


def mission_success() -> None:
    print(f"DIRECT HIT!!!! {int(100 * random.random())} KILLED.")
    print("MISSION SUCCESSFUL.")


def death_with_chance(p_death: float) -> bool:
    """
    Takes a float between 0 and 1 and returns a boolean
    if the player has survived (based on random chance)

    Returns True if death, False if survived
    """
    return p_death > random.random()


def commence_non_kamikazi_attack() -> None:
    while True:
        try:
            nmissions = int(input("HOW MANY MISSIONS HAVE YOU FLOWN? "))

            while nmissions >= 160:
                print("MISSIONS, NOT MILES...")
                print("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS")
                nmissions = int(input("NOW THEN, HOW MANY MISSIONS HAVE YOU FLOWN? "))
            break
        except ValueError:
            # In the BASIC implementation this
            # wasn't accounted for
            print("TRY AGAIN...")
            continue

    if nmissions >= 100:
        print("THAT'S PUSHING THE ODDS!")

    if nmissions < 25:
        print("FRESH OUT OF TRAINING, EH?")

    print()
    return (
        mission_success() if nmissions >= 160 * random.random() else mission_failure()
    )


def mission_failure() -> None:
    weapons_choices = {
        "1": "GUNS",
        "2": "MISSILES",
        "3": "BOTH",
    }
    print(f"MISSED TARGET BY {int(2 + 30 * random.random())} MILES!")
    print("NOW YOU'RE REALLY IN FOR IT !!")
    print()
    enemy_weapons = _stdin_choice(
        prompt="DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)? ",
        choices=weapons_choices,
    )

    # If there are no gunners (i.e. weapon choice 2) then
    # we say that the gunners have 0 accuracy for the purposes
    # of calculating probability of player death

    enemy_gunner_accuracy = 0.0
    if enemy_weapons != "2":
        # If the enemy has guns, how accurate are the gunners?
        while True:
            try:
                enemy_gunner_accuracy = float(
                    input("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? ")
                )
                break
            except ValueError:
                # In the BASIC implementation this
                # wasn't accounted for
                print("TRY AGAIN...")
                continue

        if enemy_gunner_accuracy < 10:
            print("YOU LIE, BUT YOU'LL PAY...")
            return player_death()

    missile_threat_weighting = 0 if enemy_weapons == "1" else 35

    death = death_with_chance(
        p_death=(enemy_gunner_accuracy + missile_threat_weighting) / 100
    )

    return player_survived() if not death else player_death()


def play_italy() -> None:
    targets_to_messages = {
        # 1 - ALBANIA, 2 - GREECE, 3 - NORTH AFRICA
        "1": "SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE.",
        "2": "BE CAREFUL!!!",
        "3": "YOU'RE GOING FOR THE OIL, EH?",
    }
    target = _stdin_choice(
        prompt="YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)",
        choices=targets_to_messages,
    )

    print(targets_to_messages[target])
    return commence_non_kamikazi_attack()


def play_allies() -> None:
    aircraft_to_message = {
        "1": "YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI.",
        "2": "YOU'RE DUMPING THE A-BOMB ON HIROSHIMA.",
        "3": "YOU'RE CHASING THE BISMARK IN THE NORTH SEA.",
        "4": "YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.",
    }
    aircraft = _stdin_choice(
        prompt="AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4): ",
        choices=aircraft_to_message,
    )

    print(aircraft_to_message[aircraft])
    return commence_non_kamikazi_attack()


def play_japan() -> None:
    print("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.")
    first_mission = input("YOUR FIRST KAMIKAZE MISSION? (Y OR N): ")
    if first_mission.lower() == "n":
        return player_death()
    return mission_success() if random.random() > 0.65 else player_death()


def play_germany() -> None:
    targets_to_messages = {
        # 1 - RUSSIA, 2 - ENGLAND, 3 - FRANCE
        "1": "YOU'RE NEARING STALINGRAD.",
        "2": "NEARING LONDON.  BE CAREFUL, THEY'VE GOT RADAR.",
        "3": "NEARING VERSAILLES.  DUCK SOUP.  THEY'RE NEARLY DEFENSELESS.",
    }
    target = _stdin_choice(
        prompt="A NAZI, EH?  OH WELL.  ARE YOU GOING FOR RUSSIA(1),\nENGLAND(2), OR FRANCE(3)? ",
        choices=targets_to_messages,
    )

    print(targets_to_messages[target])

    return commence_non_kamikazi_attack()


def play_game() -> None:
    print("YOU ARE A PILOT IN A WORLD WAR II BOMBER.")
    sides = {"1": play_italy, "2": play_allies, "3": play_japan, "4": play_germany}
    side = _stdin_choice(
        prompt="WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4): ", choices=sides
    )
    return sides[side]()


if __name__ == "__main__":
    again = True
    while again:
        play_game()
        again = input("ANOTHER MISSION? (Y OR N): ").upper() == "Y"
