#!/usr/bin/python3

import datetime
import getpass
import os
import subprocess
import click

import sys
for root, dirs, _ in os.walk("/usr/lib/python3/dist-packages"):
    for dir_name in dirs:
        if dir_name.startswith("UpgradeCheck"):
            sys.path.append(os.path.join(root, dir_name))
        if dir_name.startswith("DependCheck"):
            sys.path.append(os.path.join(root, dir_name))

from UpgradeCheck.util_conf import KpctConf
from UpgradeCheck.util_deb import KpctDeb
from UpgradeCheck.util_log import Log
from UpgradeCheck.util_dbus import ExternalController
from UpgradeCheck.util_database import KylinUpGradeDataBase
from UpgradeCheck.collect_sys_info import SysInfo, DbusInfo
from UpgradeCheck.collect_deb_info import DebInfo, NewOsDebDepend, LibInDirInfo, BaseCollect
from UpgradeCheck.collect_file_info import (ElfInfo, ConfInfo, PermissionInfo, ServiceInfo, ShellInfo,
                                            HighPermissionInfo, DesktopInfo, QtInfo, DbusListInfo,
                                            SystemdStrongDependInfo, PythonImportInfo,JavaJarInfo)

from DependCheck.static_depend_collect import main_for_collect as static_depend_check, PathStandardChecker

# 默认值（与config.yaml一致，完全与配置文件解耦）
DEFAULT_COLLECT_LIST = ['SysInfo', 'DebInfo', 'DesktopInfo', 'ElfInfo', 'ShellInfo', 'ConfInfo',
                        'ServiceInfo', 'DbusInfo', 'PermissionInfo',
                        'HighPermissionInfo', 'QtInfo', 'NewOsDebDepend', 'DbusListInfo',
                        'SystemdStrongDependInfo', 'PythonImportInfo']
DEFAULT_OUTPUT_RESULT = False
DEFAULT_THIRD_APP = True
DEFAULT_SAVE_PATH = os.path.join(os.path.expanduser('~'), '.kpct', 'UpgradeCheck')
DEFAULT_SOURCE = None
DEFAULT_DEB_FILE_PATH = None
DEFAULT_FULL_SCAN = True

# 当前检测内容，已转为可配置项
collect_list_for_dbus = [
    SysInfo,
    DebInfo,
    DesktopInfo,
    ElfInfo,
    ShellInfo,
    ConfInfo,
    ServiceInfo,
    DbusInfo,
    # PermissionInfo,
    # HighPermissionInfo,
    # QtInfo,
    # NewOsDebDepend,
    # DbusListInfo,
    # SystemdStrongDependInfo,
    # PythonImportInfo,
    # JavaJarInfo,
]


class CustomInt(int):
    def __init__(self, value):
        if not isinstance(value, int):
            raise ValueError('请输入整数')
        self._value = value

    def __add__(self, other):
        if (self._value + other) >= 50:
            return 49
        else:
            return self._value + other


progress_percect = CustomInt(0)


def collect_for_dbus():
    from UpgradeCheck.util_common import result_path
    start_time = datetime.datetime.now()
    kuc_log = Log().getlog()

    kpct_deb = KpctDeb(kuc_log, third_app_flag=True)
    BaseCollect.deb_name = kpct_deb.deb_name
    BaseCollect.deb_dict = kpct_deb.deb_dict

    kuc_process = ExternalController()
    if not os.path.exists(result_path):
        os.makedirs(result_path)
    db_path = '{}/{}'.format(result_path, "collect_information.db")
    kdb = KylinUpGradeDataBase.create(db_path)

    global progress_percect
    kuc_process.send_progress_signal(progress_percect)

    for collect_check in collect_list_for_dbus:
        if issubclass(collect_check, ElfInfo):
            obj = collect_check(full_scan=True,log=kuc_log, database=kdb, kuc_process=kuc_process, progress_percect=progress_percect)
        else:
            obj = collect_check(kuc_log, kdb)
        obj.run()
        if issubclass(collect_check, ElfInfo):
            progress_percect = obj.progress_percect + 2
        else:
            progress_percect += 2
        kuc_process.send_progress_signal(progress_percect)

    mid_time = datetime.datetime.now()
    kuc_log.info("磐石架构标准检测")
    panshi_check = PathStandardChecker()
    res = panshi_check.batch_check_packages(obj.deb_dict)
    kuc_log.info(panshi_check.generate_report(res))
    mid_time_end = datetime.datetime.now()
    kuc_log.info("===== Upgrade信息收集时间：{}s".format((mid_time_end - mid_time).total_seconds()))

    kdb.close()
    end_time = datetime.datetime.now()
    kuc_log.info("===== Upgrade升级检测: 信息收集结束")
    kuc_log.info("===== Upgrade信息收集时间：{}s".format((end_time - start_time).total_seconds()))


@click.command()
@click.option('-c', '--collect', 'paths', nargs=2, help='文件夹内库文件对比，传入两个路径')
@click.option('--dbus', is_flag=True, help='判断是否是dbus调用')
@click.option('--deb-list', '-d', 'deb_list_opt', multiple=True, help='指定软件包列表，多个包用逗号分隔')
@click.option('--collect-list', '-l', 'collect_list_opt', multiple=True, help='指定收集项列表，如:SysInfo,DebInfo,ElfInfo')
@click.option('--third-app/--no-third-app', 'third_app_opt', default=None, help='启用/禁用第三方软件包信息收集')
@click.option('--save-path', '-s', 'save_path_opt', help='输出文件存放路径')
@click.option('--source', 'source_opt', help='使用在线源进行升级检测:kylin-v4|kylin-v10|kylin-v10-sp1|custom|或指定.list文件')
@click.option('--deb-file-path', '-f', 'deb_file_path_opt', help='对指定路径下的deb软件包文件进行依赖收集')
@click.option('--output/--no-output', 'output_opt', default=None, help='启用/禁用软件包依赖检查结果输出')
@click.option('--full-scan/--no-full-scan', 'full_scan_opt', default=True, help='启用/禁用全量依赖符号扫描(默认启用)')
def main(paths, dbus, deb_list_opt, collect_list_opt, third_app_opt, save_path_opt, source_opt, deb_file_path_opt, output_opt, full_scan_opt):
    if dbus:
        collect_for_dbus()
        return

    start_time = datetime.datetime.now()

    kuc_log = Log().getlog()

    # 检查是否有任何CLI参数被提供
    has_cli_args = any([
        deb_list_opt, collect_list_opt, third_app_opt is not None,
        save_path_opt, source_opt, deb_file_path_opt,
        output_opt is not None, full_scan_opt is not None
    ])

    # CLI模式：必须指定 deb_list 或 third_app
    if has_cli_args:
        if not deb_list_opt and third_app_opt is None and paths is None:
            kuc_log.error('使用CLI参数时，必须指定 --deb-list 或 --third-app/--no-third-app')
            return

        # 使用CLI参数，配置的默认值作为后备
        deb_list = list(deb_list_opt) if deb_list_opt else None
        deb_file_path = deb_file_path_opt if deb_file_path_opt else DEFAULT_DEB_FILE_PATH
        output = output_opt if output_opt is not None else DEFAULT_OUTPUT_RESULT
        third_app = third_app_opt if third_app_opt is not None else DEFAULT_THIRD_APP
        save_path = save_path_opt if save_path_opt else DEFAULT_SAVE_PATH
        source = source_opt if source_opt else DEFAULT_SOURCE

        # collect_list 默认为全选
        if collect_list_opt:
            collect_list = []
            for item in collect_list_opt:
                if ',' in item:
                    collect_list.extend([eval(i.strip()) for i in item.split(',')])
                else:
                    collect_list.append(eval(item.strip()))
        else:
            collect_list = [eval(i) for i in DEFAULT_COLLECT_LIST]

        # CLI模式下创建kpct_conf对象，供save_conf_2_db使用
        from UpgradeCheck.util_common import result_path, opt_path
        kpct_conf = KpctConf.__new__(KpctConf)
        kpct_conf.log = kuc_log
        # 优先使用result_path，如果不存在则使用opt_path
        if os.path.exists(os.path.join(result_path, 'config.yaml')):
            kpct_conf.conf_path = os.path.join(result_path, 'config.yaml')
        elif os.path.exists(os.path.join(opt_path, 'config.yaml')):
            kpct_conf.conf_path = os.path.join(opt_path, 'config.yaml')
        else:
            kpct_conf.conf_path = os.path.join(result_path, 'config.yaml')
        kpct_conf.deb_list = deb_list if deb_list else []
        kpct_conf.collect_list = [c.__name__ if hasattr(c, '__name__') else str(c) for c in collect_list]
        kpct_conf.output_result = output
        kpct_conf.third_app = third_app
        kpct_conf.save_path = save_path
        kpct_conf.deb_file_path = deb_file_path
        kpct_conf.report_format = 'all'
        kpct_conf.source = source

        # 生成配置yaml文件供save_conf_2_db使用
        default_compare_list = ['PermissionCompare', 'ConfFileCompare', 'ServiceFileCompare',
                               'SysLibInDeb', 'SelfIncludeCompare', 'DiffSymCompare',
                               'DiffPreupgradeCompare', 'DiffLddCompare', 'QtInDebCompare',
                               'DtSysLibInDeb', 'DbusUnderControl']
        kpct_conf.compare_list = default_compare_list
        if os.path.exists(kpct_conf.conf_path):
            import yaml as yaml_lib
            with open(kpct_conf.conf_path, 'r') as f:
                original_conf = yaml_lib.safe_load(f)
            if original_conf and original_conf.get('compare_list'):
                compare_list_for_write = original_conf.get('compare_list', [])
            else:
                compare_list_for_write = default_compare_list
        else:
            compare_list_for_write = default_compare_list

        deb_list_yaml = '\n    - '.join(deb_list) if deb_list else ''
        collect_list_yaml = '\n    - '.join(kpct_conf.collect_list)

        compare_list_yaml = '\n    - '.join(compare_list_for_write)

        cli_conf_content = f"""# kpct配置文件
deb_list:
    - {deb_list_yaml}

collect_list:
    - {collect_list_yaml}

compare_list:
    - {compare_list_yaml}

output_result: {str(output).lower()}

third_app: {str(third_app).lower()}

save_path: {save_path}

source: {source if source else 'no'}

deb_file_path: {deb_file_path if deb_file_path else ''}

report_format: all
"""
        os.makedirs(result_path, exist_ok=True)
        # CLI模式不覆盖配置文件
    else:
        # 配置模式：完全使用配置文件
        kpct_conf = KpctConf(kuc_log)
        conf_path = kpct_conf.conf_path

        deb_list = kpct_conf.deb_list
        deb_file_path = kpct_conf.deb_file_path
        output = kpct_conf.output_result
        third_app = kpct_conf.third_app
        save_path = kpct_conf.save_path
        source = kpct_conf.source
        collect_list = [eval(i) for i in kpct_conf.collect_list]

    full_scan = full_scan_opt if full_scan_opt is not None else DEFAULT_FULL_SCAN
    source_list = ['kylin-v4', 'kylin-v10', 'kylin-v10-sp1', 'custom']

    if source:
        NewOsDebDepend.password = getpass.getpass('Enter your password: ')

    kuc_log.info('=== 获取检测软件包列表')
    if deb_list:
        KpctDeb.custom_list = deb_list
    elif deb_file_path:
        find_cmd = 'find {} -type f'.format(deb_file_path)
        find_res = subprocess.run(find_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        deb_file_list = [i.strip() for i in find_res.stdout.splitlines() if i.endswith('.deb')]

    kuc_deb = KpctDeb(kuc_log, third_app)
    BaseCollect.deb_name = kuc_deb.deb_name
    BaseCollect.deb_dict = kuc_deb.deb_dict

    kuc_log.info('=== 创建数据库链接')
    if not os.path.exists(save_path):
        os.makedirs(save_path)
    db_path = '{}/{}'.format(save_path, "collect_information.db")
    kdb = KylinUpGradeDataBase.create(db_path)

    if paths:
        kuc_log.info("===== Upgrade升级检测: 文件夹内库文件对比")
        LibInDirInfo.old_path = paths[0]
        LibInDirInfo.new_path = paths[1]
        obj = LibInDirInfo(kuc_log, kdb)
        obj.run()

        kdb.close()

        end_time = datetime.datetime.now()
        kuc_log.info("===== Upgrade升级检测: 信息收集结束")
        kuc_log.info("===== Upgrade信息收集时间：{}s".format((end_time - start_time).total_seconds()))
        return

    if output:
        kuc_log.info('=== 开始执行软件包依赖检测')
        output_dict = {}
        if deb_list:
            kuc_log.info('=== 已安装软件包依赖检测')
            # output_dict = {deb1: {control: dep1,dep2..., first_level_depends: dep1,dep2...}, deb2: ...}
            # 遍历deb
            for child_deb in deb_list:
                child_deb_depends = static_depend_check((0, str(child_deb)), save_path, kuc_log, output)
                output_dict[child_deb] = {'control': child_deb_depends[0], 'first_level': child_deb_depends[1],"not_found":child_deb_depends[2]}

            if not os.path.exists(save_path):
                os.makedirs(save_path)
            with open('{}/deb_list_depdens.txt'.format(save_path), 'w') as f:
                for output_deb, depends in output_dict.items():
                    f.write('{} 依赖包: \n'.format(os.path.basename(output_deb)))
                    for output_deb_depend in depends['control']:
                        f.write('    {}\n'.format(output_deb_depend))
                    for output_deb_depend in depends['first_level']:
                        f.write('    {}\n'.format(output_deb_depend))
                    for output_deb_depend in depends['not_found']:
                        f.write('    {}\n'.format(output_deb_depend))

        if deb_file_path:
            kuc_log.info('=== 未安装软件包依赖批量检测')
            # output_dict = {deb1: {control: dep1,dep2..., first_level_depends: dep1,dep2...}, deb2: ...}
            # 通过find获取文件夹内文件列表
            # 遍历deb
            if len(deb_file_list) == 0:
                 kuc_log.info(f'=== {deb_file_path}下未找到deb包，请检查路径')
            for child_deb in deb_file_list:
                child_deb_depends = static_depend_check((1, str(child_deb)), save_path, kuc_log, output)
                output_dict[child_deb] = {'control': child_deb_depends[0], 'first_level': child_deb_depends[1]}

            if not os.path.exists(save_path):
                os.makedirs(save_path)
            with open('{}/deb_list_depdens.txt'.format(save_path), 'w') as f:
                for output_deb, depends in output_dict.items():
                    f.write('{} 依赖包: \n'.format(os.path.basename(output_deb)))
                    for output_deb_depend in depends['control']:
                        f.write('    {}\n'.format(output_deb_depend))
                    for output_deb_depend in depends['first_level']:
                        f.write('    {}\n'.format(output_deb_depend))
        else:
            kuc_log.info('=== 无法进行依赖检测，需要指定 deb_list 或 deb_file_path项')
        return

    if source:
        kuc_log.info('=== 开始执行软件包迁移检测')
        if source in source_list:
            kuc_log.info('=== 使用工具内源：{}'.format(source))
            kuc_log.info('===== Upgrade升级检测: 信息收集开始')
            NewOsDebDepend.target_source = source
        elif source.endswith('.list'):
            kuc_log.info('=== 使用指定源：{}'.format(source))
            if os.path.exists(source):
                kuc_log.info('===== Upgrade升级检测: 信息收集开始')
                NewOsDebDepend.target_source = source
            else:
                kuc_log.info('=== 指定源 {} 不存在，请重新输入'.format(source))
                return
        else:
            kuc_log.info('** config.yaml文件中 source项请填入下列值其中之一：')
            kuc_log.info(source_list)
            return
    else:
        kuc_log.info('=== config.yaml文件未指定 source 项，不进行迁移检测')

    kuc_log.info("=== 开始执行第三方软件包检测")
    if not third_app:
        kuc_log.info("===== config.yaml文件未指定 third_app 项，不进行系统内第三方软件包检测")

    kuc_log.info('===== Upgrade升级检测: 信息收集开始')
    for collect_check in collect_list:
        if issubclass(collect_check, ElfInfo):
            obj = collect_check(full_scan=full_scan, log=kuc_log, database=kdb)
        else:
            obj = collect_check(kuc_log, kdb)
        obj.run()

    kpct_conf.save_conf_2_db_direct(kdb)

    kdb.close()

    end_time = datetime.datetime.now()
    kuc_log.info("===== Upgrade信息收集时间：{}s".format((end_time - start_time).total_seconds()))
    kuc_log.info('===== Upgrade升级检测: 数据库位置{}'.format(db_path))
    kuc_log.info("===== Upgrade升级检测: 配置文件位置 {}".format(kpct_conf.conf_path))
    kuc_log.info("===== Upgrade升级检测: 结果存放位置 {}".format(save_path))
    kuc_log.info("===== Upgrade升级检测: 程序结束")


if __name__ == "__main__":
    main()
