#!/usr/bin/python3

# TODO: solve all devel packages to include

import cmdln
import os
import re
import ToolBase
import traceback
import logging

from osc import conf
from osclib.conf import Config
from osclib.stagingapi import StagingAPI
from pkglistgen.tool import PkgListGen
from pkglistgen.update_repo_handler import update_project, merge_susetags

class CommandLineInterface(ToolBase.CommandLineInterface):
    SCOPES = ['all', 'target', 'rings', 'staging']

    def __init__(self, *args, **kwargs):
        ToolBase.CommandLineInterface.__init__(self, args, kwargs)

    def setup_tool(self):
        tool = PkgListGen()
        if self.options.debug:
            logging.basicConfig(level=logging.DEBUG)
        elif self.options.verbose:
            logging.basicConfig(level=logging.INFO)

        return tool

    def do_handle_update_repos(self, subcmd, opts, project):
        """${cmd_name}: Update 00update-repos

        Reads config.yml from 00update-repos and will create required solv files

        ${cmd_usage}
        ${cmd_option_list}
        """
        return update_project(conf.config['apiurl'], project)

    def do_merge_susetags(self, subcmd, opts, output, *files):
        """${cmd_name}: Merge susetags files and output into a new

        ${cmd_usage}
        ${cmd_option_list}
        """
        return merge_susetags(output, files)

    @cmdln.option('-f', '--force', action='store_true', help='continue even if build is in progress')
    @cmdln.option('-p', '--project', help='target project')
    @cmdln.option('-s', '--scope', action='append', help='scope on which to operate ({}, staging:$letter)'.format(', '.join(SCOPES)))
    @cmdln.option('--no-checkout', action='store_true', help='reuse checkout in cache')
    @cmdln.option('--stop-after-solve', action='store_true', help='only create group files')
    @cmdln.option('--staging', help='Only solve that one staging')
    @cmdln.option('--only-release-packages', action='store_true', help='Generate 000release-packages only')
    def do_update_and_solve(self, subcmd, opts):
        """${cmd_name}: update and solve for given scope

        ${cmd_usage}
        ${cmd_option_list}
        """

        if opts.staging:
            match = re.match('(.*):Staging:(.*)', opts.staging)
            opts.scope = ['staging:' + match.group(2)]
            if opts.project:
                raise ValueError('--staging and --project conflict')
            opts.project = match.group(1)
        elif not opts.project:
            raise ValueError('project is required')
        elif not opts.scope:
            opts.scope = ['all']

        apiurl = conf.config['apiurl']
        Config(apiurl, opts.project)
        target_config = conf.config[opts.project]

        # Store target project as opts.project will contain subprojects.
        target_project = opts.project

        api = StagingAPI(apiurl, target_project)

        main_repo = target_config['main-repo']

        # used by product converter
        # these needs to be kept in sync with OBS config
        if apiurl.find('suse.de') > 0:
            os.environ['OBS_NAME'] = 'build.suse.de'
        if apiurl.find('opensuse.org') > 0:
            os.environ['OBS_NAME'] = 'build.opensuse.org'

        # special case for all
        if opts.scope == ['all']:
            opts.scope = target_config.get('pkglistgen-scopes', 'target').split(' ')

        self.error_occured = False

        def solve_project(project, scope):
            try:
                self.tool.reset()
                self.tool.dry_run = self.options.dry
                if self.tool.update_and_solve_target(api, target_project, target_config, main_repo,
                                project=project, scope=scope, force=opts.force,
                                no_checkout=opts.no_checkout,
                                only_release_packages=opts.only_release_packages,
                                stop_after_solve=opts.stop_after_solve):
                    self.error_occured = True
            except Exception:
                # Print exception, but continue to prevent problems effecting one
                # project from killing the whole process. Downside being a common
                # error will be duplicated for each project. Common exceptions could
                # be excluded if a set list is determined, but that is likely not
                # practical.
                traceback.print_exc()
                self.error_occured = True

        for scope in opts.scope:
            if scope.startswith('staging:'):
                letter = re.match('staging:(.*)', scope).group(1)
                solve_project(api.prj_from_short(letter), 'staging')
            elif scope == 'target':
                solve_project(target_project, scope)
            elif scope == 'rings':
                solve_project(api.rings[1], scope)
            elif scope == 'staging':
                letters = api.get_staging_projects_short()
                for letter in letters:
                    solve_project(api.prj_from_short(letter), scope)
            else:
                raise ValueError('scope "{}" must be one of: {}'.format(scope, ', '.join(self.SCOPES)))

        return self.error_occured
