import configparser
from common.utils import PathManager, HostMessage
import os
import shutil
from pathlib import Path
import subprocess
import re

class DriverPackage:
    def __init__(self):
        self.container_name = "v10sp1Driver"

    def create_filter_script(self, container_name, filter_path, target_path):
        if not os.path.exists(target_path):
            os.makedirs(os.path.dirname(target_path), exist_ok=True)
        with open(target_path, 'w') as f:
            f.write("#!/bin/bash\n")
            f.write(f"/usr/bin/kare run --redirect_io {container_name} {filter_path} \"$@\"\n")
            os.chmod(target_path, 0o755)

    def record_file_path(self, path, package_path):
        record = {}
        try:
            for root, _, files in os.walk(path):
                for filename in files:
                    file_path = os.path.join(root, filename)
                    rel_path = os.path.relpath(file_path, path)
                    try:
                        is_symlink = os.path.islink(file_path)
                        actual_path = file_path
                        if is_symlink:
                            actual_path = Path(package_path+os.readlink(file_path))
                        stat_info = os.lstat(actual_path)
                        record[rel_path] = {
                            "path": file_path,
                            "size": stat_info.st_size,
                            "mtime": stat_info.st_mtime,
                            "mode": stat_info.st_mode
                        }
                    except Exception as e:
                        continue
        except:
            pass
        return record
    def diff_records(self, before_records, after_records):
        result = []
        for before_record, after_record in zip(before_records, after_records):
            for file_path, attrs in after_record.items():
                if file_path not in before_record:
                    result.append(attrs['path'])
                else:
                    before_attrs = before_record[file_path]
                    if (attrs["size"] != before_attrs["size"] or
                        attrs["mtime"] != before_attrs["mtime"] or
                        attrs["mode"] != before_attrs["mode"]):
                        result.append(attrs['path'])
        return result
    def set_record_cups_files(self, ppd_records, filter_records, mime_records):
        self.ppd_records = ppd_records
        self.filter_records = filter_records
        self.mime_records = mime_records

    def scan_cups_files(self, package_path):
        ppd_dirs = [
            "usr/share/ppd",
            "usr/local/share/ppd",
            "opt/share/ppd",
            "usr/share/cups/model",
        ]
        filter_dirs = ["usr/lib/cups/filter"]
        mime_dirs = ["usr/share/cups/mime", "etc/cups"]

        ppd_records =[self.record_file_path(os.path.join(package_path, dir), package_path) for dir in ppd_dirs]
        filter_records = [self.record_file_path(os.path.join(package_path, dir), package_path) for dir in filter_dirs]
        mime_records = [self.record_file_path(os.path.join(package_path, dir), package_path) for dir in mime_dirs]

        return (ppd_records, filter_records, mime_records)
    
    def diff_cups_files(self, package_path):
        ppd_records, filter_records, mime_records = self.scan_cups_files(package_path)
        ppd_diffs = self.diff_records(self.ppd_records, ppd_records)
        filter_diffs = self.diff_records(self.filter_records, filter_records)
        mime_diffs = self.diff_records(self.mime_records, mime_records)
        return (ppd_diffs, filter_diffs, mime_diffs)

    def cups_pre_install(self, package_path):
        ppd_records, filter_records, mime_records = self.scan_cups_files(package_path)
        self.set_record_cups_files(ppd_records, filter_records, mime_records)

    def white_pre_post_install(self, package_path, package_name, cups_files, filter_diffs):
        if package_name == "epson-inkjet-printer-escpr2":
            filter_wrapper = '''#!/bin/bash
exec -a "$PRINTER" /opt/Epson/epson-inkjet-printer-escpr2/lib/cups/filter/epson-escpr-wrapper2 "$@"'''
            os.system("echo '{}' >> {}/opt/Epson/epson-inkjet-printer-escpr2/lib/cups/filter/epson-escpr-wrapper2.kare".format(filter_wrapper, package_path))
            os.system("chmod +x {}/opt/Epson/epson-inkjet-printer-escpr2/lib/cups/filter/epson-escpr-wrapper2.kare".format(package_path))
            cups_files.append("{}/opt/Epson/epson-inkjet-printer-escpr2/lib/cups/filter/epson-escpr-wrapper2.kare".format(package_path))
            filter_diffs.append("{}/opt/Epson/epson-inkjet-printer-escpr2/lib/cups/filter/epson-escpr-wrapper2.kare".format(package_path))

    def cups_post_install(self, package_path, package_name):
        ppd_diffs, filter_diffs, mime_diffs = self.diff_cups_files(package_path)
        cups_files=[]
        self.white_pre_post_install(package_path, package_name, cups_files, filter_diffs)
        for ppd in ppd_diffs:
            target = "/opt/share/ppd/"
            os.makedirs(target, exist_ok=True)
            os.system("ln -s {} {}".format(ppd, target))
            cups_files.append(os.path.join(target, os.path.basename(ppd)))
        for mime in mime_diffs:
            target = "/etc/cups/"
            os.makedirs(target, exist_ok=True)
            os.system("ln -s {} {}".format(mime, target))
            cups_files.append(os.path.join(target, os.path.basename(mime)))
        for filter_path in filter_diffs:
            filter_path = os.path.join("/", os.path.relpath(filter_path, package_path))
            target_path = "/opt/kare"+filter_path
            if not filter_path.startswith("/usr/lib/cups/filter"):
                target_path = filter_path
            if filter_path.endswith(".kare"):
                target_path = target_path[:-len(".kare")]
            if target_path.startswith("/usr"):
                continue
            self.create_filter_script(package_name, filter_path, target_path)
            cups_files.append(target_path)
        return (cups_files, len(mime_diffs)>0)

class BiometricPackage:
    def __init__(self):
        self.container_name = "bioDriver"
        self.driver = ''
    def run_command_bio(self,command):
        print(f"command:{command}")
        result = subprocess.run(
            command,
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        if result.returncode:
            error_msg = result.stderr.strip()
            if error_msg:
                print(f"Error executing command: {command}")
                print(f"Error details: {error_msg}")
            return ""  # 出错时返回空字符串，避免后续处理崩溃
        return result.stdout
    def is_biometric(self,package):
        try:
            dir_path = Path("/tmp")
            dir_path.mkdir(parents=True, exist_ok=True)
            # 执行 dpkg -e 解压 control 文件到指定目录
            command = f"dpkg -e {package} {dir_path}"
            self.run_command_bio(command)
            
            # 拼接 postinst 路径
            postinst_path = dir_path / "postinst"
            
            # 判断 postinst 是否存在
            if postinst_path.exists():
                with open(postinst_path, 'r') as f:
                    content = f.readlines()
                    i = 0
                    while i < len(content):
                        line = content[i]
                        if line.startswith("driver="):
                            self.driver = line.strip().split("=")[1]
                            j=i+1
                            while j<len(content):
                                line=content[j]
                                if line.startswith("driver="):
                                    self.driver = line.strip().split("=")[1]
                                    break
                                j+=1
                            self.run_command_bio(f"sudo rm {postinst_path}")
                            return True
                        else:
                            if "biometric-config-tool add-driver -f" in line:
                                self.driver = line.strip().split(" ")[-2]
                                self.run_command_bio(f"sudo rm {postinst_path}")
                                return True
        
                        i += 1
                    return False
                            
        except :
            print("Error_is_biometric")
    
    def get_extra_dir(self,path):
        # 分割路径，找到包含 drivers/extra 的部分
        parts = path.split('/')
        if "drivers" in parts and "extra" in parts:
            # 找到 extra 后面的部分，例如 eyecool-finger
            drivers_extra_index = parts.index("extra") + 1
            if len(parts) > drivers_extra_index:
                extra_dir = "/".join(parts[drivers_extra_index:-1])
                print(extra_dir)
        return extra_dir

    def copy_biometric_file(self,driver,deb_path,package_path):
        so_files_dict = {
        "extra": [],          # 包含 /drivers/extra 的 .so 文件
        "main_so": [],        # 仅包含 /drivers 的 .so 文件
        "Non-normative": []   # 不包含 drivers 的 .so 文件
    }
        extra_dir=''
        copy_file_list = []
        try:
            ret = self.run_command_bio("dpkg -c {} | awk '{{print $6}}'".format(deb_path))
            print(ret)
            for line in ret.splitlines():
                line=line.strip('.')
                print(line)
                if line.endswith(".so"):
                    if "/drivers/extra" in line:
                        so_files_dict["extra"].append(line)
                    elif "/drivers" in line:
                        so_files_dict["main_so"].append(line)
                    else:
                        so_files_dict["Non-normative"].append(line)
                
                elif "/usr/lib/biometric-authentication/drivers/extra" in line and not line.endswith("/"):
                    
                    so_files_dict["extra"].append(line)
            print(so_files_dict)

            if len(so_files_dict["extra"])>0:  # 如果 extra 列表不为空
                example_so_path = so_files_dict["extra"][0]  
                
                # 分割路径，找到包含 drivers/extra 的部分
                parts = example_so_path.split('/')
                
                if "drivers" in parts and "extra" in parts:
                    # 找到 extra 后面的部分，例如 eyecool-finger
                    drivers_extra_index = parts.index("extra") + 1
                    if len(parts) > drivers_extra_index:
                        extra_dir_one = parts[drivers_extra_index]
                
                print(so_files_dict["extra"])
            
            if len(so_files_dict["extra"])>0:
                for file in so_files_dict["extra"]:
                    print(file)
                    extra_dir = self.get_extra_dir(file)
                    print(extra_dir)
                    self.run_command_bio("mkdir -p /opt/biometric/drivers/extra/{}".format(extra_dir))
                    self.run_command_bio("cp -r {}/{} /opt/biometric/drivers/extra/{}".format(package_path,file,extra_dir))
                    copy_file_list.append("/opt/biometric/drivers/extra/{}/{}".format(extra_dir,os.path.basename(file)))
                    print(os.path.basename(file))
                    self.run_command_bio(f"patchelf --set-rpath /opt/biometric/drivers/extra/{extra_dir} /opt/biometric/drivers/extra/{extra_dir}/{os.path.basename(file)}")
            print(len(so_files_dict["Non-normative"]))
            
            if len(so_files_dict["Non-normative"])>0:
                print("in the non file copy object")
                for file in so_files_dict["Non-normative"]:
                    self.run_command_bio("sudo mkdir -p /opt/biometric/drivers/extra/{} ".format(driver))
                    self.run_command_bio("sudo cp -r {}/{} /opt/biometric/drivers/extra/{}".format(package_path,file,driver))
                    copy_file_list.append("/opt/biometric/drivers/extra/{}/{}".format(driver,os.path.basename(file)))
                    self.run_command_bio(f"sudo patchelf --set-rpath /opt/biometric/drivers/extra/{driver} /opt/biometric/drivers/extra/{driver}/{os.path.basename(file)}")
                print("finish non file copy")
                    
            if len(so_files_dict["main_so"])>0:        
                for file in so_files_dict["main_so"]:
                    print("in the main file copy object")
                    self.run_command_bio("mkdir -p /opt/biometric/drivers/".format(file))
                    self.run_command_bio("cp -r {}/{} /opt/biometric/drivers/".format(package_path,file))
                    copy_file_list.append("/opt/biometric/drivers/{}".format(os.path.basename(file)))
                    print(os.path.basename(file))
                    if len(extra_dir)>0:
                        self.run_command_bio(f"sudo patchelf --set-rpath /opt/biometric/drivers/extra/{extra_dir_one} /opt/biometric/drivers/{os.path.basename(file)}")
                    if len(so_files_dict["Non-normative"])>0:
                        self.run_command_bio(f"sudo patchelf --set-rpath /opt/biometric/drivers/extra/{driver} /opt/biometric/drivers/{os.path.basename(file)}")
                print("finish main file copy")
                
            if driver == "aratek_trustfinger":
                self.run_command_bio("mkdir -p /opt/biometric/drivers/extra/aratek_trustfinger")
                self.run_command_bio("mkdir -p /opt/biometric/discover-tools")
                self.run_command_bio("cp -r {}/usr/lib/biometric-authentication/drivers/extra/aratek-trustfinger/libAraTrustFinger.so /opt/biometric/drivers/extra/aratek_trustfinger/".format(package_path))
                self.run_command_bio("cp -r {}/usr/lib/biometric-authentication/drivers/extra/aratek-trustfinger/libaratek.so /opt/biometric/drivers/extra/aratek_trustfinger/".format(package_path))
                self.run_command_bio("cp -r {}/usr/lib/biometric-authentication/discover-tools/usb-aratek_trustfinger /opt/biometric/discover-tools/".format(package_path))
                self.run_command_bio("sudo patchelf --set-rpath /opt/biometric/drivers/extra/aratek_trustfinger /opt/biometric/drivers/aratek_trustfinger.so")
                self.run_command_bio("sudo patchelf --set-rpath /opt/biometric/drivers/extra/aratek_trustfinger /opt/biometric/drivers/extra/aratek_trustfinger/libaratek.so")
                copy_file_list.append("/opt/biometric/drivers/extra/aratek_trustfinger/libAraTrustFinger.so")
                copy_file_list.append("/opt/biometric/drivers/extra/aratek_trustfinger/libaratek.so")
            self.set_config(self.driver,package_path)
            return copy_file_list
        except:
            print("Error_is_copy_fie")

    def set_config(self,driver_name,package_path):
        src_config_path = f"{package_path}/etc/biometric-auth/biometric-drivers.conf"
        dest_config_path = "/etc/biometric-auth/biometric-drivers.conf"
        # driver_name = "synaptics"
        try:
            config = configparser.ConfigParser()
            config.optionxform = str
            config.read(src_config_path)
            print("driver_name:{}".format(driver_name))
            if driver_name in config:
                # 修改路径部分
                section_content = dict(config[driver_name])
                original_driver_path = section_content.get("Driver", "")

                if "/usr/lib/biometric-authentication" in original_driver_path:
                    new_driver_path = original_driver_path.replace(
                        "/usr/lib/biometric-authentication", "/opt/biometric"
                    )
                    section_content["Driver"] = new_driver_path

                # 确保目标路径存在
                os.makedirs(os.path.dirname(dest_config_path), exist_ok=True)

                # 读取已有的目标配置文件内容（避免覆盖）
                existing_config = configparser.ConfigParser()
                existing_config.optionxform = str
                existing_config.read(dest_config_path)

                # 更新或添加新的 driver 配置块
                existing_config[driver_name] = section_content

                # 将更新后的内容写回文件
                with open(dest_config_path, "w") as configfile:
                    existing_config.write(configfile)

                print(f"Config for {driver_name} has been updated in {dest_config_path}")
            else:
                print(f"Driver section [{driver_name}] not found in {src_config_path}")
        except Exception as e:
                print(f"Error updating config for {driver_name}: {e}")

class DkmsPackage:
    def __init__(self):
        self.container_name = "dkmsContainer"

class Package(PathManager, HostMessage, DriverPackage,DkmsPackage,BiometricPackage):

    """
    @brief Package类, 用于处理deb包，包括解压、获取包信息、包安装功能等
    @param package: deb包路径
    @param log: 日志对象
    """
    
    def __init__(self, package, log, chroot=False):
        self.package = package
        self.log = log
        self.vp_package = None
        self.chroot = chroot
        if self.is_driver:
            DriverPackage.__init__(self)
        elif self.is_dkms_driver:
            DkmsPackage.__init__(self)
        elif self.is_biometric(package):
            BiometricPackage.__init__(self)
        else:
            self.container_name = self.package_name + "_" + self.package_version

    def run_dpkg_shell(self, message:str)->str:
        command = "dpkg -I {} | grep {}".format(self.package, message)
        ret = self.run_command(command)
        try:
            ret = ret.split(message)[-1]
        except:
            ret = ""
        return ret
    
    @property
    def is_driver(self):
        files_in_deb =[]
        if self.package_name == "wps-office":
            return False
        try:
            output = subprocess.check_output(['dpkg', '-c', self.package], stderr=subprocess.STDOUT)
            files_in_deb = output.decode().split('\n')
        except subprocess.CalledProcessError as e:
            self.log.error(f"Error running dpkg -c: {e.output.decode()}")
            return False

        printer_patterns = [
            r'.*usr/share/ppd/.*\.ppd$',
            r'.*/usr/share/cups/model/.*\.ppd$',
            r'.*/usr/lib/cups/',
            r'/lpd/filter.*'
        ]
        for filepath in files_in_deb:
            for pattern in printer_patterns:
                if re.search(pattern, filepath):
                    return True
        
        scanner_patterns = [
            r'.*usr/share/sane',
            r'.*etc/sane.d/',
            r'.*usr/lib/sane',
            r'libsane-.*.so.*'
        ]
        for filepath in files_in_deb:
            for pattern in scanner_patterns:
                if re.search(pattern, filepath):
                    return True
        return False
    
    #DKMS包判断
    @property
    def is_dkms_driver(self):
        keyword = "dkms"
        if keyword.lower() in self.package_name.lower():
            return True
        files_in_deb =[]
        try:
            output = subprocess.check_output(['dpkg', '-c', self.package], stderr=subprocess.STDOUT)
            files_in_deb = output.decode().split('\n')
        except subprocess.CalledProcessError as e:
            self.log.error(f"Error running dpkg -c: {e.output.decode()}")
            return False

        printer_patterns = [
            r'.*dkms.conf$'
        ]
        for filepath in files_in_deb:
            for pattern in printer_patterns:
                if re.search(pattern, filepath):
                    return True

        return False
    """
    @brief 获取deb包control文件中的Package字段
    @return Package字段
    """
    @property
    def package_name(self)->str:
        return self.run_dpkg_shell("Package:").strip()
    
    """
    @brief 获取deb包control文件中的Version字段
    @return Version字段
    """    
    @property
    def package_version(self)->str:
        return self.run_dpkg_shell("Version:").strip()

    """
    @brief 获取deb包control文件中的Architecture字段
    @return Architecture字段
    """    
    @property
    def package_arch(self)->str:
        return self.run_dpkg_shell("Architecture:").strip()

    """
    @brief deb包解压
    @return 解压缩目录
    """   
    def unpack(self)->str:
        cmd = "dpkg-deb -x {} /tmp/{}".format(self.package, self.package_name)
        if self.run_command_status(cmd) == 0:
            return "/tmp/{}".format(self.package_name)
        else:
            return None
    
    """
    @brief 生成deb包
    @return 是否成功
    """   
    def pack(self)->bool:
        vp_path = os.path.dirname(self.VP_DEBIAN_PATH)
        deb_file = f"{os.path.dirname(vp_path)}/{self.package_name}_{self.package_version}-kare_{self.package_arch}.deb" 
        ret = self.run_command_status(" ".join(["dpkg-deb", "--build", vp_path, deb_file]))
        if ret == 0: 
            self.vp_package = deb_file
            self.log.info(f"生成deb包 {deb_file}成功")
            return True
        else:
            self.log.error(f"生成deb包 {deb_file}失败")
            return False

    """
    @brief 删除vp目录下所有文件
    """   
    def _clean_vp_path(self):
        self.log.info("清理vp目录下所有文件")
        self.remove_dir(os.path.dirname(self.VP_DEBIAN_PATH))

    def _ostree_dir_install(self, dir_path):
        if self.chroot == True:
            if "DEBIAN" in dir_path.parts[4]:
                return
            p = (dir_path.parts[0], "opt", "kare",) + dir_path.parts[4:]
        else:
            if "DEBIAN" in dir_path.parts[5]:
                return
            p = (dir_path.parts[0], "opt", "kare",) + dir_path.parts[5:]
        ostree_dir_path = Path(*p)
        ostree_dir_path.mkdir(parents=True, exist_ok=True)

    def _ostree_file_install(self, file_path):
        if self.chroot == True:
            if "DEBIAN" in file_path.parts[4]:
                return
            p = (file_path.parts[0], "opt", "kare",) + file_path.parts[4:]
        else :
            if "DEBIAN" in file_path.parts[5]:
                return
            p = (file_path.parts[0], "opt", "kare",) + file_path.parts[5:]
        ostree_file_path = Path(*p)
        self.copy_file(file_path, ostree_file_path)

    def _ostree_desktop_install(self, file_path):
        if self.chroot == True:
            if "DEBIAN" in file_path.parts[4]:
                return
            p = (file_path.parts[0], "opt", "kare",) + file_path.parts[4:]
        else :
            if "DEBIAN" in file_path.parts[5]:
                return
            p = (file_path.parts[0], "opt", "kare",) + file_path.parts[5:]
        ostree_file_path = Path(*p)
        os.system(f"mv {file_path} {ostree_file_path}")
   
    def _ostree_host_path_install(self, file_path):
        if self.chroot == True:
            if "DEBIAN" in file_path.parts[4]:
                return
            p = (file_path.parts[0],) + file_path.parts[4:]
        else:
            if "DEBIAN" in file_path.parts[5]:
                return
            p = (file_path.parts[0],) + file_path.parts[5:]
        ostree_file_path = Path(*p)
        self.copy_file(file_path, ostree_file_path)

    """
    @brief 不可变系统安装逻辑
    """   
    def _ostree_install(self, postinst):
        vp_path = os.path.dirname(self.VP_DEBIAN_PATH)
        for root, dirs, files in os.walk(vp_path):
            # 跳过隐藏目录 (以 . 开头的目录)
            dirs[:] = [d for d in dirs if not d.startswith('.')]
            # 打印当前目录中的文件夹
            for d in dirs:
                self._ostree_dir_install(Path(os.path.join(root, d)))
                
            # 打印当前目录中的文件（跳过隐藏文件）
            for f in files:
                if f.endswith(".rules"):
                    self._ostree_host_path_install(Path(os.path.join(root, f)))
                elif "/etc/xdg/autostart" in root and f.endswith(".desktop"):
                    self._ostree_host_path_install(Path(os.path.join(root, f)))
                # elif f.endswith(".desktop"):
                #     self._ostree_desktop_install(Path(os.path.join(root, f)))
                elif not f.startswith('.'):
                    self._ostree_file_install(Path(os.path.join(root, f)))
        if postinst is False:
            self._clean_vp_path()

    """
    @brief 安装虚包， 需要区分是否为不可变系统
    @return 
    """    
    def install(self, postinst=False)->bool:
        if self.is_ostree:
            self._ostree_install(postinst)
            return True
        ret = self.run_command_status(f"dpkg -i --force-overwrite {self.vp_package}")
        self._clean_vp_path()
        return True if ret == 0 else False
    
    """
    @brief 服务文件安装
    """    
    def services_install(self, services_list):
        target_path = "/opt/kare/" if self.is_ostree else "/"
        for service in services_list: 
            if "local" in service.parts or "opt" in service.parts:
                p = ("/", "etc", "systemd", "system")
                service_path = Path(*p)
            elif "init.d" in service.parts:
                p = ("/", "etc", "init.d")
                service_path = Path(*p)
            else:
                p = (target_path, ) + service.parts[3:]
                service_path = Path(*p)
            if not service_path.parent.exists():
                service_path.parent.mkdir(parents=True, exist_ok=True)
            self.copy_file(service, service_path)
    
    """
    @brief 文件清理操作
    """    
    def clean_package(self, package_path, is_driver, is_upgrade, is_dkms):
        if is_driver or is_upgrade:
            self.log.info(f"当前卸载驱动包 不删除{package_path}目录")
            return
        if is_dkms:
            self.log.info(f"当前卸载dkms包 不删除{package_path}目录")
            return

        self.log.info(f"删除文件{package_path}")
        self.run_command(f"sudo umount {package_path}/merge/*")
        self.remove_dir(package_path)

    """
    @brief 包卸载功能
    """    
    def remove_package(self, package):
        if self.is_ostree:
            self.get_config_json(self.INSTALL_JSON, package)
            try:
                conf = self.get_config_json(self.INSTALL_JSON, package)
                for file_path in conf.get("file", []):
                    try:
                        os.remove(file_path)
                    except FileNotFoundError as e:
                        self.log.error(e)
            except AttributeError as e:
                self.log.error(e)
        else:
            cmd = f"dpkg -P {package}"
            ret = self.run_command_status(cmd)
            if ret != 0:
                self.log.error(f"卸载{package}失败, 存在残留")
            else:
                self.log.info(f"卸载{package}成功")
                self.run_command("apt-get -y autoremove")

if __name__ == "__main__":
    dpkg = Package("/home/kylin/d-feet_0.3.15-2_all.deb")
    print(dpkg.package_name())
    print(dpkg.package_version())