import colorsys
import json
import io
import logging
import textwrap
from collections import namedtuple

import flask
import lxml.etree as xmltree

from . import core, gamedata, jsonizer, util

pth_upgrade_schema = core.path_appdir / "basepak/rules/schemas/upgrades.xsd"
doc_upgrade_schema = xmltree.parse(pth_upgrade_schema.as_posix())
upgrade_schema = xmltree.XMLSchema(doc_upgrade_schema)
upgrade_schema_parser = xmltree.XMLParser(schema=upgrade_schema)

core.schemas.append((upgrade_schema, "rules/upgrades/**.xml"))

UpgradeData = namedtuple("UpgradeData", ["id", "name", "desc", "requires", "food", "shinies"])

## \brief Renders an upgrade tree.
#  \param tree The upgrade tree to retrieve.
#  \api{GET} /upgrades/`<tree>`
#  \return An SVG.
@core.app.route("/upgrades/<tree>")
def get_upgrade_tree(tree):
    buf_mmd = io.StringIO()
    rulefile = xmltree.parse(gamedata.vfs.open(f"/rules/upgrades/{tree}.xml"), upgrade_schema_parser)
    ruleset = rulefile.getroot()

    unlocked = []
    if len(flask.request.query_string) > 0:
        for upgrade in flask.request.args.get("unlocked", "").split(","):
            if len(upgrade) > 0:
                unlocked.append(upgrade)

    hnd_treedata = ruleset.xpath("./upgrades:TreeData", namespaces=core.xml_namespaces)[0]
    hnd_name = hnd_treedata.xpath("./upgrades:Name", namespaces=core.xml_namespaces)[0]
    hnd_primary_color = hnd_treedata.xpath("./upgrades:PrimaryColor", namespaces=core.xml_namespaces)[0]

    buf_mmd.write(textwrap.dedent(f"""
    ---
    title: {hnd_name.text}
    securityLevel: loose
    ---
    flowchart LR
    """).lstrip())

    rgb_primary_color = util.hex_to_rgb(hnd_primary_color.text)
    hsv_primary_color = colorsys.rgb_to_hsv(*rgb_primary_color)
    print(f"{rgb_primary_color} ----> {hsv_primary_color}")
    hsv_fill_open_color = (hsv_primary_color[0], hsv_primary_color[1] / 2, hsv_primary_color[2])
    rgb_fill_open_color = colorsys.hsv_to_rgb(*hsv_fill_open_color)
    hsv_stroke_unlock_color = (hsv_primary_color[0], 1.0, 55)
    rgb_stroke_unlock_color = colorsys.hsv_to_rgb(*hsv_stroke_unlock_color)
    hsv_fill_lock_color = (hsv_primary_color[0], 0.1, min(hsv_primary_color[2] * 1.5, 255))
    rgb_fill_lock_color = colorsys.hsv_to_rgb(*hsv_fill_lock_color)
    print(f"{hsv_fill_lock_color}, {rgb_fill_lock_color}")
    hsv_stroke_lock_color = (hsv_primary_color[0], 0.1, 55)
    rgb_stroke_lock_color = colorsys.hsv_to_rgb(*hsv_stroke_lock_color)

    buf_mmd.write(f"    classDef open fill:{util.rgb_to_hex(*rgb_fill_open_color)},stroke:{util.rgb_to_hex(*rgb_primary_color)}\n")
    buf_mmd.write(f"    classDef unlocked fill:{util.rgb_to_hex(*rgb_primary_color)},stroke:{util.rgb_to_hex(*rgb_stroke_unlock_color)}\n")
    buf_mmd.write(f"    classDef locked fill:{util.rgb_to_hex(*rgb_fill_lock_color)},stroke:{util.rgb_to_hex(*rgb_stroke_lock_color)}\n")

    tree_upgrades = []
    for hnd_upgrade in ruleset.iter("{seagull:rules/upgrades}Upgrade"):
        hnd_id = hnd_upgrade.xpath("./upgrades:Id", namespaces=core.xml_namespaces)[0]
        hnd_upgrade_name = hnd_upgrade.xpath("./upgrades:Name", namespaces=core.xml_namespaces)[0]
        hnd_desc = hnd_upgrade.xpath("./upgrades:Desc", namespaces=core.xml_namespaces)[0]
        try:
            hnd_requires = hnd_upgrade.xpath("./upgrades:Requirements", namespaces=core.xml_namespaces)[0]
            require_list = [elem.text for elem in hnd_requires.iter("{seagull:rules/upgrades}Require")]
            try:
                hnd_food = hnd_requires.xpath("./upgrades:Food", namespaces=core.xml_namespaces)[0]
            except IndexError:
                hnd_food = None
            try:
                hnd_shinies = hnd_requires.xpath("./upgrades:Shinies", namespaces=core.xml_namespaces)[0]
            except IndexError:
                hnd_shinies = None
        except IndexError:
            require_list = []
            hnd_food = None
            hnd_shinies = None
        

        upgrade = UpgradeData(
            id=hnd_id.text,
            name=hnd_upgrade_name.text,
            desc=hnd_desc.text,
            requires=require_list,
            food=int(hnd_food.text) if hnd_food is not None else 0,
            shinies=int(hnd_shinies.text) if hnd_shinies is not None else 0
        )

        tree_upgrades.append(upgrade)
    
    tiers = {}
    dependency_lines = []
    for upgrade in tree_upgrades:
        deptier = 0
        upgrade_food_costs = f"🍏{upgrade.food}" if upgrade.food > 0 else ""
        upgrade_shinies_costs = f"🪙{upgrade.shinies}" if upgrade.shinies > 0 else ""
        buf_mmd.write(f"    {upgrade.id}@{{label: \"{upgrade.name}\n<span style='font-size:0.6em'>{upgrade_food_costs}{upgrade_shinies_costs}</span>\"}}\n")
        collected_tiers = []
        for require in upgrade.requires:
            if require in tiers:
                collected_tiers.append(tiers[require])
            
            dependency_lines.append(f"    {require} --> {upgrade.id}\n")
        
        if len(collected_tiers) > 0:
            deptier = max(collected_tiers)
        
        tier = deptier + 1
        tiers[upgrade.id] = tier

        all_requirements_met = True
        if tier > 1:
            while all_requirements_met:
                for require in upgrade.requires:
                    all_requirements_met = require in unlocked
        
        if not all_requirements_met:
            buf_mmd.write(f"    class {upgrade.id} locked\n")
        elif upgrade in unlocked:
            buf_mmd.write(f"    class {upgrade.id} unlocked\n")
        else:
            buf_mmd.write(f"    class {upgrade.id} open\n")
            buf_mmd.write(f"    click {upgrade.id} call purchase_upgrade({tree},{upgrade.id})\n")
    
    for line in dependency_lines:
        buf_mmd.write(line)

    buf_mmd.seek(0)
    mmd_str = buf_mmd.read()
    retval = flask.make_response(mmd_str, 200)
    retval.headers["Content-Type"] = "text/plain"
    return retval
    #mmd_upgradetree = mermaid.Mermaid(mmd_str, height=400)
    #print(mmd_str)
    #return mmd_upgradetree.svg_response.content

@core.app.route("/upgrades/<tree>/<upgrade>")
def get_upgrade_data(tree, upgrade):
    rulefile = xmltree.parse(gamedata.vfs.open(f"/rules/upgrades/{tree}.xml"), upgrade_schema_parser)
    ruleset = rulefile.getroot()

    target_upgrade = None
    hnd_id = None
    for hnd_upgrade in ruleset.iter("{seagull:rules/upgrades}Upgrade"):
        hnd_id = hnd_upgrade.xpath("./upgrades:Id", namespaces=core.xml_namespaces)[0]
        if hnd_id.text == upgrade:
            target_upgrade = hnd_upgrade
            break
    
    hnd_name = hnd_upgrade.xpath("./upgrades:Name", namespaces=core.xml_namespaces)[0]
    hnd_desc = hnd_upgrade.xpath("./upgrades:Desc", namespaces=core.xml_namespaces)[0]
    try:
        hnd_requires = hnd_upgrade.xpath("./upgrades:Requirements", namespaces=core.xml_namespaces)[0]
        require_list = [elem.text for elem in hnd_requires.iter("{seagull:rules/upgrades}Require")]
        try:
            hnd_food = hnd_requires.xpath("./upgrades:Food", namespaces=core.xml_namespaces)[0]
        except IndexError:
            hnd_food = None
        try:
            hnd_agility = hnd_requires.xpath("./upgrades:Agility", namespaces=core.xml_namespaces)[0]
        except IndexError:
            hnd_agility = None
        try:
            hnd_instinct = hnd_requires.xpath("./upgrades:Instinct", namespaces=core.xml_namespaces)[0]
        except IndexError:
            hnd_instinct = None
        try:
            hnd_leadership = hnd_requires.xpath("./upgrades:Leadership", namespaces=core.xml_namespaces)[0]
        except IndexError:
            hnd_leadership = None
        try:
            hnd_shinies = hnd_requires.xpath("./upgrades:Shinies", namespaces=core.xml_namespaces)[0]
        except IndexError:
            hnd_shinies = None
    except IndexError:
        hnd_agility = None
        hnd_instinct = None
        hnd_leadership = None
        hnd_requires = None
        hnd_shinies = None
        hnd_food = None
        require_list = []
    
    return flask.make_response(json.dumps({
        "id": hnd_id.text,
        "name": hnd_name.text,
        "desc": hnd_desc.text,
        "food": int(hnd_food.text) if hnd_food is not None else 0,
        "shinies": int(hnd_shinies.text) if hnd_shinies is not None else 0,
        "agility": int(hnd_agility.text) if hnd_agility is not None else 0,
        "instinct": int(hnd_instinct.text) if hnd_instinct is not None else 0,
        "leadership": int(hnd_leadership.text) if hnd_leadership is not None else 0,
        "requires": require_list
    }, cls=jsonizer.JSONizer), 200)
