import gzip
import json
import platform
import UnityPy
from datetime import datetime

from .statCalculation import get_all_stats, fill_miracle_numbers
from .endpoints.friend import KF3_Friend
from .endpoints.friends import KF3_Friends
from .endpoints.item import KF3_Item
from .endpoints.items import KF3_Items
from .endpoints.update import KF3_Update

class Database:
    def __init__(self, api) -> None:
        app = api.app
        if "KF3" in app.databases:
            del app.databases["KF3"]

        self.reload_data()

        app.databases["KF3"] = self
        api.add_resource(KF3_Friend, "/KF3/Friend/<int:id>")
        api.add_resource(KF3_Friends, "/KF3/Friends")
        api.add_resource(KF3_Item, "/KF3/Item/<int:id>")
        api.add_resource(KF3_Items, "/KF3/Items")
        api.add_resource(KF3_Update, "/KF3/Update")

    def unload_data(self):
        self.charaData = {}
        self.itemCommon = {}
        self.promoteData = {}
        self.charaClothesData = {}
        self.promotePresetData = {}
        self.limitlevel_rising_status = {}

        self.paramArts = {}
        self.paramAbilities = {}
        self.paramAbilities1 = {}
        self.paramAbilities2 = {}
        self.paramAlphaBases = {}
        self.paramWaitActions = {}
        self.paramSpecialAttacks = {}

        self.processed_friends = {}

    def reload_data(self):
        self.unload_data()

        if platform.system() == "Windows":
            path = "H:\\Apache\\Katworks\\KF\\assets\\KF3\\cache\\"
        else:
            path = "/var/www/html/Katworks/KF/assets/KF3/develop01/cache/"

        with gzip.open(path + "CHARA_DATA.d", mode="rt", encoding="utf-8") as file:
            for entry in json.loads(file.read()):
                self.charaData[entry["id"]] = entry

        with gzip.open(path + "ITEM_COMMON.d", mode="rt", encoding="utf-8") as file:
            for entry in json.loads(file.read()):
                self.itemCommon[entry["id"]] = entry

        with gzip.open(path + "CHARA_PROMOTE_DATA.d", mode="rt", encoding="utf-8") as file:
            for entry in json.loads(file.read()):
                self.promoteData[entry["promoteId"]] = entry

        with gzip.open(path + "CHARA_PROMOTE_PRESET_DATA.d", mode="rt", encoding="utf-8") as file:
            for entry in json.loads(file.read()):
                if entry["promotePresetId"] not in self.promotePresetData:
                    self.promotePresetData[entry["promotePresetId"]] = []
                self.promotePresetData[entry["promotePresetId"]].append(entry)

        with gzip.open(path + "CHARA_CLOTHES_DATA.d", mode="rt", encoding="utf-8") as file:
            for entry in json.loads(file.read()):
                if entry["clothesPresetId"] not in self.charaClothesData:
                    self.charaClothesData[entry["clothesPresetId"]] = []
                self.charaClothesData[entry["clothesPresetId"]].append(entry)

        with gzip.open(path + "LIMITLEVEL_RISING_STATUS.d", mode="rt", encoding="utf-8") as file:
            for entry in json.loads(file.read()):
                self.limitlevel_rising_status[entry["patternId"]] = entry

        self.parse_parameter()
        self.process_friends()
        print("Data reload finished")
        self.update_in_progress = False

    def parse_parameter(self):
        if platform.system() == "Windows":
            path = "H:\\Apache\\Katworks\\KF\\assets\\KF3\\assets\\"
        else:
            path = "/var/www/html/Katworks/KF/assets/KF3/develop01/assets/Windows/"

        paramAsset = UnityPy.load(path + "parameter.asset")
        for obj in paramAsset.objects:
            data = obj.read()
            if data.name.split('_')[0] == "ParamAbility":
                id = int(data.name.split('_')[1])
                if data.name.endswith("_1"):
                    self.paramAbilities1[id] = obj.read_typetree()
                elif data.name.endswith("_2"):
                    self.paramAbilities2[id] = obj.read_typetree()
                else:
                    self.paramAbilities[id] = obj.read_typetree()
            elif data.name.split('_')[0] == "ParamAlphaBase":
                id = int(data.name.split('_')[1])
                self.paramAlphaBases[id] = obj.read_typetree()
            elif data.name.split('_')[0] == "ParamArts":
                id = int(data.name.split('_')[1])
                self.paramArts[id] = obj.read_typetree()
            elif data.name.split('_')[0] == "ParamSpecialAttack":
                id = int(data.name.split('_')[1])
                self.paramSpecialAttacks[id] = obj.read_typetree()
            elif data.name.split('_')[0] == "ParamWaitAction":
                id = int(data.name.split('_')[1])
                self.paramWaitActions[id] = obj.read_typetree()

    def process_friends(self):
        for charaID in self.charaData:
            self.processed_friends[charaID] = self.process_chara(charaID)

    def process_chara(self, id : int):
        chara = {}
        
        charaData = self.charaData[id]
        alphaBase = self.paramAlphaBases[id] if id in self.paramAlphaBases else None

        wrLocked = False
        promoIds = []
        promotePresetId = charaData["promotePresetId"]
        promos = self.promotePresetData[promotePresetId] if promotePresetId in self.promotePresetData else []
        for promo in promos:
            if promo["promoteStepDatetime"] == 1917860400000:
                wrLocked = True
                continue
            else:
                promoIds.append([promo["promoteId00"], promo["promoteId01"], promo["promoteId02"], promo["promoteId03"], promo["promoteId04"], promo["promoteId05"]])

        emptyPromote = {"promoteAtk": 0, "promoteDef": 0, "promoteHp": 0, "promoteAvoid": 0, "promoteActionDamageRatio": 0, "promoteBeatDamageRatio": 0, "promoteTryDamageRatio": 0}
        promoteDatas = [[(self.promoteData[id] if id in self.promoteData else emptyPromote) for id in promo] for promo in promoIds]
        promote_bonus = {"atk" : 0, "def" : 0, "hp" : 0, "evd" : 0, "beat" : 0, "act" : 0, "try" : 0}
        for promoTier in promoteDatas:
            for promoteStep in promoTier:
                    promote_bonus["atk"] += promoteStep["promoteAtk"] 
                    promote_bonus["def"] += promoteStep["promoteDef"] 
                    promote_bonus["hp"] += promoteStep["promoteHp"] 
                    promote_bonus["evd"] += promoteStep["promoteAvoid"] 
                    promote_bonus["act"] += promoteStep["promoteActionDamageRatio"] 
                    promote_bonus["beat"] += promoteStep["promoteBeatDamageRatio"] 
                    promote_bonus["try"] += promoteStep["promoteTryDamageRatio"]

        clothesPresetId = charaData["clothesPresetId"]
        clothesDatas = self.charaClothesData[clothesPresetId] if clothesPresetId in self.charaClothesData else []
        costume_bonus = {"atk" : 0, "def" : 0, "hp" : 0, "evd" : 0, "beat" : 0, "act" : 0, "try" : 0}
        for clothesData in clothesDatas:
            costume_bonus["atk"] += clothesData["atkBonus"] 
            costume_bonus["def"] += clothesData["defBonus"] 
            costume_bonus["hp"] += clothesData["hpBonus"] 

        chara["id"] = id
        chara["promoteBonus"] = promote_bonus
        chara["costumeBonus"] = costume_bonus
        chara["attribute"] = charaData["attribute"]
        chara["name"] = charaData["name"]
        chara["nameEn"] = charaData["nameEn"]
        chara["nickname"] = charaData["nickname"]
        chara["rankLow"] = charaData["rankLow"]
        chara["rankHigh"] = charaData["rankHigh"]
        chara["castName"] = charaData["castName"]
        chara["loginText"] = charaData["loginText"]
        chara["flavorText"] = charaData["flavorText"]
        chara["kizunaupText"] = charaData["kizunaupText"]
        chara["greetingText"] = charaData["greetingText"]
        chara["startTimeRaw"] = charaData["startTime"]
        chara["startTime"] = datetime.fromtimestamp(charaData["startTime"]/1000.0).strftime('%A, %B %d, %Y')

        chara["wr"] = 4 if wrLocked else 5
        chara["is_wr5"] = chara["wr"] == 5
        chara["has_party_dress"] = False
        for clothesData in clothesDatas:
            if clothesData["clothesId"] == 8:
                chara["has_party_dress"] = True
                break
        chara["has_rainbow_trait"] = id in self.paramAbilities2

        chara["risingStatusPatternId"] = patternId = charaData["risingStatusPatternId"]

        if patternId not in self.limitlevel_rising_status:
            level_curve = None
        else:
            level_curve = self.limitlevel_rising_status[patternId] if patternId != 0 else None

        if alphaBase is None:
            chara["stats_min"] = {"level" : 0,"status" : 0,"wr" : 0,"hp" : 0,"atk" : 0,"def" : 0,"evd" : 0,"beat" : 0,"act" : 0,"try" : 0}
            chara["stats_max"] = {"level" : 0,"status" : 0,"wr" : 0,"hp" : 0,"atk" : 0,"def" : 0,"evd" : 0,"beat" : 0,"act" : 0,"try" : 0}
            chara["plasmPoint"] = 0
            chara["cards"] = {0,0,0,0,0}
        else:
            chara["stats_min"] = get_all_stats(chara, alphaBase, max_level = False, rising_status_pattern=level_curve)
            chara["stats_max"] = get_all_stats(chara, alphaBase, max_level = True, rising_status_pattern=level_curve)
            chara["plasmPoint"] = alphaBase["plasmPoint"]
            chara["cards"] = [
                {"type":alphaBase["orderCardType00"], "value":alphaBase["orderCardValue00"]},
                {"type":alphaBase["orderCardType01"], "value":alphaBase["orderCardValue01"]},
                {"type":alphaBase["orderCardType02"], "value":alphaBase["orderCardValue02"]},
                {"type":alphaBase["orderCardType03"], "value":alphaBase["orderCardValue03"]},
                {"type":alphaBase["orderCardType04"], "value":alphaBase["orderCardValue04"]}
            ]

        chara["synergy_flag"] = self.paramArts[id]["authParam"]["SynergyFlag"] if id in self.paramArts else 0
        
        chara["arts"] = self.paramArts[id] if id in self.paramArts else None
        chara["ability"] = self.paramAbilities[id] if id in self.paramAbilities else None
        chara["ability1"] = self.paramAbilities1[id] if id in self.paramAbilities1 else None
        chara["ability2"] = self.paramAbilities2[id] if id in self.paramAbilities2 else None
        chara["wait_action"] = self.paramWaitActions[id] if id in self.paramWaitActions else None
        chara["special_attack"] = self.paramSpecialAttacks[id] if id in self.paramSpecialAttacks else None

        chara["costumes"] = self.get_costumes(id, chara["rankLow"])

        return chara
    
    def get_item(self, id : int):
        if id in self.itemCommon:
            return self.itemCommon[id]
        else:
            return None

    def get_chara(self, id : int):
        if id in self.processed_friends:
            return self.processed_friends[id]
        else:
            return None

    def get_chara_wiki(self, id : int):
        chara = self.get_chara(id)
        if chara is None:
            return None

        lines = []
        
        stars_word = chara["rankLow"]
        stars_word = "Two" if stars_word == 2 else "Three" if stars_word == 3 else "Four" if stars_word == 4 else "Five" if stars_word == 5 else "Unknown"
        
        attribute_word = chara["attribute"]
        attribute_word = "Funny" if attribute_word == 1 else "Friendly" if attribute_word == 2 else "Relaxed" if attribute_word == 3 else "Lovely" if attribute_word == 4 else "Active" if attribute_word == 5 else "Carefree" if attribute_word == 6 else "Unknown"

        categories = [
            f"[[Category:{attribute_word} KF3 Friends]]",
            f"[[Category:{stars_word} Star KF3 Friends]]",
            f"[[Category:Missing Content]]",
            f"[[Category:Needs Audio]]",
            f"{{{{#vardefine:id|{str(chara['id']).zfill(4)}}}}}"
        ]
        
        if chara['has_party_dress']:
            categories.append("[[Category:Party Dress KF3 Friends]]")
        if chara['is_wr5']:
            categories.append("[[Category:Wild Release 5 KF3 Friends]]")
        if chara['has_rainbow_trait']:
            categories.append("[[Category:Rainbow Trait KF3 Friends]]")

        lines.append(" ".join(categories))
        lines.append("")
        lines.append("{{FriendBox/KF3")
        lines.append(f"|name={chara['nameEn'].replace('_', ' ')}")
        lines.append(f"|apppic={chara['nameEn'].replace('_', ' ')}KF3.png")
        lines.append(f"|apprarity={{{{KF3{chara['rankLow']}Star}}}}")
        lines.append(f"|seiyuu={chara['castName']}")
        lines.append(f"|attribute={attribute_word} {{{{KF3{attribute_word}}}}}")
        lines.append(f"|implemented={chara['startTime']} (App)")
        lines.append(f"|id={chara['id']}")
        lines.append(f"|wr5={'Yes' if chara['is_wr5'] else 'No'}")
        lines.append(f"|rainbowtrait={'Yes' if chara['has_rainbow_trait'] else 'No'}")
        lines.append(f"|partydress={'Yes' if chara['has_party_dress'] else 'No'}")
        lines.append("}}")
        lines.append("")
        lines.append("{{FriendBuilder/KF3")
        lines.append(f"|introduction = '''{chara['nameEn'].replace('_', ' ')}''' is a Friend that appears in the app version of [[Kemono Friends 3]].")
        lines.append(f"|status={chara['stats_min']['status']}")
        lines.append(f"|hp={chara['stats_min']['hp']}")
        lines.append(f"|atk={chara['stats_min']['atk']}")
        lines.append(f"|def={chara['stats_min']['def']}")
        lines.append(f"|evd={round(chara['stats_min']['evd']/10,1)}%")
        lines.append(f"|beat={chara['stats_min']['beat']}%")
        lines.append(f"|action={chara['stats_min']['act']}%")
        lines.append(f"|try={chara['stats_min']['try']}%")
        lines.append(f"|plasm={chara['plasmPoint']}")
        lines.append(f"|maxstatus={chara['stats_max']['status']}")
        lines.append(f"|maxhp={chara['stats_max']['hp']}")
        lines.append(f"|maxatk={chara['stats_max']['atk']}")
        lines.append(f"|maxdef={chara['stats_max']['def']}")
        lines.append(f"|maxevd={round(chara['stats_max']['evd']/10,1)}%")
        lines.append(f"|maxbeat={chara['stats_max']['beat']}%")
        lines.append(f"|maxaction={chara['stats_max']['act']}%")
        lines.append(f"|maxtry={chara['stats_max']['try']}%")
        lines.append("")
        cards_str = "|flags="
        for card in chara["cards"]:
            cards_str += " {{"
            if card["type"] == 1:
                cards_str += "Beat}}"
            elif card["type"] == 2:
                cards_str += f"Action{card['value']}}}}}"
            elif card["type"] == 3:
                if card["value"] == 20:
                    cards_str += "TryLow}}"
                if card["value"] == 30:
                    cards_str += "TryMiddle}}"
                if card["value"] == 40:
                    cards_str += "TryHigh}}"
        lines.append(cards_str)
        cardType = "Beat" if chara["synergy_flag"] == 1 else "Action20" if chara["synergy_flag"] == 2 else "TryHigh" if chara["synergy_flag"] == 3 else ""
        lines.append(f"|miracleplus={{{{{cardType}}}}}")

        if chara["arts"] is not None:
            miracles = fill_miracle_numbers(chara)
            lines.append(f"|miracle={chara['arts']['actionName']}")
            for idx,miracle in enumerate(miracles):
                lines.append("|miracle" + str(idx + 1) + "=" + miracle.replace("\r\n", "\n").replace("\\n", "\n").replace("\\", " "))

        if chara["special_attack"] is not None:
            lines.append(f"|beatname={chara['special_attack']['actionName']}")
            lines.append(f"|beatskill={chara['special_attack']['actionEffect']}")
        if chara["wait_action"] is not None:
            lines.append(f"|standby={chara['wait_action']['skillName']}")
            lines.append(f"|standbyskill={chara['wait_action']['skillEffect']}")
        if chara["ability"] is not None:
            lines.append(f"|unique={chara['ability']['abilityName']}")
            lines.append(f"|uniqueskill={chara['ability']['abilityEffect']}")
        if chara["ability1"] is not None:
            lines.append(f"|miracletrait={chara['ability1']['abilityName']}")
            lines.append(f"|miracletraitskill={chara['ability1']['abilityEffect']}")
        else:
            lines.append(f"|miracletrait=N/A")
            lines.append(f"|miracletraitskill=Not Implemented.")
        if chara["ability2"] is not None:
            lines.append(f"|rainbowtrait={chara['ability2']['abilityName']}")
            lines.append(f"|rainbowtraitskill={chara['ability2']['abilityEffect']}")
        else:
            lines.append(f"|rainbowtrait=N/A")
            lines.append(f"|rainbowtraitskill=Not Implemented.")

        lines.append("")
        lines.append(f"|bondup={chara['kizunaupText']}")
        lines.append(f"|profile={chara['flavorText']}")
        lines.append(f"|greeting={chara['loginText']}")
        lines.append(f"|selfintro={chara['greetingText']}")
        lines.append(f"|enselfintro=")

        cos_images = "|cos = " + ",".join([costume["image_name"] for costume in chara["costumes"]]) + ",end"
        cos_obtain = "|cosobt = " + ",".join([costume["obtain"] for costume in chara["costumes"]])
        cos_names = "|cosname = " + ",".join([costume["name"] for costume in chara["costumes"]])

        lines.append("")
        lines.append(cos_images)
        lines.append(cos_names)
        lines.append(cos_obtain)

        lines.append("}}")

        return "\n".join(lines)
    
    def get_costumes(self, id : int, defaultRarity : int):
        if id not in self.charaClothesData:
            return []
        
        output = []
        data = self.charaClothesData[id]
        for costume in data:
            cos = {
                "image_name" : f"icon dressup {costume['id']}.png",
                "obtain" : "Limited"
            }
            cosId = costume['clothesId']
            if cosId == 1:
                cos["name"] = "Default"
                cos["obtain"] = "Default"

            elif cosId == 2:
                cos["name"] = "Personal Fashion"
                cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default"

            elif cosId == 3:
                cos["name"] = "Tracksuit"
                cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default"
                
            elif cosId == 4:
                cos["name"] = "Park Staff"
                cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default"
                
            elif cosId == 5:
                cos["name"] = "Café Uniform"
                cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default"
                
            elif cosId == 6:
                cos["name"] = "Special Outfit 1"
                
            elif cosId == 8:
                cos["name"] = "Party Dress"
                cos["obtain"] = "Increase Photo Pocket level to 12"
                
            elif cosId == 9:
                cos["name"] = "School Uniform"
                
            elif cosId == 10:
                cos["name"] = "Gym Clothes"
                
            elif cosId == 11:
                cos["name"] = "Raincoat"
                
            elif cosId == 12:
                cos["name"] = "Hanamaru Animal Swimsuit"
                
            elif cosId == 13:
                cos["name"] = "Cafe Uniform (Green)"
                cos["obtain"] = "Dojo reward"
                
            elif cosId == 14:
                cos["name"] = "Cafe Uniform (Blue)"
                cos["obtain"] = "Dojo reward"
                
            elif cosId == 15:
                cos["name"] = "Cafe Uniform (Pink)"
                cos["obtain"] = "Dojo reward"
                
            elif cosId == 16:
                cos["name"] = "Orihime Costume"
                
            elif cosId == 17:
                cos["name"] = "Arle's Outfit"
                
            elif cosId == 18:
                cos["name"] = "Marine Nanoda! Swimsuit"
                
            elif cosId == 19:
                cos["name"] = "Batten Japari Dan Swimsuit"
                
            elif cosId == 20:
                cos["name"] = "Lifeguard Swimsuit"
                
            elif cosId == 21:
                cos["name"] = "Harvest Festival Outfit"
                
            elif cosId == 22:
                cos["name"] = "Halloween Costume"
                
            elif cosId == 23:
                cos["name"] = "Classic Maid Uniform"
                
            elif cosId == 24:
                cos["name"] = "Arle's Outfit"
                
            elif cosId == 25:
                cos["name"] = "Swimsuit"
                
            elif cosId == 26:
                cos["name"] = "Swimsuit"
                
            elif cosId == 27:
                cos["name"] = "Swimsuit"
                
            elif cosId == 28:
                cos["name"] = "Matching Clothes"
                
            elif cosId == 29:
                cos["name"] = "Frilly Frilly PPP Bikini"
                
            elif cosId == 30:
                cos["name"] = "Venus Flower Bikini"
                
            elif cosId == 31:
                cos["name"] = "Christmas Dress"
                
            elif cosId == 32:
                cos["name"] = "Chinese Shrine Maiden"
                
            elif cosId == 33:
                cos["name"] = "Chinese Dress"
                
            elif cosId == 34:
                cos["name"] = "Hanamaru Stage Costume"
                
            elif cosId == 35:
                cos["name"] = "Camo Outfit"
                
            elif cosId == 36:
                cos["name"] = "でびでび・ふりる"
                
            elif cosId == 37:
                cos["name"] = "夜に紛れし闇の衣"
                
            elif cosId == 38:
                cos["name"] = "Santa"
                
            elif cosId == 39:
                cos["name"] = "Fluffy Pajamas"
                
            elif cosId == 40:
                cos["name"] = "Japanese Armor"
                
            elif cosId == 41:
                cos["name"] = "Miko Outfit"
                
            elif cosId == 42:
                cos["name"] = "PPP Hoodie"
                
            elif cosId == 43:
                cos["name"] = "Blue Tracksuit"
                
            elif cosId == 44:
                cos["name"] = "Park Staff (beige)"
                
            elif cosId == 45:
                cos["name"] = "Sailor School Uniform"
                
            elif cosId == 46:
                cos["name"] = "Snow Knight Dress"
                
            elif cosId == 47:
                cos["name"] = "Sparkling Tree Dress"
                
            elif cosId == 48:
                cos["name"] = "Retro Detective"
                
            elif cosId == 49:
                cos["name"] = "Steampunk Detective"
                
            elif cosId == 50:
                cos["name"] = "鞠と花びらのはんなり和風服"
                
            elif cosId == 51:
                cos["name"] = "Numazu Deep Sea Aquarium Clothes"
                
            elif cosId == 52:
                cos["name"] = "たのしいことをお届け!"

            elif cosId == 53:
                cos["name"] = "かわいい勝負ですよ!ワンピース水着"

            elif cosId == 54:
                cos["name"] = "どきどき♡ずっきゅんビキニ"

            elif cosId == 55:
                cos["name"]= "のんほいパークの服"

            elif cosId == 56:
                cos["name"]= "なないろのはごろも"

            elif cosId == 57:
                cos["name"]= "4周年!ハッピーラッキーTシャツ"
                
            elif cosId == 58:
                cos["name"]= "純情色のワンピース"

            else:
                print(f"{costume['clothesId']}\t{costume['id']}\t{costume['name']}")
                continue

            output.append(cos)
        return output