import os
import flask
import git
import mimetypes
import magic
from markupsafe import escape

import config

app = flask.Flask(__name__)
import gitHTTP

mime = magic.Magic(mime=True)


def humanSize(value, decimals=2, scale=1024, units=("B", "kiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB")):
    for unit in units:
        if value < scale:
            break
        value /= scale
    if int(value) == value:
        # do not return decimals, if the value is already round
        return int(value), unit
    return round(value * 10**decimals) / 10**decimals, unit


def guessMIME(path):
    if os.path.isdir(path):
        mimetype = "inode/directory"
    elif mime.from_file(path):
        mimetype = mime.from_file(path)
    else:
        mimetype = "application/octet-stream"
    return mimetype


def convertToHTML(path):
    with open(path, "r") as f:
        contents = f.read()
    return contents


@app.route("/")
def main():
    return flask.render_template("home.html", title="gitme")


@app.route("/accounts/", methods=["GET", "POST"])
def login():
    return flask.render_template("login.html", title="gitme")


@app.route("/<username>/")
def userProfile(username):
    return flask.render_template("teapot.html"), 418


@app.route("/<username>/<repository>/")
def repositoryIndex(username, repository):
    return flask.redirect("./tree", code=302)


@app.route("/<username>/<repository>/raw/<branch>/<path:subpath>")
def repositoryRaw(username, repository, branch, subpath):
    serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository))

    app.logger.info(f"Loading {serverRepoLocation}")

    if not os.path.exists(serverRepoLocation):
        app.logger.error(f"Cannot load {serverRepoLocation}")
        return flask.render_template("not-found.html"), 404

    repo = git.Repo(serverRepoLocation)
    try:
        repo.git.checkout(branch)
    except git.exc.GitCommandError:
        return flask.render_template("not-found.html"), 404

    return flask.send_from_directory(config.REPOS_PATH, os.path.join(username, repository, subpath))


@app.route("/<username>/<repository>/tree/", defaults={"branch": None, "subpath": ""})
@app.route("/<username>/<repository>/tree/<branch>/", defaults={"subpath": ""})
@app.route("/<username>/<repository>/tree/<branch>/<path:subpath>")
def repositoryTree(username, repository, branch, subpath):
    serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository))

    app.logger.info(f"Loading {serverRepoLocation}")

    if not os.path.exists(serverRepoLocation):
        app.logger.error(f"Cannot load {serverRepoLocation}")
        return flask.render_template("not-found.html"), 404

    repo = git.Repo(serverRepoLocation)
    if not branch:
        branch = repo.heads[0].name
        return flask.redirect(f"./{branch}", code=302)
    try:
        repo.git.checkout(branch)
    except git.exc.GitCommandError:
        return flask.render_template("not-found.html"), 404

    branches = repo.heads
    if os.path.isdir(os.path.join(serverRepoLocation, subpath)):
        files = []
        blobs = []

        for entry in os.listdir(os.path.join(serverRepoLocation, subpath)):
            if not os.path.basename(entry) == ".git":
                files.append(os.path.join(subpath, entry))

        infos = []

        for file in files:
            path = os.path.join(serverRepoLocation, file)
            mimetype = guessMIME(path)

            info = {
                "name": os.path.basename(file),
                "serverPath": path,
                "relativePath": file,
                "link": os.path.join(f"/{username}/{repository}/tree/{branch}/", file),
                "size": humanSize(os.path.getsize(path)),
                "mimetype": f"{mimetype}{f' ({mimetypes.guess_type(path)[1]})' if mimetypes.guess_type(path)[1] else ''}",
            }

            specialIcon = config.matchIcon(os.path.basename(file))
            if specialIcon:
                info["icon"] = specialIcon
            elif os.path.isdir(path):
                info["icon"] = config.folderIcon
            elif mimetypes.guess_type(path)[0] in config.fileIcons:
                info["icon"] = config.fileIcons[mimetypes.guess_type(path)[0]]
            else:
                info["icon"] = config.unknownIcon

            if os.path.isdir(path):
                infos.insert(0, info)
            else:
                infos.append(info)

        return flask.render_template(
                "repo-tree.html",
                username=username,
                repository=repository,
                files=infos,
                subpath=os.path.join("/", subpath),
                branches=branches,
                current=branch
        )
    else:
        path = os.path.join(serverRepoLocation, subpath)

        if not os.path.exists(path):
            return flask.render_template("not-found.html"), 404

        mimetype = guessMIME(path)
        mode = mimetype.split("/", 1)[0]
        size = humanSize(os.path.getsize(path))

        specialIcon = config.matchIcon(os.path.basename(path))
        if specialIcon:
            icon = specialIcon
        elif os.path.isdir(path):
            icon = config.folderIcon
        elif mimetypes.guess_type(path)[0] in config.fileIcons:
            icon = config.fileIcons[mimetypes.guess_type(path)[0]]
        else:
            icon = config.unknownIcon

        contents = None
        if mode == "text":
            contents = convertToHTML(path)

        return flask.render_template(
                "repo-file.html",
                username=username,
                repository=repository,
                file=os.path.join(f"/{username}/{repository}/raw/{branch}/", subpath),
                branches=branches,
                current=branch,
                mode=mode,
                mimetype=mimetype,
                size=size,
                icon=icon,
                subpath=os.path.join("/", subpath),
                basename=os.path.basename(path),
                contents=contents
        )


@app.route("/<username>/<repository>/forum/")
def repositoryForum(username, repository):
    return flask.render_template("repo-forum.html", username=username, repository=repository)


@app.route("/<username>/<repository>/docs/")
def repositoryDocs(username, repository):
    return flask.render_template("repo-docs.html", username=username, repository=repository)


@app.route("/<username>/<repository>/releases/")
def repositoryReleases(username, repository):
    return flask.render_template("repo-releases.html", username=username, repository=repository)


@app.route("/<username>/<repository>/branches/")
def repositoryBranches(username, repository):
    return flask.render_template("repo-branches.html", username=username, repository=repository)


@app.route("/<username>/<repository>/people/")
def repositoryPeople(username, repository):
    return flask.render_template("repo-people.html", username=username, repository=repository)


@app.route("/<username>/<repository>/activity/")
def repositoryActivity(username, repository):
    return flask.render_template("repo-activity.html", username=username, repository=repository)


@app.route("/<username>/<repository>/ci/")
def repositoryCI(username, repository):
    return flask.render_template("repo-ci.html", username=username, repository=repository)


@app.route("/<username>/<repository>/settings/")
def repositorySettings(username, repository):
    flask.abort(401)
    return flask.render_template("repo-settings.html", username=username, repository=repository)


@app.errorhandler(404)
def e404(error):
    return flask.render_template("not-found.html"), 404


@app.errorhandler(401)
def e401(error):
    return flask.render_template("unauthorised.html"), 401


@app.errorhandler(403)
def e403(error):
    return flask.render_template("forbidden.html"), 403


@app.errorhandler(418)
def e418(error):
    return flask.render_template("teapot.html"), 418

