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

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)

LOG_DUMMY = logging.getLogger("dummy")
LOG_DUMMY.addHandler(logging.NullHandler())

DEFAULT_DIR_TEMPLATES = ROOTDIR / "templates"

# =============================================================================
def get_templates_list(
        dir_templates = DEFAULT_DIR_TEMPLATES,
        ):

    templates_list = []

    dir_templates_path = pathlib.Path(dir_templates)

    for potential_template in dir_templates_path.iterdir():

        if (potential_template / "template.svg.j2").is_file():
            templates_list.append(potential_template.name)

    return templates_list

# =============================================================================
def get_infos_from_url(
        url,
        token,
        options = {},
        outform = "dict",
        top = 8,
        proxy = None,
        log = LOG_DUMMY,
        ):

    url_parsed = urllib.parse.urlparse(url)

    if url_parsed.netloc not in [ "smash.gg" ]:
        raise ValueError("Unsupported domain name")
    if outform not in [ "dict", "lkrz" ]:
        raise ValueError("Unsupported outform")

    # -------------------------------------------------------------------------
    if url_parsed.netloc == "smash.gg":

        if (url_parsed.path.split("/")[1] != "tournament"):
            raise Exception("No tournament found in url {}".format(url_parsed.path.split("/")))

        # Get infos from smash.gg and write the config file
        tournament, top_players = getTournamentTop(
                id_or_slug = url_parsed.path.split("/")[2],
                get_prefixes = options.get("use_smashgg_prefixes", False),
                top = top,
                token = token,
                proxy = proxy,
                log = log,
                )

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

    # -------------------------------------------------------------------------
    if outform == "dict":
        return {
                "tournament": tournament,
                "players": top_players,
                }

    # -------------------------------------------------------------------------
    if outform == "lkrz":
        return "\n".join(
                [ tournament.conf() ] \
                        + list(map(
                            lambda p:p.conf(),
                            top_players,
                            ))
                        )

# =============================================================================
def generate_pic(
        infos_or_lkrzfile = None,
        template = None,
        outform = "svg",
        options = {},
        dir_templates = DEFAULT_DIR_TEMPLATES,
        dir_res = None,
        dir_cache = None,
        log = LOG_DUMMY,
        ):

    if outform not in ["svg", "png"]:
        raise Exception("Unsupported outform")

    if type(infos_or_lkrzfile) == str:
        # TODO : load lkrz as dict infos
        raise NotImplementedError()
    else:
        infos = infos_or_lkrzfile

    # -------------------------------------------------------------------------
    # Build the context which will be passed to the template
    context = {
            "tournament": infos["tournament"].clean_name(
                options.get(
                    "name_seo_delimiter",
                    None
                    )
                ),
            "players" : sorted(
                infos["players"],
                key = lambda p: p.placement,
                ),
            "dir_res_ssbu": dir_res,
            "dir_template": str(dir_templates/template),
            "options": options.get("template_options", []),
            }

    pic = export.generate_pic(
            dir_templates,
            template,
            context,
            outform,
            log = log,
            cachedir = dir_cache,
            )

    if pic is None:
        raise Exception("Failed to generate pic")

    return pic

# =============================================================================
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 = (lambda s: s if s.startswith("http") else pathlib.Path(s)),
            default = ROOTDIR / "data" / "playerskinsdb.json",
            help = "A JSON file path or url matching player tags, characters,"\
                   " sponsors, and preferred skins",
            )
    top8_parser.add_argument(
            "--cachedir", "-CD",
            type = pathlib.Path,
            default = pathlib.Path(APPDIRS.user_cache_dir),
            help = "A directory to use for temporary files",
            )
    top8_parser.add_argument(
            "--templatesdir", "-TD",
            type = pathlib.Path,
            default = DEFAULT_DIR_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).",
            )
    top8_parser.add_argument(
            "--use-smashgg-prefixes", "-P",
            action = "store_true",
            help = "Use the prefixes (sponsor, team, etc) set by players on " \
                   "smash.gg for 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))
        try:
            PLAYERSKINS =  requests.get(args.playerskinsdb).json()
            smashgg.GET_PLAYERDATA = (lambda tag: PLAYERSKINS[tag.lower()])
        except:
            with args.playerskinsdb.open("r", encoding="utf8") as f:
                PLAYERSKINS = json.load(f)
                smashgg.GET_PLAYERDATA = (lambda tag: PLAYERSKINS[tag.lower()])

        #
        tournament = None
        top_players = {}

        if args.lkrz_file is not None and args.lkrz_file.exists():
            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"],
                            twitterHandle = section["twitter"],
                            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,
                    get_prefixes = args.use_smashgg_prefixes,
                    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,
                        ))
                    )

            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,
                    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 = args.cachedir,
                )

        if rv is None:
            return 1

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

        return 0

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

    # TODO if url matches challonge
    #
    #data = challonge.get_participants(
    #        api_key = token,
    #        tournament = id_or_slug,
    #        )
    #
    #top_array = []*top
    #for p in data:
    #   top_array[p["participant"]["final_rank"]] = ...

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

        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

        if get_prefixes:
            prefix = participant_data["prefix"]
        else:
            prefix = ""

        player = smashgg.Player(
                id = standing["entrant"]["id"],
                prefix = 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

    # Sort top_players by rank instead of id:
    top_players_sorted = sorted(
                top_players.values(),
                key = lambda p: p.placement,
                )

    # Return the data
    return tournament, top_players_sorted

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

