import os
import re
import dbus
import dbus.service
from common.log import Log
import threading
import subprocess
import time
from common.errors import KareErrors
# 使用 GLib 主循环

class KareDbus(dbus.service.Object):
    def __init__(self):
        self.log_path = "/var/log/kare/kare-install.log"
        self.log = Log("/var/log/kare/kare-dbus.log")

        bus = dbus.SystemBus()
        bus_name = dbus.service.BusName('org.kylin.kare', bus=bus)
        super().__init__(bus_name, '/org/kylin/kare')

        # 构建进度状态变量
        self.current_build_progress = 0
        self.current_build_phase = 0       # 0-未开始,1-4对应各阶段
        self.phase_line_count = 0
        self.base_environment = ""
        self.build_proc = None
        self.timer_id = None
        self.build_lock = threading.Lock()

        # 安装相关状态变量
        self.current_package = ""
        self.current_install_progress = 0
        self.install_proc = None
        self.install_timer = None
        self.install_lock = threading.Lock()
        self.progress_stages = [
            (re.compile(r'Resources processed'), 20),
            (re.compile(r'Building installation command'), 25),
            (re.compile(r'Installation succeeded'), 70),
            (re.compile(r'Dependencies resolved'), 99)
        ]
        self.current_install_stage = 0  # 当前阶段索引
        self.current_install_max_progress = 20  # 当前阶段允许的最大进度

        # 卸载相关状态变量
        self.remove_proc = None
        self.remove_lock = threading.Lock()


    @dbus.service.signal('org.kylin.kare', signature='siis')
    def installStatusChanged(self, name, progress, status, error_info):
        """发送安装状态信号"""
        pass
    
    @dbus.service.signal('org.kylin.kare', signature='sis')
    def removeStatusChanged(self, name,  status, error_info):
        """发送卸载状态信号"""
        pass

    @dbus.service.signal('org.kylin.kare', signature='siis')
    def buildStatusChanged(self, base_env, progress, status, error_info):
        """发送安装状态信号"""
        pass

    # @dbus.service.signal('org.kylin.kare', signature='si') 
    # def installFinished(self, name, status):
    #     """发送安装完成信号"""
    #     pass

    # @dbus.service.signal('org.kylin.kare', signature='si')
    # def removeFinished(self, name, status):
    #     """发送卸载完成信号"""
    #     pass

    @dbus.service.method('org.kylin.kare', in_signature='ss', out_signature='i')
    def install(self, release, package):
        if os.path.exists("/var/lock/kare-install.lock"):
            return False
        t1 = threading.Thread(target=self._install, args=(release, package,))
        t1.daemon = True
        t1.start()
        return True
    
    @dbus.service.method('org.kylin.kare', in_signature='s', out_signature='i')
    def remove(self, package):
        t1 = threading.Thread(target=self._remove, args=(package,))
        t1.daemon = True
        t1.start()
        return True
    
    @dbus.service.method('org.kylin.kare', in_signature='s', out_signature='i')
    def buildBaseEnv(self, base_env):
        if os.path.exists("/var/lock/kare-build.lock"):
            return False
        t1 = threading.Thread(target=self._build_base_env, args=(base_env,))
        t1.daemon = True
        t1.start()
        return True

    def _build_base_env(self, base_env):
        self.base_environment = base_env
        command = ["kare", "-b", base_env]

        try:
            self.build_proc = subprocess.Popen(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True
            )
            self.log.info(f"Build process started: {command}")
        except Exception as e:
            self.log.error(f"Process start failed: {str(e)}")
            self.buildStatusChanged(base_env, 0, -1, f"Build process start failed: {str(e)}")
            return False

        # 初始化状态
        with self.build_lock:
            self.current_build_progress = 0
            self.current_build_phase = 0
            self.phase_line_count = 0

        # 启动进度监控线程（新增）
        build_progress_thread = threading.Thread(target=self._monitor_build_progress)
        build_progress_thread.daemon = True
        build_progress_thread.start()

        # 启动输出监控线程
        build_output_thread = threading.Thread(target=self._monitor_build_output)
        build_output_thread.daemon = True
        build_output_thread.start()
        self.log.info(f"monitor Build process output started: {command}")

        # 启动完成监控线程
        completion_thread = threading.Thread(target=self._wait_for_build_completion)
        completion_thread.daemon = True
        completion_thread.start()
        self.log.info(f"monitor Build process completion started: {command}")
        return True

    def _monitor_build_progress(self):
        """构建进度监控线程（每3秒发送进度）"""
        while True:
            with self.build_lock:
                # 检查构建进程是否已结束
                if self.build_proc is None or self.build_proc.poll() is not None:
                    break

                # 发送当前进度（无论是否变化）
                self.buildStatusChanged(
                    self.base_environment,
                    self.current_build_progress,
                    1,  # 进行中状态
                    ""
                )
                self.log.info(f"[构建心跳] 当前进度: {self.current_build_progress}%")

            time.sleep(3)

    def _monitor_build_output(self):
        while self.build_proc.poll() is None:
            line = self.build_proc.stdout.readline()
            self.log.info(f"Build output: {line}")
            if line:
                self._process_line(line.strip())

    def _process_line(self, line):
        with self.build_lock:
            # 保存旧进度用于比较
            old_build_progress = self.current_build_progress
            
            # 阶段转换检测
            self._detect_phase_transition(line)
            
            # 行数统计进度更新
            if self.current_build_phase > 0:
                self.phase_line_count += 1
                self._update_build_progress_by_lines()

            self.log.info(f"Build progress: {self.current_build_progress}")
            self.log.info(f"phase line count: {self.phase_line_count}")
            # 进度变化时发送信号
            if self.current_build_progress != old_build_progress:
                self._send_build_progress_signal(1)  # 进行中状态码

    def _detect_phase_transition(self, line):
        phase_triggers = [
            {   # Phase 1
                'start': re.compile(r'Validating|Retrieving', re.I),
                'next': re.compile(r'Extracting')
            },
            {   # Phase 2
                'start': re.compile(r'Extracting'),
                'next': re.compile(r'Unpacking|Configuring', re.I)
            },
            {   # Phase 3
                'start': re.compile(r'Unpacking|Configuring', re.I),
                'next': re.compile(r'Base system installed successfully')
            },
            {   # Phase 4
                'start': re.compile(r'Base system installed successfully'),
                'next': None
            }
        ]

        # 检测阶段开始
        if self.current_build_phase == 0:
            for idx, phase in enumerate(phase_triggers, 1):
                if phase['start'].search(line):
                    self.log.info(f"!!! Phase {idx} trigger matched !!!")
                    self.current_build_phase = idx
                    self.log.info(f"Build phase {self.current_build_phase} started")
                    self._jump_to_phase_start(idx)
                    return

        # 检测阶段跳转
        else:
            current_phase = phase_triggers[self.current_build_phase-1]
            if current_phase['next'] and current_phase['next'].search(line):
                next_phase = self.current_build_phase + 1
                if next_phase <= 4:
                    self.current_build_phase = next_phase
                    self.log.info(f"Build phase {self.current_build_phase} started")
                    self._jump_to_phase_start(next_phase)

    def _jump_to_phase_start(self, phase_num):
        phase_progress_map = {
        1: 0,
        2: 45,
        3: 50,
        4: 95
    }
        
        self.current_build_progress = phase_progress_map[phase_num]
        self.phase_line_count = 0
        self.log.info(f"Build phase {phase_num} started")
        self._send_build_progress_signal(1)

    def _update_build_progress_by_lines(self):
        phase_config = {
            1: {'step': 20, 'max': 45},
            2: {'step': 25, 'max': 50},
            3: {'step': 30, 'max': 95},
            4: {'step': 20, 'max': 100}
        }[self.current_build_phase]

        if self.phase_line_count % phase_config['step'] == 0:
            new_build_progress = min(
                self.current_build_progress + 1,
                phase_config['max']
            )
            if new_build_progress > self.current_build_progress:
                self.current_build_progress = new_build_progress
        self.log.info(f"Build progress=======: {self.current_build_progress}%")

    def _send_build_progress_signal(self, status):
        self.buildStatusChanged(
            self.base_environment,
            self.current_build_progress,
            status,
            ""
        )
        self.log.info(f"emit signal: {self.current_build_progress}%")
    
    def _wait_for_build_completion(self):
        exit_code = self.build_proc.wait()
        self.log.info(f"Build process exited with code {exit_code}")
        with self.build_lock:
            if exit_code == 0:
                # 最终进度补全
                final_progress = 100
                if self.current_build_progress < final_progress:
                    self.current_build_progress = final_progress
                self.buildStatusChanged(
                    self.base_environment,
                    self.current_build_progress,
                    0,  # 成功状态
                    ""
                )
                self.log.info("Build completed successfully.")
            else:
                error_code, error_msg = self._get_error_info(exit_code)
                self.buildStatusChanged(
                    self.base_environment,
                    self.current_build_progress,
                    -1,
                    error_msg
                )
            self._reset_build_state()
            self.log.error(f"Build failed with exit code {exit_code}: {error_msg}")

    def _reset_build_state(self):
        self.log.info("Resetting build state.")
        self.current_build_progress = 0
        self.current_build_phase = 0
        self.phase_line_count = 0
        self.base_environment = ""
        self.build_proc = None

    def _install(self, release, package):
        """安装核心逻辑"""
        with self.install_lock:
            # 初始化安装状态
            self.current_package = package
            self.current_install_progress = 0
            self.current_install_stage = 0  # 重置阶段索引

            try:
                # 使用script命令包装执行流程
                self.install_proc = subprocess.Popen(
                    [
                        "script", "-a", "-e", "-q", "-c", 
                        f"kare -e {release} -i {package}",
                        self.log_path
                    ],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.STDOUT,
                    text=True,
                    bufsize=1,
                    universal_newlines=True
                )
            except Exception as e:
                self._send_install_error(KareErrors.INSTALL_START_FAILED)
                return

            # 启动输出分析线程
            output_thread = threading.Thread(target=self._analyze_install_output)
            output_thread.daemon = True
            output_thread.start()

            # 启动进度监控线程
            self.install_timer = threading.Thread(target=self._monitor_install_progress)
            self.install_timer.daemon = True
            self.install_timer.start()

    def _analyze_install_output(self):
        """持续读取安装输出并处理"""
        try:
            while True:
                line = self.install_proc.stdout.readline()
                if not line and self.install_proc.poll() is not None:
                    break
                if line:
                    self._process_install_line(line.strip())
                    self.log.info(f"Install output line: {line.strip()}")
        except Exception as e:
            self.log.error(f"输出分析失败: {str(e)}")
        finally:
            # 处理安装完成
            self._handle_install_completion()

    def _process_install_line(self, line):
        """安装输出处理"""
        with self.install_lock:
            # 已处理完所有阶段时跳过
            if self.current_install_stage >= len(self.progress_stages):
                return

            # 只检查当前阶段的关键字
            pattern, target = self.progress_stages[self.current_install_stage]
            if pattern.search(line):
                # 立即跳转到阶段目标进度
                new_progress = min(target, self.progress_stages[self.current_install_stage][1])
                if new_progress > self.current_install_progress:
                    self.current_install_progress = new_progress
                    self.installStatusChanged(
                        self.current_package,
                        self.current_install_progress,
                        1,
                        ""
                    )
                
                # 更新阶段设置
                self.current_install_stage += 1
                if self.current_install_stage < len(self.progress_stages):
                    self.current_install_max_progress = self.progress_stages[self.current_install_stage][1]
                else:
                    self.current_install_max_progress = 99

                self.log.info(f"进入阶段 {self.current_install_stage + 1}，最大进度设为 {self.current_install_max_progress}%")

    def _monitor_install_progress(self):
        """进度监控主循环"""
        while True:
            with self.install_lock:
                # 终止条件检查
                if self.install_proc is None or self.install_proc.poll() is not None:
                    break
                
                # 获取当前阶段最大允许进度
                stage_max = self.current_install_max_progress
                
                # 自动递增逻辑
                if self.current_install_progress < stage_max:
                    new_progress = min(
                        self.current_install_progress + 1,
                        stage_max
                    )
                    self.current_install_progress = new_progress

                # 更新进度和心跳检测
                self.installStatusChanged(
                    self.current_package,
                    self.current_install_progress,
                    1,
                    ""
                )
                self.log.info(f"当前安装进度: {new_progress}%")
            time.sleep(3)

    def _handle_install_completion(self):
        """处理安装完成状态"""
        final_exit_code = self._get_final_exit_code(self.install_proc, self.log_path)
        
        with self.install_lock:
            if final_exit_code == 0:
                # 最终补全进度
                self.current_install_progress = 100
                self.installStatusChanged(
                    self.current_package,
                    100,
                    0,  # 成功状态
                    "安装成功"
                )
            else:
                error_code, error_msg = self._get_error_info(final_exit_code)
                self.installStatusChanged(
                    self.current_package,
                    self.current_install_progress,
                    -error_code,
                    error_msg
                )
            self._reset_install_state()
    
    def _reset_install_state(self):
        """重置安装状态"""
        self.current_install_stage = 0
        self.current_install_max_progress = 20
        self.current_install_progress = 0
        self.current_package = ""
        self.install_proc = None
        self.install_timer = None
    
    def _send_install_error(self, error):
        """立即发送错误信号"""
        self.installStatusChanged(
            self.current_package,
            0,
            -error[0],
            error[1]
        )

    def _remove(self, package):
        """卸载核心逻辑"""
        with self.remove_lock:
            self.current_package = package
            try:
                # 使用script命令执行并记录完整日志
                self.remove_proc = subprocess.Popen(
                    [
                        "script", "-a", "-e", "-q", "-c",
                        f"kare -P {package}", 
                        self.log_path
                    ],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.STDOUT,
                    text=True,
                    bufsize=1,
                    universal_newlines=True
                )
            except Exception as e:
                self.removeStatusChanged(
                    self.current_package,
                    -1,
                    f"卸载进程启动失败: {str(e)}"
                )
                self.log.error(f"卸载进程启动失败: {str(e)}")
                return

            # 启动完成监控线程
            completion_thread = threading.Thread(target=self._wait_remove_completion)
            # completion_thread.daemon = True
            completion_thread.start()

            # 启动心跳监控线程
            remove_heartbeat_thread = threading.Thread(target=self._monitor_remove_progress)
            remove_heartbeat_thread.daemon = True
            remove_heartbeat_thread.start()

    def _monitor_remove_progress(self):
        """卸载进度心跳检测"""
        while True:
            with self.remove_lock:
                # 检查进程是否已结束
                if self.remove_proc is None or self.remove_proc.poll() is not None:
                    break
                
                self.removeStatusChanged(
                    self.current_package,
                    1,
                    ""
                )
                self.log.info("卸载中，正在进行...")
            time.sleep(3)

    def _wait_remove_completion(self):
        """等待卸载完成并处理结果"""
        final_exit_code = self._get_final_exit_code(self.remove_proc, self.log_path)
        self.log.info(f"卸载最终退出码: {final_exit_code}")
        if final_exit_code == 0:
            self.removeStatusChanged(
                self.current_package,
                0,
                ""
            )
            self.log.info("卸载成功")
        else:
            error_info = self._get_error_info(final_exit_code)
            self.removeStatusChanged(
                self.current_package,
                -1,
                error_info[1]
            )
            self.log.error(f"卸载失败: {error_info[1]}")
        with self.remove_lock:
            self.current_package = ""
            self.remove_proc = None

    def _get_last_line(self, log_path):
        """从script日志解析真实退出码"""
        """高效获取日志文件最后一行内容"""
        try:
            with open(log_path, 'rb') as f:
                # 跳转到文件末尾前2字节（避免读取整个文件）
                f.seek(-2, os.SEEK_END)
                
                # 向前查找换行符
                while f.read(1) != b'\n':
                    try:
                        f.seek(-2, os.SEEK_CUR)
                    except OSError:
                        # 到达文件头部
                        f.seek(0)
                        break
                
                # 读取最后一行并解码
                last_line = f.readline().decode('utf-8', 'ignore')
                return last_line.strip()
        except FileNotFoundError:
            self.log.error(f"日志文件不存在: {log_path}")
            return None
        except Exception as e:
            self.log.error(f"读取日志失败: {str(e)}")
            return None

    def _parse_exit_code(self, log_path):
        """从最后一行解析退出码"""
        last_line = self._get_last_line(log_path)
        if not last_line:
            return 0  # 默认成功
        
        # 使用正则匹配退出码
        match = re.search(r'COMMAND_EXIT_CODE="(\d+)"', last_line)
        self.log.info(f"解析的退出码: {match.group(1)}")
        return int(match.group(1)) if match else 0
    
    def _get_final_exit_code(self, proc, log_path):
        """综合判断最终退出码"""
        # 先获取script命令的原始退出码
        try:
            script_exit_code = proc.wait()
        except:
            script_exit_code = -999  # 未知异常代码

        # 从日志最后一行解析
        parsed_code = self._parse_exit_code(log_path)
        
        # 决策逻辑：
        # 1. 日志中有明确退出码时优先使用
        # 2. 未找到时结合script退出码判断
        if parsed_code != 0:
            return parsed_code
        elif script_exit_code == 0:
            return 0  # 双重验证成功
        else:
            return script_exit_code

    def _get_error_info(self, exit_code):
        """统一错误处理函数"""
        error_msg = KareErrors.error_code.get(
            exit_code, 
            f"未知错误（代码：{exit_code}）"
        )
        return exit_code, error_msg




