"""
This module provides some more general utilities useful in various parts
of the application.

Roundabout - git hosting for everyone <https://roundabout-host.com>
Copyright (C) 2023-2025 Roundabout developers <root@roundabout-host.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

from common import *

__all__ = ["git_command", "only_chars", "get_permission_level", "get_visibility", "get_favourite", "human_size",
           "guess_mime", "convert_to_html", "js_to_bool", "get_commit_identity"]

import subprocess
import os
import magic

def only_chars(string, chars):
    chars = set(chars)
    all_chars = set(string)
    return all_chars.issubset(chars)


def get_permission_level(logged_in, username, repository):
    from models import User, Repo, RepoAccess
    user = User.query.filter_by(username=logged_in).first()
    repo = Repo.query.filter_by(route=f"/{username}/{repository}").first()

    if user and repo:
        permission = RepoAccess.query.filter_by(user=user, repo=repo).first()
        if permission:
            return permission.access_level

    return None


def get_visibility(username, repository):
    from models import Repo
    repo = Repo.query.filter_by(route=f"/{username}/{repository}").first()

    if repo:
        return repo.visibility

    return None


def get_favourite(logged_in, username, repository):
    from models import RepoFavourite
    relationship = RepoFavourite.query.filter_by(user_username=logged_in,
                                                 repo_route=f"/{username}/{repository}").first()
    return relationship


def human_size(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 guess_mime(path):
    if os.path.isdir(path):
        mimetype = "inode/directory"
    elif magic.from_file(path, mime=True):
        mimetype = magic.from_file(path, mime=True)
    else:
        mimetype = "application/octet-stream"
    return mimetype


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


def js_to_bool(js):
    return js.lower() == "true" if isinstance(js, str) else bool(js)


def get_commit_identity(identity, pusher, repo):
    from models import User, RepoAccess
    email = identity.rpartition("<")[2].rpartition(">")[0].strip()
    # If the email is not valid, attribute the commit to the pusher.
    if not email:
        return pusher
    email_users = User.query.filter_by(email=email).all()
    # If no user has the email, attribute the commit to the pusher.
    if not email_users:
        return pusher

    # If only one user has the email, attribute the commit to them.
    if len(email_users) == 1:
        return email_users[0]

    # If it's ambiguous, attribute the commit to an user with a higher permission level.
    for user in email_users:
        if repo.owner == user:
            return user

    for user in email_users:
        relationship = RepoAccess.query.filter_by(user=user, repo=repo).first()
        if relationship.permission_level == 2:
            return user

    for user in email_users:
        relationship = RepoAccess.query.filter_by(user=user, repo=repo).first()
        if relationship.permission_level == 1:
            return user

    # If no user has a higher permission level, attribute the commit to the pusher :(
    return pusher

