#!/usr/bin/env python3
"""
Module to adjust general SolydXK settings

Author: Arjen Balfoort
Date: 2026-02-22
"""

import os
import re
import subprocess
import apt
import psutil


def is_dpkg_locked() -> bool:
    """Check if dpkg is locked."""
    filepath = "/var/lib/dpkg/lock"
    for proc in psutil.process_iter(['pid', 'name', 'open_files']):
        try:
            for file in proc.info['open_files'] or []:
                if filepath in file.path:
                    return True
        except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
            continue
    return False

def get_debian_version() -> int:
    """ Get Debian's version number (int) """
    for version_file in ['/etc/debian_version','/etc/lsb-release', '/etc/os-release']:
        with open(file=version_file, mode='r', encoding='utf-8') as f:
            content = f.read().strip()
            match =re.search(r'^([\d]{2})\.*\d*$|"([\d]{2})"', content)
            if match:
                if match.group(1):
                    return int(match.group(1))
                return int(match.group(2))
    return 0

def source_file_to_environ(file_path):
    """ Load environment variables from a file """
    if not os.path.exists(file_path):
        return
    with open(file=file_path, mode='r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if line and not line.startswith('#') and '=' in line:
                key, value = line.split('=', 1)
                value = value.strip('"\'')
                os.environ[key] = value

def is_string_in_file(file_path: str, string: str) -> bool:
    """
    Check if a string is in a file.

    Args:
        file_path (str): The file path.
        string (str): The string to search for.
    """
    if not os.path.exists(file_path) or not string:
        return False
    with open(file=file_path, mode='r', encoding='utf-8') as f:
        return string.lower() in f.read().lower()

# Global variables
source_file_to_environ("/usr/share/solydxk/info")
try:
    XK_RELEASE = int(os.environ['RELEASE'])
except KeyError:
    XK_RELEASE = 0
QTCHOOSER_DIR = "/usr/share/qtchooser"
QT6_CONF = 'qt6-x86_64-linux-gnu.conf'
DEB_VERSION = get_debian_version()
APT_CACHE = apt.Cache()

# Fix some programs:
# [package, what to fix, options(1), exec from debian version(2)
# (1): touch/mkdir/purge/install|owner:group|permissions
# (2): check /etc/debian_version or 0 = all
fix_progs = [['login', '/var/log/btmp', 'touch|root:utmp|600', 0],
             ['login', '/var/log/lastlog', 'touch|root:utmp|664', 0],
             ['login', '/var/log/faillog', 'touch|root:utmp|664', 0],
             ['login', '/var/log/wtmp', 'touch|root:utmp|664', 0],
             ['apache2', '/var/log/apache2', 'mkdir|root:adm|755', 0],
             ['mysql-common', '/var/log/mysql', 'mkdir|mysql:adm|755', 0],
             ['clamav', '/var/log/clamav', 'mkdir|clamav:clamav|755', 0],
             ['clamav', '/var/log/clamav/freshclam.log', 'touch|clamav:clamav|644', 0],
             ['samba', '/var/log/samba', 'mkdir|root:adm|755', 0],
             ['consolekit', '/var/log/ConsoleKit', 'mkdir|root:root|755', 0],
             ['exim4-base', '/var/log/exim4', 'mkdir|Debian-exim:adm|755', 0],
             ['lightdm', '/var/lib/lightdm/data', 'mkdir|lightdm:lightdm|755', 0],
             ['usbguard', '/etc/usbguard/rules.conf', 'touch|root:root|600', 0],
             ['ntpsec', '/var/log/ntpsec', 'mkdir|ntpsec:ntpsec|755', 0]]

try:
    for prog in fix_progs:
        if DEB_VERSION >= prog[3] or prog[3] == 0:
            if APT_CACHE[prog[0]].is_installed:
                options = prog[2].split('|')
                if options[0] == 'purge' or options[0] == 'install':
                    if not is_dpkg_locked():
                        try:
                            subprocess.run(["apt-get", "-y", options[0], prog[1]], check=True)
                        except subprocess.CalledProcessError:
                            pass
                elif options[0] == 'touch' and not os.path.exists(prog[1]):
                    dir_name = os.path.dirname(prog[1])
                    if not os.path.isdir(dir_name):
                        try:
                            subprocess.run(["mkdir", "-p", dir_name], check=True)
                            subprocess.run(["chown", options[1], dir_name], check=True)
                            subprocess.run(["chmod", options[2], dir_name], check=True)
                        except subprocess.CalledProcessError:
                            pass
                    subprocess.run(["touch", prog[1]], check=True)
                    subprocess.run(["chown", options[1], prog[1]], check=True)
                    subprocess.run(["chmod", options[2], prog[1]], check=True)
                elif options[0] == 'mkdir' and not os.path.isdir(prog[1]):
                    subprocess.run(["mkdir", "-p", prog[1]], check=True)
                    subprocess.run(["chown", options[1], prog[1]], check=True)
                    subprocess.run(["chmod", options[2], prog[1]], check=True)
except Exception as e:
    print(e)

if is_string_in_file("/etc/lsb-release", "debian") or \
   is_string_in_file("/usr/lib/os-release", "debian") or \
   is_string_in_file("/etc/issue", "debian") or \
   is_string_in_file("/etc/issue.net", "debian"):
    # Fix /etc/lsb-release, /usr/lib/os-release, /etc/issue and /etc/issue.net
    codename = f'CODENAME="{os.environ["CODENAME"]}"\n'
    edition = f'EDITION="{os.environ["EDITION"]}"\n'
    release = f'RELEASE="{os.environ["RELEASE"]}"\n'
    distrib_id = f'DISTRIB_ID="{os.environ["DISTRIB_ID"]}"\n'
    description = f'DESCRIPTION="{os.environ["DESCRIPTION"]}"\n'
    pretty_name = f'PRETTY_NAME="{os.environ["PRETTY_NAME"]}"\n'
    home_url = f'HOME_URL="{os.environ["HOME_URL"]}"\n'
    support_url = f'SUPPORT_URL="{os.environ["SUPPORT_URL"]}"\n'
    bug_report_url = f'BUG_REPORT_URL="{os.environ["BUG_REPORT_URL"]}"\n'

    try:
        # Restore LSB information
        with open(file="/etc/lsb-release", mode="w", encoding="utf-8") as f:
            f.writelines([distrib_id,
                        "DISTRIB_" + release,
                        "DISTRIB_" + codename,
                        "DISTRIB_" + description])
    except Exception as detail:
        print(detail)

    try:
        with open(file="/usr/lib/os-release", mode="w", encoding="utf-8") as f:
            f.writelines([pretty_name,
                        codename.replace("CODENAME", "NAME"),
                        release.replace("RELEASE", "VERSION_ID"),
                        distrib_id.replace("DISTRIB_ID", "ID"),
                        release.replace("RELEASE", "VERSION"),
                        codename.replace("CODENAME", "VERSION_CODENAME"),
                        home_url,
                        support_url,
                        bug_report_url])
    except Exception as e:
        print(e)

    try:
        # Restore /etc/issue and /etc/issue.net
        issue = description.replace("DESCRIPTION=", "").replace("\"", "")
        with open(file="/etc/issue", mode="w", encoding="utf-8") as f:
            f.writelines(issue.strip() + " \\n \\l\n")
        with open(file="/etc/issue.net", mode="w", encoding="utf-8") as f:
            f.writelines(issue)
    except Exception as e:
        print(e)

if os.path.isdir(QTCHOOSER_DIR):
    qt6_path = os.path.join(QTCHOOSER_DIR, QT6_CONF)
    if not os.path.exists(qt6_path):
        with open(file=qt6_path, mode='w', encoding='utf-8') as f:
            f.writelines(['/usr/lib/qt6/bin\n', '/usr/lib/x86_64-linux-gnu\n'])
        os.system(f"ln -s ../../../share/qtchooser/{QT6_CONF} "
                "/usr/lib/x86_64-linux-gnu/qtchooser/qt6.conf")
        os.system(f"ln -s ../../../share/qtchooser/{QT6_CONF} "
                "/usr/lib/x86_64-linux-gnu/qtchooser/6.conf")
