import argparse
import configparser
import datetime
import html
import json
import logging
import pathlib
import sys

import appdirs

from . import export
from . import resources
from . import smashgg
from . import version

# =============================================================================
__version__ = version.__version__
__license__ = version.__license__

ROOTDIR = pathlib.Path(__file__).absolute().parent

APPDIRS = appdirs.AppDirs(version.NAME, version.ENTITY)

# =============================================================================
def main():

    # -------------------------------------------------------------------------
    parser = argparse.ArgumentParser(
            formatter_class = argparse.ArgumentDefaultsHelpFormatter,
            )

    subparsers = parser.add_subparsers(
            dest = "command",
            help = "commands",
            )

    parser.add_argument(
            "--proxy", "-p",
            default = None,
            help = "the proxy to use",
            )

    # -------------------------------------------------------------------------
    init_parser = subparsers.add_parser(
            "init",
            formatter_class = argparse.ArgumentDefaultsHelpFormatter,
            )

    init_parser.add_argument(
            "game",
            default = "ssbu",
            help = "The game you want to initialize the resources for",
            )

    init_parser.add_argument(
            "--imgdir", "-ID",
            type = pathlib.Path,
            default = pathlib.Path(APPDIRS.user_data_dir) / "res",
            help = "The directory we should download the resources to",
            )

    # -------------------------------------------------------------------------
    top8_parser = subparsers.add_parser(
            "top8",
            formatter_class = argparse.ArgumentDefaultsHelpFormatter,
            )

    top8_parser.add_argument(
            "tournament",
            default = None,
            help = "The tournament slug or id",
            )

    top8_parser.add_argument(
            "--token", "-t",
            default = None,
            help = "the authentication token to use",
            )

    top8_parser.add_argument(
            "--imgdir", "-ID",
            type = pathlib.Path,
            default = pathlib.Path(APPDIRS.user_data_dir) / "res",
            help = "The directories containing images, be careful whether " \
                   "you specify an absolute path or a relative one.",
            )
    top8_parser.add_argument(
            "--playerskinsdb", "-PD",
            type = pathlib.Path,
            default = ROOTDIR / "data" / "playerskinsdb.json",
            help = "A JSON file matching player tags, characters and " \
                   "preferred skins",
            )
    top8_parser.add_argument(
            "--templatesdir", "-TD",
            type = pathlib.Path,
            default = ROOTDIR / "templates",
            help = "The local result templates directory",
            )
    top8_parser.add_argument(
            "--template", "-T",
            default = "rebootlyon2020",
            help = "The local result template to use",
            )
    top8_parser.add_argument(
            "--template-options", "-O",
            action = "append",
            default = [],
            help = "Template-specific options",
            )
    top8_parser.add_argument(
            "--lkrz-file", "-f",
            type = pathlib.Path,
            default = None,
            help = "The lkrz file in which the results are stored ; if it " \
                   "does not exist, one will be created from the smashgg data",
            )
    top8_parser.add_argument(
            "--outfile", "-o",
            type = pathlib.Path,
            default = None,
            help = "The SVG or PNG local result file to output to ; if it's " \
                   "not specified, it will use the tournament slug as name",
            )
    top8_parser.add_argument(
            "--name-seo-delimiter",
            default = None,
            help = "A character that will delimit in a tournament name what " \
                   "really is its name, and what's actually here for SEO "    \
                   "purposes (example: in 'Cornismash #42 - Ultimate Weekly " \
                   "Lyon', only the 'Cornismash #42' is the tournament name, "\
                   "the rest is here to help find the tournament).",
            )

    # -------------------------------------------------------------------------
    parser.add_argument( "--verbose", "-v",
                         default = 0,
                         action = "count",
                         help = "increase verbosity" )

    parser.add_argument( "--version", "-V",
                         default = False,
                         action = "store_true",
                         help = "show version number" )

    # -------------------------------------------------------------------------
    args = parser.parse_args()

    # Set log level
    # -------------------------------------------------------------------------
    log = logging.getLogger(version.NAME)
    log.setLevel(logging.DEBUG)

    log_handler_console = logging.StreamHandler()
    log_handler_console.setLevel(logging.WARNING)

    if(args.verbose >= 2):
        log_handler_console.setLevel(logging.DEBUG)
    elif(args.verbose >=1):
        log_handler_console.setLevel(logging.INFO)
    else:
        log_handler_console.setLevel(logging.WARNING)

    log_formatter_console = logging.Formatter("%(name)s:%(levelname)s: %(message)s")

    log_handler_console.setFormatter(log_formatter_console)

    log.addHandler(log_handler_console)

    # Print version if required
    # -------------------------------------------------------------------------
    if args.version:
        print(version.VERSION_NAME)
        return 0

    # -------------------------------------------------------------------------
    if args.command not in [ "init", "top8" ]:
        parser.print_help()
        return 1

    # -------------------------------------------------------------------------
    if args.command == "init":
        args.imgdir.mkdir(parents=True, exist_ok=True)
        resources.download_res_ssbu(
                dstdir = args.imgdir,
                proxy = args.proxy,
                log = log,
                )
        return 0
    # -------------------------------------------------------------------------
    if args.command == "top8":

        # Initialize PLAYERSKINS db
        log.debug("loading playerskins db from '{}'" \
                .format(args.playerskinsdb))
        with args.playerskinsdb.open("r") as f:
            smashgg.PLAYERSKINS = json.load(f)

        #
        tournament = None
        top_players = {}

        if args.lkrz_file is not None:
            lkrz = configparser.ConfigParser()
            lkrz.read(str(args.lkrz_file))

            log.info("Loading data from '{}'".format(args.lkrz_file))

            for s in lkrz:
                section = lkrz[s]
                if s == "Tournament":
                    tournament = smashgg.Tournament(
                            id = 0,
                            name = section["name"],
                            slug = section["slug"],
                            startAt = datetime.datetime.strptime(
                                section["date"],
                                "%Y-%m-%d %H:%M:%S",
                                ),
                            numEntrants = int(section["numEntrants"]),
                            venueName = section["location"],
                            )

                elif s.startswith("player "):
                    chars = {}
                    for char in section["characters"].split(","):
                        c = char.strip()
                        charname = c.split("_")[0]
                        charskin = c.split("_")[1].split(" ")[0]
                        charscore = float(c.split("(")[1].split(")")[0])

                        chars[(charname,charskin)] = charscore

                    player = smashgg.Player(
                            id = 0,
                            prefix = section["team"],
                            gamerTag = section["tag"],
                            placement = section["placement"],
                            seeding = section["seeding"],
                            chars = chars,
                            )

                    top_players[player.gamerTag] = player

        else:

            # Get infos from smash.gg and write the config file
            tournament, top_players = getTournamentTop(
                    id_or_slug = args.tournament,
                    top = 8,
                    token = args.token,
                    proxy = args.proxy,
                    log = log,
                    )

            if tournament is None or top_players is None:
                log.error("Could not load data from smash.gg")
                return 1

            lkrz_data = "\n".join(
                    [ tournament.conf() ] \
                    + list(map(
                        lambda p:p.conf(),
                        top_players.values(),
                        ))
                    )

            if args.lkrz_file is None:
                args.lkrz_file = pathlib.Path(
                        "{}.lkrz".format(tournament.slug)
                        )

            with args.lkrz_file.open("w", encoding="utf8") as f:
                f.write(lkrz_data)

        # Default outfile is 'tournament-slug.svg'
        if args.outfile is None:
            args.outfile = pathlib.Path(
                    "{}.svg".format(tournament.slug),
                    )

        # Build the context which will be passed to the template
        try:
            dir_res_ssbu = args.imgdir.as_uri() # not absolute => error
        except ValueError:
            dir_res_ssbu = args.imgdir.as_posix()

        context = {
                "tournament": tournament.clean_name(args.name_seo_delimiter),
                "players" : sorted(
                    top_players.values(),
                    key = lambda p: p.placement,
                    ),
                "dir_res_ssbu": dir_res_ssbu,
                "dir_template": str(args.templatesdir / args.template),
                "options": args.template_options,
                }

        rv = export.generate_outfile(
                args.templatesdir,
                args.template,
                context,
                args.outfile,
                log = log,
                cachedir = pathlib.Path(APPDIRS.user_cache_dir),
                )

        if rv is None:
            return 1

        log.info("Successfully saved outfile as '{}'".format(rv))

        return 0

# -----------------------------------------------------------------------------
def getTournamentTop(
        id_or_slug,
        top=8,
        token = "",
        proxy = None,
        log=None):
    """Returns a tuple : the smashgg.Tournament object and a list of the top
    smashgg.Player in that tournament."""


    # -------------------------------------------------------------------------
    # Select the right event (the one with the most entrants or the most sets)
    def selectBiggestEvent(data, log=None):

        try:
            event = data["events"][0]
        except:
            log.error("No event found in data")
            log.debug(data)
            return None

        try:
            numEntrants = event["numEntrants"]
        except KeyError:
            numEntrants = event["standings"]["pageInfo"]["total"]

        for e in data["events"]:
            try:
                ne = e["numEntrants"]
            except KeyError:
                ne = e["standings"]["pageInfo"]["total"]

            if ne > numEntrants:
                event = e
                numEntrants = ne

        log.info("Selected Event '{}' with {} entrants" \
                .format(
                    event["name"],
                    numEntrants,
                    ))

        return event

    # -------------------------------------------------------------------------
    data = None

    try:
        data = smashgg.run_query(
                query_name = "getTournamentTopById",
                variables = {
                    "id" : int(id_or_slug), # If this fails, it's a slug
                    "top": top,
                    },
                query_dir = ROOTDIR / "queries",
                token = token,
                proxy = proxy,
                log = log,
                )

    except ValueError:
        data = smashgg.run_query(
                query_name = "getTournamentTopBySlug",
                variables = {
                    "slug" : id_or_slug,
                    "top": top,
                    },
                query_dir = ROOTDIR / "queries",
                token = token,
                proxy = proxy,
                log = log,
                )

    try:
        tournament_data = data["tournament"]
    except:
        log.error("Failed to load Tournaments")
        return None,None

    if tournament_data is None:
        log.error("Failed to load Tournament")
        return None,None

    event = selectBiggestEvent(tournament_data, log)

    if event is None :
        return None,None

    # Get the tournament
    tournament = smashgg.Tournament(
            id = tournament_data["id"],
            slug = tournament_data["slug"],
            name = tournament_data["name"],
            startAt = \
                    datetime.datetime. \
                    fromtimestamp(tournament_data["startAt"]),
            numEntrants = event["standings"]["pageInfo"]["total"],
            venueAddress = tournament_data["venueAddress"],
            venueName = tournament_data["venueName"],
            city = tournament_data["city"],
            countryCode = tournament_data["countryCode"],
            hashtag = tournament_data["hashtag"],
            )


    # Get the top players
    top_players = {}

    standings = event["standings"]["nodes"]

    for standing in standings :

        seeding = None
        for seed in standing["entrant"]["seeds"]:
            # Take the seeding from the phase with *all* Event entrants
            if seed["phase"]["numSeeds"] == tournament.numEntrants:
                seeding = seed["groupSeedNum"]

        participant_data = standing["entrant"]["participants"][0]

        try:
            twitterHandle = participant_data \
                    ["player"] \
                    ["user"] \
                    ["authorizations"] \
                    [0] \
                    ["externalUsername"]
        except TypeError:
            twitterHandle = None


        player = smashgg.Player(
                id = standing["entrant"]["id"],
                prefix = participant_data["prefix"],
                gamerTag = participant_data["gamerTag"],
                placement = standing["placement"],
                seeding = seeding,
                twitterHandle = twitterHandle,
                )

        top_players[player.id] = player

    # -------------------------------------------------------------------------
    # Now, we need to find which characters those top players chose
    data = None

    data = smashgg.run_query(
            query_name = "getCharsByTournamentIdAndEntrantIds",
            variables = {
                "tournamentId" : int(tournament.id),
                "entrantIds": [ id for id in top_players.keys() ],
                },
            query_dir = ROOTDIR / "queries",
            token = token,
            proxy = proxy,
            log = log,
            )

    try:
        tournament_data = data["tournament"]
    except:
        log.error("Failed to load Tournament")
        return None,None

    if tournament_data is None:
        log.error("Failed to load Tournament")
        return None,None

    event = selectBiggestEvent(tournament_data, log)

    if event is None :
        return None,None

    # TODO check that sets number is < to hardcoded 100 max value (cf query)
    sets = event["sets"]["nodes"]

    for s in sets:
        try:
            for g in s["games"]:

                winnerId = g["winnerId"]

                for slct in g["selections"]:

                    if slct["selectionType"] == "CHARACTER":
                        eid = slct["entrant"]["id"]
                        try:
                            top_players[eid].add_character_selection(
                                    character = slct["selectionValue"],
                                    win = (winnerId == eid),
                                    )
                        except KeyError:
                            pass
        except TypeError:
            # If some games or selections are null, this can happen
            continue



    # Return the data
    return tournament, top_players

# =============================================================================
if __name__ == '__main__':
    rv = main()
    sys.exit(rv)

