"""
The WyldWood Character Creation
"""

import inflect
from typeclasses.characters import Character

# from evennia.prototypes.spawner import spawn
# from evennia.utils.evtable import EvTable

from utils.word_list import paragraph

_INFLECT = inflect.engine()


#########################################################
#                   Utilities
#########################################################

# def create_objects(character):
#     """do the actual object spawning"""
#     # since our example chargen saves the starting prototype to an attribute, we retrieve that here
#     proto = dict(character.db.starter_weapon)
#     # set the location to our character, so they actually have it
#     proto["location"] = character
#     # create the object
#     spawn(proto)


#########################################################
#                   Welcome Page
#########################################################


def menunode_welcome(caller):
    """Starting page."""

    char = caller.new_char

    # another decision, so save the resume point
    char.db.chargen_step = "menunode_welcome"

    text = paragraph("""\
    |wHoward's Character Creator, v1|n

    This game takes place in the |cDomain of the WyldWood|n, and your
    character may be a faery denizen of this strange wonderland, or
    perhaps someone more typical, but who stepped into a faery circle
    or other portal and landed in this strange, magical place.

    To create this fantasy character, I will ask a series of
    questions, and you will type your answer.
    Type |ghelp|n at any of these prompts, for additional information.
    Type |gquit|n to stop the process (recover your session using
    |gcharcreate|n).
    """) + """

      1. Short Description
      2. Set Gender
      3. Character's "Pose"
      4. Long Description
      5. Character's Name
    """ + "|/Type |cReturn|n to begin creating your character."

    help = paragraph("""
    Type |gnext|n or |gskip|n (or even a blank answer), to keep any
    previously entered data for the step and go to the next prompt.

    Type |gback|n to return to a previous step, well, except for
    this first message. Type |gquit|n instead. :-D
    """)

    options = {"key": "_default", "goto": _check_welcome}
    return (text, help), options


def _check_welcome(caller, raw_string, **kwargs):
    """Check and fix the welcome message."""
    return "menunode_sdesc"


#########################################################
#                   Short Description
#########################################################

def menunode_sdesc(caller, raw_string, **kwargs):
    """Starting page."""
    char = caller.new_char

    # another decision, so save the resume point
    char.db.chargen_step = "menunode_sdesc"

    # check if an error message was passed to the node. if so, you'll
    # want to include it into your "name prompt" at the end of the
    # node text.

    if error := kwargs.get("error"):
        prompt_text = f"{error}. Enter a different short description:"
    elif char.db._sdesc:
        prompt_text = f"You previously wrote: |w{char.db._sdesc}|n"
    else:
        prompt_text = paragraph("""\
        Let's begin with a two or three word description, e.g.
        "big-eared hobbit", "beardless dwarf" or "frumpy, old woman":
        """)

    text = paragraph(f"""\
    |wShort Description|n

    Since this game emphasizes |wrole playing|n, you will pick your
    character's name at the end (since no one will know, or should
    know, your |wreal|n name).

    When you and another player characters uses the |glook|n command,
    you will see a description of the area, including a list of all
    the other characters, but using a short description instead of the
    character's name.

    What should your |wshort description|n identify your character?

    {prompt_text}""")

    help = paragraph("""\
    In fact, you will even use this description to refer to them, for
    instance, to get a better description of the character, you might
    type |glook hobbit|n, or if there are two hobbits in the area,
    they would need to type more of
    the description: |glook big-eared hobbit|n

    |wNote:|n Type, |gnext|n to keep your value and skip to the next step.""")

    # Free-formed text:
    options = {"key": "_default", "goto": _check_sdesc}
    return (text, help), options


def _check_sdesc(caller, raw_string, **kwargs):
    """Check and confirm short description"""

    # strip any extraneous whitespace from the raw text
    sdesc = raw_string.strip()

    if sdesc == 'back':
        return "menunode_welcome"
    if sdesc in ('next', 'skip') or not sdesc:
        return "menunode_gender"

    # aside from validation, the built-in normalization function from
    # the caller's Account does some useful cleanup on the input, just
    # in case they try something sneaky:
    sdesc = caller.account.normalize_username(sdesc)

    # Make sure it isn't too long.
    if len(sdesc) > 30:
        return (
            "menunode_sdesc",
            {"error": f"|w{sdesc}|n is too long. Try less than two dozen letters."},
        )

    caller.new_char.db._sdesc = sdesc
    return "menunode_gender"


#########################################################
#                Character Gender
#########################################################

def menunode_gender(caller, raw_string, **kwargs):
    """Choosing a gender for the character."""
    char = caller.new_char

    # another decision, so save the resume point
    char.db.chargen_step = "menunode_gender"

    text = paragraph(f"""\
    |wCharacter Gender|n

    Specifying the gender allows the system to use the correct
    pronouns for your character. You can change this in the game
    using the |ggender|n command.

    Choose one of the following:""")

    help = paragraph("""\
    This feature only works from the system, not from other players.
    A character would need to tell others of their preferred pronouns.
    """)

    options = (
        {"key": "m",
         "desc": "male with pronouns, he/him/his",
         "goto": (_check_gender, {"gender": "male"}),
         },
        {"key": "f",
         "desc": "female with pronouns, she/her/hers",
         "goto": (_check_gender, {"gender": "female"}),
         },
        {"key": "n",
         "desc": "neutral with pronouns, it/its",
         "goto": (_check_gender, {"gender": "neutral"}),
         },
        {"key": "a",
         "desc": "ambiguous with pronouns, they/them/their",
         "goto": (_check_gender, {"gender": "ambiguous"}),
         },
        {"key": "b",
         "desc": "go back and change the short description",
         "goto": (menunode_sdesc),
         },
    )
    return (text, help), options


def _check_gender(caller, rawstring, **kwargs):
    """Check and confirm pose"""

    caller.new_char.db.gender = kwargs['gender']
    return "menunode_pose"


#########################################################
#                Posing the Character
#########################################################

def menunode_pose(caller, raw_string, **kwargs):
    """Posing a character."""
    char = caller.new_char

    # another decision, so save the resume point
    char.db.chargen_step = "menunode_pose"

    if char.db.pose:
        prompt_text = f"You previously wrote: |w{char.db.pose}|n"
    else:
        prompt_text = ''

    text = paragraph(f"""\
    |wPosing a Character|n

    Along with the short description given in the previous step, we
    can |wpose|n our character. For instance, entering:

          |wsmoking a pipe|n

    Might show our character as:

          a |mbig-eared hobbit|w smoking a pipe|n

    As the game plays, you can change this at any time using the
    |gpose|n command. This is just sets up a default pose, which can
    return to with |gpose reset|n. Even this isn't permanent, as you
    can change the default with something like:

           |gpose default sitting on the ground|n

    {prompt_text}

    Enter an initial pose to describe, |m{char.db._sdesc}|n:""")

    help = paragraph("""\
    Don't worry much about this pose, since you can (and will) change
    it as you play the game.

    Having an initial pose makes the game (and your character) look
    better.

    Enter an initial pose to describe your character, or type |gback|n
    to change your short description.""")

    # Free-formed text:
    options = {"key": "_default", "goto": _check_pose}
    return (text, help), options


def _check_pose(caller, raw_string, **kwargs):
    """Check and confirm pose"""

    # strip any extraneous whitespace from the raw text
    pose = raw_string.strip()

    if pose == 'back':
        return "menunode_gender"
    if pose in ('next', 'skip') or not pose:
        return "menunode_desc"

    # First, allow the character to be posed:
    caller.new_char.pose = True
    caller.new_char.db.pose = pose
    caller.new_char.db.pose_default = pose

    return "menunode_desc"


#########################################################
#                Full Description
#########################################################

def menunode_desc(caller, raw_string, **kwargs):
    """Posing a character."""
    char = caller.new_char

    # another decision, so save the resume point
    char.db.chargen_step = "menunode_desc"

    if char.db.desc and char.db.desc != "This is a character.":
        prompt_text = f"You previously wrote:\n\n        |w{char.db.desc}|n"
    else:
        prompt_text = ''

    text = paragraph(f"""
    |wCharacter's Full Description|n

    Unlike the short description given in the previous step (see only
    when glancing around the room), a player will see this description
    when they type:

       |glook {char.db._sdesc}|n

    This can be as long as you would like. Keep in mind this
    description is what a player |wsees|n. so, no backstory here.
    Well, unless you can cleverly weave in backstory hints from that
    scar across your cheek.

    You can change this at any time using the |gdesc|n command.

    {prompt_text}

    Enter your character's full description:""")

    help = paragraph("""\
    What makes a good character description? You want someone to be
    able to picture your character in their mind, so hair, skin and
    eye color, as well as what they are wearing.

    To make your character truly memorable, mention the unique
    features, so think of your character's backstory, and describe the
    small hammer on your necklace, so you can tell others who inquire
    about your father's last wish...

    Enter your character's full description or type |gback|n to change
    your pose.""")

    # Free-formed text:
    options = {"key": "_default", "goto": _check_desc}
    return (text, help), options


def _check_desc(caller, raw_string, **kwargs):
    """Check and confirm desc"""

    # strip any extraneous whitespace from the raw text
    desc = raw_string.strip()

    if desc == 'back':
        return "menunode_pose"
    if desc in ('next', 'skip') or not desc:
        return "menunode_choose_name"

    # First, allow the character to be descd:
    caller.new_char.db.desc = desc

    return "menunode_choose_name"


#########################################################
#                Choosing a Name
#########################################################

def menunode_choose_name(caller, raw_string, **kwargs):
    """Name selection"""
    char = caller.new_char

    # another decision, so save the resume point
    char.db.chargen_step = "menunode_choose_name"

    # check if an error message was passed to the node. if so, you'll
    # want to include it into your "name prompt" at the end of the
    # node text.
    if error := kwargs.get("error"):
        prompt_text = f"{error}. Enter a different name."
    else:
        # there was no error, so just ask them to enter a name.
        prompt_text = "Enter a name here to check if it's available."

    # this will print every time the player is prompted to choose a name,
    # including the prompt text defined above
    text = paragraph(f"""\
    |wChoosing a Name|n

    Now, let's give your character a name. Unless you tell others, no
    one will know (or need to know) your this name, however, you will
    use this name, when you type |gic Name|n to choose this character
    to play.

    {prompt_text}""")

    help = paragraph("""
    Since you may have to type the entire name, you may want this to
    be a single word for the first name. Your background could include
    your full family name.
    """)

    # since this is a free-text field, we just have the one
    options = {"key": "_default", "goto": _check_charname}
    return (text, help), options


def _check_charname(caller, raw_string, **kwargs):
    """Check and confirm name choice"""

    # strip any extraneous whitespace from the raw text
    # if you want to do any other validation on the name, e.g. no punctuation allowed, this
    # is the place!
    charname = raw_string.strip()

    if charname == 'back':
        return "menunode_desc"
    if charname in ('next', 'skip') or not charname:
        return (
            "menunode_choose_name",
            {"error": f"Can't skip this step. Please enter a name."},
        )

    # aside from validation, the built-in normalization function from the caller's Account does
    # some useful cleanup on the input, just in case they try something sneaky
    charname = caller.account.normalize_username(charname)

    # check to make sure that the name doesn't already exist
    candidates = Character.objects.filter_family(db_key__iexact=charname)
    if len(candidates):
        # the name is already taken - report back with the error
        return (
            "menunode_choose_name",
            {"error": f"|w{charname}|n is unavailable. Enter a different name."},
        )
    else:
        # it's free! set the character's key to the name to reserve it
        caller.new_char.key = charname
        # continue on to the confirmation node
        return "menunode_confirm_name"


def menunode_confirm_name(caller, raw_string, **kwargs):
    """Confirm the name choice"""
    char = caller.new_char

    text = paragraph(f"""\
    |wCreate Character?|n

    Shall I create your character, |c{char.key}|n:

      |w{char.db._sdesc} {char.db.pose}|n

    Description:

      {char.db.desc}""")

    options = [
        {"key": ("Yes", "y"), "goto": "menunode_end"},
        {"key": ("No", "n"), "goto": "menunode_choose_name"},
    ]
    return text, options


#########################################################
#                     The End
#########################################################


def menunode_end(caller, raw_string):
    """End-of-chargen cleanup."""
    char = caller.new_char

    # clear in-progress status
    char.attributes.remove("chargen_step")
    char.db.tutorstate = 0
    char.db.visited = False

    text = paragraph(f"""
    You have created a character. I hope you enjoy playing this game.

    To stop playing with this character, type: |gooc|n

    You can create a new character with: |gcharcreate|n

    To play with this one, type: |gic {caller.new_char.key}|n

    As always, type |ghelp|n (especially |ghelp start|n),
    to learn more about how to play this game.

    Alrighty then, let's log in and start playing the game.
    Imagine that you are now in a strange land on the edge of Forever.
    Welcome to the cozy forest section in the |wDomain of the WyldWood|n.
    """)
    return text, None
