#!/usr/bin/python
# -*- coding: utf-8 -*-

import re
import os
import subprocess
import urllib.request
import json

# --------------------------------------------------------------------------- #
# ОПРЕДЕЛЕНИЕ ТИПА VCS                                                       #
# --------------------------------------------------------------------------- #

def _detect_vcs(url):
    url_lower = url.lower()
    if 'github.com' in url_lower or 'gitlab.com' in url_lower or '/git/' in url_lower or url_lower.endswith('.git') or url_lower.startswith(('git@', 'ssh://git')):
        return 'git'
    if '/svn/' in url_lower or url_lower.startswith('svn://'):
        return 'svn'
    return 'git'

# --------------------------------------------------------------------------- #
# GITHUB API                                                                 #
# --------------------------------------------------------------------------- #

def _get_github_commits(url, count=1, commit_hash=None):
    parts = url.replace('https://github.com/', '').replace('.git', '').split('/')
    if len(parts) < 2:
        return None
    owner, repo = parts[0], parts[1]

    if commit_hash:
        api_url = f'https://api.github.com/repos/{owner}/{repo}/commits/{commit_hash}'
    else:
        api_url = f'https://api.github.com/repos/{owner}/{repo}/commits?per_page={count}'

    try:
        req = urllib.request.Request(api_url, headers={'User-Agent': 'Isida-NG-Bot/1.0'})
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read().decode('utf-8'))
        if commit_hash:
            return _format_github_commit(data)
        else:
            return '\n'.join([_format_github_commit(c) for c in data])
    except Exception as e:
        return L('Error: %s', '') % str(e)

def _format_github_commit(c):
    sha = c['sha'][:7]
    message = c['commit']['message'].split('\n')[0]
    author = c['commit']['author']['name']
    date = c['commit']['author']['date'][:10]
    return '%s | %s | %s: %s' % (date, sha, author, message)

# --------------------------------------------------------------------------- #
# GITLAB API                                                                 #
# --------------------------------------------------------------------------- #

def _get_gitlab_commits(url, count=1, commit_hash=None):
    import urllib.parse
    parts = url.replace('https://gitlab.com/', '').replace('.git', '').split('/')
    if len(parts) < 2:
        return None
    project_path = '/'.join(parts)
    project_id = urllib.parse.quote(project_path, safe='')

    if commit_hash:
        api_url = f'https://gitlab.com/api/v4/projects/{project_id}/repository/commits/{commit_hash}'
    else:
        api_url = f'https://gitlab.com/api/v4/projects/{project_id}/repository/commits?per_page={count}'

    try:
        req = urllib.request.Request(api_url, headers={'User-Agent': 'Isida-NG-Bot/1.0'})
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read().decode('utf-8'))
        if commit_hash:
            return _format_gitlab_commit(data)
        else:
            return '\n'.join([_format_gitlab_commit(c) for c in data])
    except Exception as e:
        return L('Error: %s', '') % str(e)

def _format_gitlab_commit(c):
    sha = c['id'][:7]
    message = c['message'].split('\n')[0]
    author = c['author_name']
    date = c['created_at'][:10]
    return '%s | %s | %s: %s' % (date, sha, author, message)

# --------------------------------------------------------------------------- #
# FORGEJO / GITEA / CODEBERG API                                              #
# --------------------------------------------------------------------------- #

def _get_forgejo_commits(url, count=1, commit_hash=None):
    parts = url.replace('https://', '').split('/')
    if len(parts) < 3:
        return None
    host = parts[0]
    owner = parts[1]
    repo = parts[2].replace('.git', '')

    if commit_hash:
        api_url = f'https://{host}/api/v1/repos/{owner}/{repo}/commits/{commit_hash}'
    else:
        api_url = f'https://{host}/api/v1/repos/{owner}/{repo}/commits?limit={count}'

    try:
        req = urllib.request.Request(api_url, headers={'User-Agent': 'Isida-NG-Bot/1.0'})
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read().decode('utf-8'))
        if commit_hash:
            return _format_forgejo_commit(data)
        else:
            return '\n'.join([_format_forgejo_commit(c) for c in data])
    except Exception as e:
        return L('Error: %s', '') % str(e)

def _format_forgejo_commit(c):
    sha = c['sha'][:7]
    message = c['commit']['message'].split('\n')[0]
    author = c['commit']['author']['name'] if 'author' in c['commit'] else c['commit']['committer']['name']
    date = c['commit']['author']['date'][:10]
    return '%s | %s | %s: %s' % (date, sha, author, message)

# --------------------------------------------------------------------------- #
# GIT LOG (локальный)                                                        #
# --------------------------------------------------------------------------- #

def _get_git_log_local(url, count=1, rev=None):
    try:
        if rev:
            check = subprocess.run(['git', 'cat-file', '-t', rev], capture_output=True, text=True, cwd=url)
            if check.returncode != 0:
                return L('Commit %s not found', '') % rev
            cmd = ['git', 'show', '--pretty=format:%h - %an: %s%n%b', rev]
        else:
            cmd = ['git', 'log', '--pretty=format:%h - %an: %s%n%b', '-n', str(count)]
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, cwd=url)
        if result.returncode != 0:
            return L('Error: %s', '') % result.stderr.strip()
        output = result.stdout.strip()
        if not output:
            return L('No commits found', '')
        if len(output) > 2000:
            output = output[:2000] + '\n... (truncated)'
        return output
    except Exception as e:
        return L('Error: %s', '') % str(e)

# --------------------------------------------------------------------------- #
# GIT LS-REMOTE (fallback)                                                   #
# --------------------------------------------------------------------------- #

def _get_git_log_remote(url, count=1):
    try:
        cmd = ['git', 'ls-remote', '--heads', url]
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        if result.returncode != 0 or not result.stdout.strip():
            return L('No branches found', '')
        heads = result.stdout.strip().split('\n')
        output = []
        for i, line in enumerate(heads[:count]):
            parts = line.split()
            if len(parts) >= 2:
                short_hash = parts[0][:7]
                output.append('%s: %s' % (parts[1].replace('refs/heads/', ''), short_hash))
        if output:
            return L('Last %s commit(s):\n%s', '') % (len(output), '\n'.join(output))
        return L('No commits found', '')
    except Exception as e:
        return L('Error: %s', '') % str(e)

# --------------------------------------------------------------------------- #
# SVN LOG                                                                    #
# --------------------------------------------------------------------------- #

def _get_svn_log(url, count=1, rev=None):
    try:
        if rev:
            cmd = ['svn', 'log', url, '-r', str(rev)]
        else:
            cmd = ['svn', 'log', url, '--limit', str(count)]
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        if result.returncode != 0:
            return L('Error: %s', '') % result.stderr.strip()
        output = result.stdout.strip()
        if not output:
            return L('No revisions found', '')
        output = re.sub(r'-{10,}', '-'*3, output)
        output = re.sub(r'\n\s*\n', '\n', output)
        if len(output) > 2000:
            output = output[:2000] + '\n... (truncated)'
        return output
    except Exception as e:
        return L('Error: %s', '') % str(e)

# --------------------------------------------------------------------------- #
# ОСНОВНАЯ ЛОГИКА                                                            #
# --------------------------------------------------------------------------- #

def _get_vcs_log(vcs, url, count=1, rev=None):
    if vcs == 'git':
        if os.path.isdir(url) and os.path.exists(os.path.join(url, '.git')):
            return _get_git_log_local(url, count, rev)
        elif 'github.com' in url:
            return _get_github_commits(url, count, rev)
        elif 'gitlab.com' in url:
            return _get_gitlab_commits(url, count, rev)
        else:
            # Forgejo/Gitea/Codeberg или неизвестный хост
            result = _get_forgejo_commits(url, count, rev)
            if result and 'Error' not in result:
                return result
            return _get_git_log_remote(url, count)
    elif vcs == 'svn':
        return _get_svn_log(url, count, rev)
    else:
        return L('Unsupported VCS', '')

# --------------------------------------------------------------------------- #
# КОМАНДА                                                                    #
# --------------------------------------------------------------------------- #

def vcs_log(type, jid, nick, text):
    if not text.strip():
        send_msg(type, jid, nick, L('Usage: vcs <url> [count|commit_hash|r<rev>]\nExamples:\n  vcs https://github.com/user/repo 5\n  vcs https://git.ubunix.pro/user/repo 5\n  vcs https://codeberg.org/user/repo a731493\n  vcs svn://example.com/repo r123', '%s/%s' % (jid, nick)))
        return

    parts = text.strip().split()
    url = parts[0]
    count = 1
    rev = None

    if len(parts) > 1:
        arg = parts[1]
        if arg.lower().startswith('r'):
            try:
                rev = int(arg[1:])
                count = 1
            except:
                rev = None
        elif re.match(r'^[0-9a-f]{7,40}$', arg, re.I):
            rev = arg
            count = 1
        else:
            try:
                count = int(arg)
                if count < 1:
                    count = 1
                if count > 20:
                    count = 20
            except:
                count = 1

    vcs = _detect_vcs(url)
    if not vcs:
        send_msg(type, jid, nick, L('Unable to detect VCS type. Supported: git, svn', '%s/%s' % (jid, nick)))
        return

    log = _get_vcs_log(vcs, url, count, rev)
    msg = L('[%s] %s\n%s', '%s/%s' % (jid, nick)) % (vcs.upper(), url, log)
    send_msg(type, jid, nick, msg)

# --------------------------------------------------------------------------- #
# АЛИАСЫ                                                                     #
# --------------------------------------------------------------------------- #

def svn_get(type, jid, nick, text):
    if text and not text.startswith(('http://', 'https://', 'svn://')):
        text = 'svn://' + text
    vcs_log(type, jid, nick, text)

# --------------------------------------------------------------------------- #
# РЕГИСТРАЦИЯ                                                                #
# --------------------------------------------------------------------------- #

execute = [
    (3, 'vcs', vcs_log, 2, 'Show VCS log. Supports git (GitHub/GitLab/Forgejo/Codeberg API), svn.\nUsage: vcs <url> [count|commit_hash|r<rev>]\nExamples:\n  vcs https://github.com/user/repo 5\n  vcs https://git.ubunix.pro/user/repo 5\n  vcs https://codeberg.org/user/repo a731493\n  vcs svn://example.com/repo r123'),
    (3, 'svn', svn_get, 2, 'Show SVN log (legacy).\nUsage: svn <url> [count|r<rev>]'),
]