#!/usr/bin/env python
# vim:se fileencoding=utf8 :
# Copyright (c) 2017-2025 Michał Górny
# SPDX-License-Identifier: GPL-2.0-or-later

# This is a Python helper script for eselect-repo that wraps routines
# for repositories.xml & repos.conf file. It helps us avoid ugly sed
# and/or dependency on non-common packages.

import argparse
import configparser
import locale
import lxml.etree
import os
import os.path
import sys


def transform_source(source_el, repo_name):
    """Transform <source/> into (sync-type, sync-uri) pair."""
    stype = source_el.get('type')
    suri = source_el.text
    # pass-through
    if stype in ('bzr', 'git', 'rsync', 'svn'):
        return (stype, suri)
    # map to 'hg' syncer
    elif stype == 'mercurial':
        return ('hg', suri)
    else:
        print(f'warning: {repo_name}: unsupported source type {stype}',
              file=sys.stderr)
        return None


def do_list(args):
    repos = {}
    for r in args.repositories_xml.getroot().findall('repo'):
        name = r.findtext('name')
        uris = [transform_source(s, name) for s in r.findall('source')]
        if name in args.repos_conf:
            sect = args.repos_conf[name]
            sync_params = (sect.get('sync-type'), sect.get('sync-uri'))
            if sync_params in uris:
                status = 'enabled'
            else:
                status = 'need-update'
        else:
            status = 'disabled'
        repos[name] = {
            'status': status,
            'url': r.findtext('homepage'),
        }

    for name, data in args.repos_conf.items():
        if name != 'DEFAULT' and (name not in repos or 'sync-uri' not in data):
            repos[name] = {
                'status': 'local',
                'url': '',
            }

    for name, data in sorted(repos.items(),
                             key=lambda kv: locale.strxfrm(kv[0])):
        print(name, data["status"], (data['url'] or '').strip())


def do_metadata(args):
    all_repos = set(
        x.findtext('name')
        for x in args.repositories_xml.getroot().findall('repo'))

    for r in args.repo:
        if r not in args.repos_conf:
            print(r, "not-exist")
            continue

        if r not in all_repos:
            state = 'local'
        elif 'sync-uri' not in args.repos_conf[r]:
            state = 'no-sync-uri'
        else:
            state = 'remote'
        local_path = args.repos_conf[r].get('location', '')
        print(r, state, local_path)


def do_remote_metadata(args):
    all_repos = dict(
        (x.findtext('name'), x)
        for x in args.repositories_xml.getroot().findall('repo'))

    for r in args.repo:
        if r in args.repos_conf:
            print(r, "enabled", args.repos_conf[r].get('location', ''))
            continue
        if r not in all_repos:
            print(r, "not-exist")
            continue

        sources = all_repos[r].findall('source')
        for s in sources:
            sync_data = transform_source(s, r)
            if sync_data is not None:
                break
        else:
            print(r, "unsupported")
            continue

        print(r, "remote", *sync_data)


def make_configparser(path):
    cfgp = configparser.ConfigParser(interpolation=None)
    if os.path.isdir(path):
        paths = [os.path.join(path, x) for x in os.listdir(path)
                 if not x.startswith('.') and not x.endswith('~')]
    else:
        paths = [path]
    # monkey-patch our path list in
    cfgp._erh_paths = paths
    cfgp.read(paths)
    return cfgp


def main():
    locale.setlocale(locale.LC_ALL, '')
    p = argparse.ArgumentParser()

    paths = p.add_argument_group('paths')
    paths.add_argument('--repos-conf', required=True,
                       type=make_configparser,
                       help='Location of repos.conf file')
    paths.add_argument('--repositories-xml', required=True,
                       type=lxml.etree.parse,
                       help='Location of repositories.xml file')

    actions = p.add_subparsers(title='actions', dest='action')
    actions.add_parser('list',
                       help='List of remote & local repositories')

    metadata_action = actions.add_parser(
        'metadata', help='Print metadata for given repos')
    metadata_action.add_argument('repo', nargs='+',
                                 help='Repository to print metadata for')

    metadata_action = actions.add_parser(
        'remote-metadata', help='Print metadata for given remote repos')
    metadata_action.add_argument('repo', nargs='+',
                                 help='Repository to print metadata for')

    args = p.parse_args()

    if args.action == 'list':
        do_list(args)
    elif args.action == 'metadata':
        do_metadata(args)
    elif args.action == 'remote-metadata':
        do_remote_metadata(args)
    else:
        raise NotImplementedError(f'Action {args.action} not implemented')


if __name__ == '__main__':
    main()
