#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Imports
from copy import copy
from json import load, dump, JSONDecodeError
from pathlib import Path
from logging import getLogger, basicConfig
from requests import get
from argparse import ArgumentParser

__version__ = "1.3.0a0"
__author__ = "Josiah Jarvis"

logger = getLogger(__name__)
basicConfig(format="%(levelname)s: %(message)s")

parser = ArgumentParser(prog="mcmu")
group = parser.add_mutually_exclusive_group()
group.add_argument("-u", "--update", help="Updates installed mods", action="store_true")
group.add_argument("-r", "--remove", help="Removes an installed mod")
group.add_argument("-i", "--install", help="Installs a mod")
group.add_argument("-l", "--list", help="List installed mods", action="store_true")
group.add_argument("-s", "--search", help="Search packages on Modrinth")
group.add_argument("-v", "--version", action="version", version=f"MCMU version: {__version__}")
# Unix systems defaults to ~/.minecraft/ for the minecraft dir, I don't know about Windows or MacOS
parser.add_argument("-m", "--minecraft_dir", default=Path(Path.home(), ".minecraft/"), help="Path to the Minecraft folder, defaults to '~/.minecraft/'")
# Game version defaults to 1.21.11 as it is the latest Minecraft release
parser.add_argument("-g", "--game_version", default="26.1", help="The game version to use to install mods, defaults to '26.1'")

args = parser.parse_args()  # Parser the arguments
config_file = Path(args.minecraft_dir, "config/mcmu.json")  # Path to the config file

def check_update(mod_name: str, current_version: str) -> [bool, dict]:
    parameters = {
            "loaders": '["fabric"]',
            "game_versions": f'["{args.game_version}"]',
            "include_changelog": "false"
    }
    print(parameters)
    response = get(f"https://api.modrinth.com/v2/project/{mod_name}/version", params=parameters)
    if response.status_code == 404:
        return False
    project_info = response.json()
    latest_version = None
    for version in project_info:
        if latest_version is None or version["version_number"] > latest_version["version_number"]:
            latest_version = version
    if latest_version is not None and latest_version["version_number"] != current_version:
        return latest_version
    return False

def write_config_file(config_f: Path, config_d: dict):
    try:
        with open(config_f, "w") as fp:
            dump(config_d, fp, indent=4)
            return True
    except FileNotFoundError:
        logger.critical("Config file does not exist.")
        return False
    except PermissionError:
        logger.error("No permission to write to file.")
        return False

def main():
    try:
        with open(config_file, "r") as fp:
            config = load(fp)
    except JSONDecodeError:
        logger.critical("Config file not valid JSON.")
        return 1
    except FileNotFoundError:
        logger.warn("Config file does not exist.")
        with open(config_file, "w+") as fp:
            logger.debug("Creating config file.")
            dump({"mods":{}}, fp, indent=4)
        logger.info("Exiting, please re-run.")
        return 1
    except PermissionError:
        logger.error("No permission to write to file.")
        return 1
    mods = copy(config['mods'])
    mod_path = Path(args.minecraft_dir, "mods/")
    if not mod_path.exists():
        logger.critical(f"Mods folder: {mod_path} does not exist. Please create it.\nExiting...")
        return 1
    if args.update:
        for mod_name in mods:
            latest_version = check_update(mod_name, mods[mod_name]['version'])
            if latest_version:
                if latest_version:
                    if latest_version is None:
                        print("No version found for the specified game version and loader.")
                        return 1
                    old_file = Path(mod_path, mods[mod_name]['file'])
                    additional_storage = latest_version['files'][0]['size'] - old_file.stat().st_size
                    if input(f"{args.install} will take up: {additional_storage} additional bytes, would you like to install? [Y/n]: ") is ("" or "Y"):
                        old_file.unlink()
                        response = get(latest_version['files'][0]['url'], stream=True)
                        if response.status_code != 200:
                            print(f"Failed to download the mod. Status code: {response.status_code}")
                            return False
                        with open(f"{mod_path}/{latest_version['files'][0]['filename']}", 'wb') as file:
                            for chunk in response.iter_content(chunk_size=1024):
                                if chunk:
                                    file.write(chunk)
                        print(f"Downloaded {latest_version['files'][0]['filename']} successfully.")
                        config['mods'][mod_name] = {'file': latest_version['files'][0]['filename'], 'version': latest_version['version_number'], 'name': mod_name}
                        if not write_config_file(config_file, config):
                            return 1
                    else:
                        print("Canceling.")
                        return 0
                else:
                    logger.error("Mod does not exist on Modrinth")
                    return 1
            else:
                print(f"Mod: {mod_name} at latest version!")
    elif args.install:
        if args.install in config['mods']:
            print(f"{args.install} already installed.")
            return 0
        latest_version = check_update(args.install, "0")
        if latest_version:
            if latest_version is None:
                print("No version found for the specified game version and loader.")
                return 1
            if input(f"{args.install} will take up: {latest_version['files'][0]['size']} bytes, would you like to install? [Y/n]: ") is ("" or "Y"):
                response = get(latest_version['files'][0]['url'], stream=True)
                if response.status_code != 200:
                    print(f"Failed to download the mod. Status code: {response.status_code}")
                    return False
                with open(f"{mod_path}/{latest_version['files'][0]['filename']}", 'wb') as file:
                    for chunk in response.iter_content(chunk_size=1024):
                        if chunk:
                            file.write(chunk)
                print(f"Downloaded {latest_version['files'][0]['filename']} successfully.")
                config['mods'][args.install] = {'file': latest_version['files'][0]['filename'], 'version': latest_version['version_number'], 'name': args.install}
                if not write_config_file(config_file, config):
                    return 1
            else:
                print("Canceling.")
                return 0
        else:
            logger.error("Mod does not exist on Modrinth")
            return 1
    elif args.remove:
        if args.remove not in config['mods']:
            logger.error("Mod not installed")
            return 1
        try:
            mod_file = Path(mod_path, config['mods'][args.remove]['file'])
            if input(f"Would you like to remove {args.remove}? This operation will clear {mod_file.stat().st_size} bytes. [Y/n]: ") is ("" or "Y"):
                mod_file.unlink()
            else:
                print("Canceling.")
                return 0
        except FileNotFoundError:
            logger.warn("Mod's file already deleted.")
        except PermissionError:
            logger.critical("No permission to delete mod file.")
            return 1
        del config['mods'][args.remove]
        if not write_config_file(config_file, config):
            return 1
        print(f"Mod: {args.remove}, successfully removed")
    elif args.list:
        for mod in config['mods']:
            print(f"{config['mods'][mod]['name']}\n\tVersion: {config['mods'][mod]['version']}\n\tFile: {config['mods'][mod]['file']}")
    elif args.search:
        parameters = {
            "query": args.search,
            "facets": '[["categories:fabric"],["project_type:mod"]]'
        }
        response = get("https://api.modrinth.com/v2/search", params=parameters)
        if response.status_code == 400:
            print(f"Query failed with error: {response.text}")
        else:
            for mod in response.json()['hits']:
                print(f"{mod['title']}:\n\tDescription: {mod['description']}\n\tAuthor: {mod['author']}\n\tDownloads: {mod['downloads']}\n\tLatest Version: {mod['latest_version']}\n\n")
    else:
        parser.print_help()  # Prints help message if there is nothing to do

    return 0 # Return 0 if all good
