#!/usr/bin/env python3
"""Executable script for running statick against a workspace."""

from __future__ import print_function

import os
import sys
from typing import Dict, List, Optional

from statick_tool.args import Args
from statick_tool.issue import Issue
from statick_tool.package import Package
from statick_tool.plugin_context import PluginContext
from statick_tool.statick import Statick


def main() -> None:  # pylint: disable=too-many-locals, too-many-branches, too-many-statements
    """Run statick_ws."""
    args = Args("Statick workspace tool")
    args.parser.add_argument("path", help="Path of workspace to scan")
    args.parser.add_argument(
        "--packages-file",
        dest="packages_file",
        type=str,
        help="File listing packages to scan",
    )
    args.parser.add_argument(
        "--list-packages",
        dest="list_packages",
        action="store_true",
        help="List packages and levels",
    )

    statick = Statick(args.get_user_paths())
    statick.gather_args(args.parser)
    parsed_args = args.get_args()
    statick.get_config(parsed_args)
    statick.get_exceptions(parsed_args)

    if parsed_args.output_directory:
        out_dir = parsed_args.output_directory
        if not os.path.isdir(out_dir):
            print("Output directory not found at " + out_dir + "!")
            sys.exit(1)

    ignore_packages = statick.get_ignore_packages()

    packages = []
    for root, dirs, files in os.walk(parsed_args.path):
        for sub_dir in dirs:
            full_dir = os.path.join(root, sub_dir)
            files = os.listdir(full_dir)
            if (
                "CMakeLists.txt" in files
                and "CATKIN_IGNORE" not in files
                and "AMENT_IGNORE" not in files
                and "package.xml" in files
            ):
                if ignore_packages and sub_dir in ignore_packages:
                    continue
                packages.append((sub_dir, full_dir))

    if parsed_args.packages_file is not None:
        packages_file_list = []
        try:
            packages_file = os.path.abspath(parsed_args.packages_file)
            with open(packages_file, "r") as fname:
                packages_file_list = [
                    package.strip()
                    for package in fname.readlines()
                    if package.strip() and package[0] != "#"
                ]
        except OSError:
            print("Packages file not found")
            sys.exit(1)
        packages = [package for package in packages if package[0] in packages_file_list]

    count = 0
    total_issues = []
    issues = {}  # type: Optional[Dict[str, List[Issue]]]
    for package in packages:
        if parsed_args.list_packages:
            print(
                "%-40s: %s" % (package[0], statick.get_level(package[1], parsed_args))
            )
            continue

        count += 1
        print(
            "-- Scanning package "
            + package[0]
            + " ("
            + str(count)
            + " of "
            + str(len(packages))
            + ") --"
        )
        issues, dummy = statick.run(package[1], parsed_args)
        if issues is not None:
            total_issues.append(issues)
        else:
            print("Failed to run statick on package " + package[0] + "!")
            sys.exit(1)
        print(
            "-- Done scanning package "
            + package[0]
            + " ("
            + str(count)
            + " of "
            + str(len(packages))
            + ") --"
        )

    if parsed_args.list_packages:
        sys.exit(0)

    print("-- All packages run --")
    print("-- overall report --")

    success = True
    for issue in total_issues:
        for key, value in list(issue.items()):
            if issues is not None:
                if key in issues:
                    issues[key] += value
                    success = False
                else:
                    issues[key] = value
                    success = False
            else:
                success = False

    # Make a fake 'all' package for reporting
    dummy_all_package = Package("all_packages", parsed_args.path)
    level = statick.get_level(dummy_all_package.path, parsed_args)
    if level is not None and statick.config is not None:
        enabled_reporting_plugins = statick.config.get_enabled_reporting_plugins(level)
        available_reporting_plugins = {}
        for plugin_info in statick.manager.getPluginsOfCategory("Reporting"):
            available_reporting_plugins[
                plugin_info.plugin_object.get_name()
            ] = plugin_info.plugin_object

    # Make a dummy plugincontext as well
    plugin_context = PluginContext(args, None, None)  # type: ignore
    plugin_context.args.output_directory = parsed_args.output_directory

    if not enabled_reporting_plugins:
        enabled_reporting_plugins = list(available_reporting_plugins)

    for plugin_name in enabled_reporting_plugins:
        if plugin_name not in available_reporting_plugins:
            print("Can't find specified reporting plugin {}!".format(plugin_name))
        plugin = statick.reporting_plugins[plugin_name]
        plugin.set_plugin_context(plugin_context)
        print("Running {} reporting plugin...".format(plugin.get_name()))
        plugin.report(dummy_all_package, issues, level)
        print("{} reporting plugin done.".format(plugin.get_name()))

    if parsed_args.check and not success:
        print("Statick exiting with errors.")
        sys.exit(1)
    else:
        print("Statick exiting with success.")
        sys.exit(0)


if __name__ == "__main__":
    main()
