#!/usr/bin/env python

import os
import re
import datetime
import time
import glob
import zipfile
import sys
import json
import argparse
import logging

# python import foo
local_dir = os.path.realpath(
    os.path.join(os.path.dirname(__file__), "..", ".."))
sys.path.insert(0, local_dir)

import stir.utils as utils

# import raw_input

logger = logging.getLogger("stir")
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.INFO)

DEFAULT_VERSION = "0.0.1"
DEFAULT_PATTERNS = ["**"]
SOURCE_NAME = "stir-define.json"
TARGET_NAME = "stir-requirements.json"
MANIFEST_NAME = "stir-installed.json"


class Server():

    def __init__(self, host, port):
        self.host = host
        self.port = port

    def get_best_version(self, version):
        return "0.0.1"

    def get_package_data(self, name, version):
        best_v = self.get_best_version(version)
        dpath = "%s-%s.json" % (name, best_v)
        return utils.json_load(dpath)

    def get_package_zip(self, name, version):
        best_v = self.get_best_version(version)
        zpath = "%s-%s.zip" % (name, best_v)
        return zpath


def command_update(args):

    if args.recursive:
        logger.info("finding all stir.json files")
        file_datas = utils.get_stir_packages()
    else:
        file_path = utils.get_stir_file_path()
        file_datas = [utils.json_load(file_path)]
        file_datas[0]["stir_file"] = file_path

    args.packages = [utils.clean_package_name(p) for p in args.packages]
    if args.packages:
        package_names = utils.get_valid_package_names(
            args.packages, file_datas)
    else:
        package_names = None

    for fdata in file_datas:
        for package in fdata["packages"]:
            if args.packages:
                if package["name"] in package_names:
                    update(package, fdata["stir_file"], args.no_increment)
            else:
                update(package, fdata["stir_file"], args.no_increment)


def update(package, file_path, no_increment_version):

    logger.info("updating %s from %s", package["name"], file_path)
    source_dir = os.path.dirname(file_path)
    logger.info("> finding files")
    file_list = utils.find_files_chroot(
        source_dir, package["patterns"], ["**stir.json"])
    if len(file_list) < 1:
        raise Exception("packages need at least 1 file")

    package["files"] = file_list
    package["last_update"] = int(time.time())

    if not no_increment_version:
        package["version"] = utils.increment_tiny(package["version"])
        logger.info("> incremented version to %s", package["version"])

    file_data = utils.json_load(file_path)
    for pdata in file_data["packages"]:
        if pdata["name"] == package["name"]:
            file_data["packages"].remove(pdata)

    file_data["packages"].append(package)

    logger.info("> saving to %s", file_path)
    utils.json_save(file_path, file_data)


def command_upsert(args):

    file_path = utils.get_stir_file_path()
    if not os.path.exists(file_path):
        if not args.yes:
            utils.get_yn(
                "stir.json not found, create it?", exit_on_n=True)
        utils.json_save(file_path, {"packages": []})

    upsert(
        name=args.package,
        patterns=args.patterns,
        version=args.version)


def upsert(name, patterns=None, version=None):

    file_path = utils.get_stir_file_path()
    source_dir = os.path.dirname(file_path)

    defaults = {
        "patterns": DEFAULT_PATTERNS,
        "version": DEFAULT_VERSION,
        "last_publish": None,
    }

    name = utils.clean_package_name(name)

    logger.info("adding new package: %s", name)

    file_data = utils.json_load(file_path)

    for pdata in file_data["packages"]:
        if pdata["name"] != name:
            continue
        defaults["version"] = utils.increment_tiny(pdata["version"])
        defaults["last_publish"] = pdata["last_publish"]
        file_data["packages"].remove(pdata)

    file_list = utils.find_files_chroot(
        source_dir, patterns or defaults["patterns"], ["**stir.json"])

    print(file_list)

    if len(file_list) < 1:
        raise Exception("packages must contain at least 1 file")

    package_data = {
        "name": name,
        "version": version or defaults["version"],
        "patterns": patterns or defaults["patterns"],
        "files": file_list,
        "last_publish": defaults["last_publish"],
        "last_update": int(time.time()),
    }

    file_data["packages"].append(package_data)
    utils.json_save(file_path, file_data)
    logger.info("> saved %s (%s)", name, package_data["version"])
    logger.debug(
        "> saved package as:\n%s", json.dumps(package_data, indent=2))


def command_pull(args):
    pass


def command_publish(args):

    if args.recursive:
        logger.info("finding all stir.json files")
        file_datas = utils.get_stir_packages()
    else:
        file_path = utils.get_stir_file_path()
        file_datas = [utils.json_load(file_path)]
        file_datas[0]["stir_file"] = file_path

    args.packages = [utils.clean_package_name(p) for p in args.packages]
    if args.packages:
        package_names = utils.get_valid_package_names(
            args.packages, file_datas)
    else:
        package_names = None

    for fdata in file_datas:
        for package in fdata["packages"]:
            if args.packages:
                if package["name"] in package_names:
                    publish(package, fdata["stir_file"])
            else:
                publish(package, fdata["stir_file"])


def publish(package, file_path):

    logger.info(
        "publishing package %s-%s", package["name"], package["version"])

    package_dir = os.path.dirname(file_path)

    if package["last_publish"] and \
            package["last_publish"] >= package["last_update"]:
        logger.info(
            "> package has not been updated since it was last published")
        return

    file_data = utils.json_load(file_path)

    for pdata in file_data["packages"]:
        if pdata["name"] == package["name"]:
            pdata["last_publish"] = int(time.time())
            package = pdata
            break

    zip_name = "%s-%s.zip" % (package["name"], package["version"])
    zip_path = os.path.join(package_dir, zip_name)
    logger.debug("> creating temporary zip: %s", zip_path)

    utils.zipfile_create_chroot(package_dir, zip_path, package["files"])

    logger.info("> pushing:\n%s", json.dumps(package, indent=2))

    logger.info("> saving to %s", file_path)
    utils.json_save(file_path, file_data)

def command_remove(args):
    pass


def cmp(a, b):
    """ trick to get python3 working with cmp """
    return (a > b) - (a < b)


def main():
    # print(find_files(path="../../", patterns=["*"]))
    default_server = os.environ.get("STIR_SERVER", "localhost")
    parser = argparse.ArgumentParser(description="interact with stir packages")
    parser.add_argument(
        "-s", "--server",
        help="the server to use [env LIBGET_SERVER], default: %s" %
        default_server, default=default_server)
    parser.add_argument(
        "-y", "--yes", action="store_true",
        help="force yes or default on questions")
    parser.add_argument(
        "-v", "--verbose", action="store_true",
        help="increase logging output")

    subparsers = parser.add_subparsers()

    upsert = subparsers.add_parser(
        "upsert", help="add or update a package")
    upsert.add_argument("package", help="package name to add or update")
    upsert.add_argument("-p", "--patterns",
                        help="patterns, use '**' for recursion", nargs="*")
    upsert.add_argument("-v", "--version", help="manually specify a version")
    upsert.set_defaults(func=command_upsert)

    update = subparsers.add_parser("update", help="updates source packages")
    update.add_argument("packages", help="packages to update", nargs="*")
    update.add_argument(
        "-r", "--recursive", action="store_true",
        help="recursively search for packages to update")
    update.add_argument("-n", "--no-increment", action="store_true",
                        help="do not increment version numbers")
    update.set_defaults(func=command_update)

    # Pull
    install = subparsers.add_parser("install", help="install a package")

    # Uninstall
    uninstall = subparsers.add_parser("uninstall", help="uninstall a package")

    # Push
    publish = subparsers.add_parser(
        "publish", help="push packages to the server")
    publish.add_argument(
        "packages",
        help="package(s) to publish. publish all packages if blank.",
        nargs="*")
    publish.add_argument(
        "-r", "--recursive", action="store_true",
        help="recursivly look for packages to publish")
    publish.set_defaults(func=command_publish)

    # Remove
    unpublish = subparsers.add_parser(
        "unpublish", help="remove a package from the server")

    args = parser.parse_args()
    if args.verbose:
        logger.setLevel(logging.DEBUG)
        logger.debug("set to verbose output")

    args.func(args)


if __name__ == "__main__":
    main()
