#!/bin/python3

# Program Should run with sudo -HE
# https://github.com/acidburnmonkey
#

import os
import re
import subprocess
import requests
import shutil
import logging
from git import Repo
import zipfile
import urllib.request
from rich.console import Console
from rich.theme import Theme
from slimParser import SlimParser

# pylint: disable=subprocess-run-check
# pylint: disable=broad-exception-caught
# pylint: disable=logging-fstring-interpolation

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
formatter = logging.Formatter("%(asctime)s | %(levelno)s | %(funcName)s| %(message)s")
f_handler = logging.FileHandler('logg.log')
f_handler.setFormatter(formatter)
logger.addHandler(f_handler)

logging.basicConfig(
    format="%(asctime)s | %(levelno)s | %(funcName)s| %(message)s ", filename='logg.log', level=logging.INFO
)

ap_theme = Theme({'ok': 'green', 'error': 'red', 'checked': 'bold cyan', 'rule': 'orange1'})
console = Console(theme=ap_theme)

user = os.getlogin()
home = os.path.join('/home', user)


def main():
    setup = ''
    local_user = user

    sudo_check()

    console.print(f"Setting up for user {local_user} ", style='rule')

    console.print('optimizing dnf.conf', style='ok')
    dnf_config()
    install_programs_dnf()
    zsh_fonts()
    hyprland()
    msic_configs()

    # This should not need sudo
    # Pass D or L to copy_dotfiles function

    if os.path.exists('dotfiles'):
        while True:
            console.print('Set up dotfiles for Desktop (D) or Laptop  (L) ?', style='rule')
            setup = input('>').lower()
            if setup == 'l' or setup == 'd':
                copy_dotfiles(setup)
                break
    else:
        console.print('Dotfiles not found skipping...', style='error')

    executable_scripts()
    systemd()

    # correcting ownership
    subprocess.run(f"chown -R {user}:{user} {home}", shell=True, stdout=subprocess.DEVNULL)


################
# END OF MAIN #
################


def dnf_config():
    console.rule("Configuring dnf", style='checked')

    # dnf conf
    with open('/etc/dnf/dnf.conf', 'r+') as f:
        text = 'max_parallel_downloads=10 \nfastestmirror=true'
        match = re.search(r'(max_parallel_downloads=10)', f.read())
        if not match:
            with open('/etc/dnf/dnf.conf', 'a+') as f:
                f.write(text)
                console.print(' changes made to dnf_config :heavy_check_mark:', style='ok')
            logger.info('changes made to dnf_config')
        else:
            console.print(' dnf.conf already optimized :heavy_check_mark:', style='checked')

    subprocess.check_call(
        'sudo dnf install -y https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm',
        stdout=subprocess.DEVNULL,
        shell=True,
    )
    subprocess.check_call(
        'sudo dnf install -y https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm',
        stdout=subprocess.DEVNULL,
        shell=True,
    )
    console.print('rpmfusion added to repos :heavy_check_mark:', style='ok')
    logger.info('rpmfusion added to repos')

    # update Dnf
    subprocess.run('dnf upgrade -y', shell=True)
    logger.info('Updated Dnf ')


def hyprland():
    console.rule("Configuring hyprland", style='checked')
    if not os.path.exists(home + '/.local/bin'):
        os.mkdir(home + '/.local/bin')

    # create tty launcher
    with open(home + '/.local/bin/wrappedhl', 'w+') as file:
        file.write('''#!/bin/sh
    cd ~
    export _JAVA_AWT_WM_NONREPARENTING=1
    export XCURSOR_SIZE=24
    exec Hyprland ''')

        file.seek(0)
        for line in file:
            line.strip()

    # create .desktop file
    with open('/usr/share/wayland-sessions/hyprland.desktop', 'w+') as file:
        file.write(f'''[Desktop Entry]
Name=Hyprland
Comment=An intelligent dynamic tiling Wayland compositor
Exec={home}/.local/bin/wrappedhl
Type=Application ''')

        file.seek(0)
        for line in file:
            line.strip()

    # making them executable
    subprocess.run(f'chmod +x {home}/.local/bin/wrappedhl', shell=True)
    subprocess.run('chmod +x /usr/share/wayland-sessions/hyprland.desktop', shell=True)

    logger.info('Created hyprland .desktop and wrappedhl')
    console.print('Created hyprland .desktop and wrappedhl :heavy_check_mark:', style='ok')


# install programs dnf
def install_programs_dnf():
    console.rule("Installing All Programs DNF ", style='checked')

    parser = SlimParser('data.conf')
    parser.cleanAll()

    repos = parser.get('Repolist')
    main = parser.get('Main')
    programs = parser.get('Programs')
    flatpaks = parser.get('Flatpak')
    copr = parser.get('Copr')

    # try to add repos
    if repos:
        for url in repos:
            try:
                subprocess.run(f'sudo dnf config-manager addrepo --from-repofile={url}', shell=True)
            except Exception as e:
                console.print(Exception(), ":x:", style='error')
                logging.critical(f"Error adding extra repos: {str(e)}")

    if copr:
        for id in copr:
            try:
                subprocess.run(f'sudo dnf copr enable -y {id}', shell=True)
            except Exception as e:
                console.print(Exception(), ":x:", style='error')
                logging.critical(f"Err adding Copr: {str(e)}")

    # for some reason they have to be passed to dnf individually
    # instead of unpacked list *programs
    main.extend(programs)
    for program in main:
        try:
            subprocess.run(f'dnf install -y {program} ', shell=True)
        except Exception as e:
            console.print(Exception(), ":x:", style='error')
            logging.critical(f"Error at Installing programs: {str(e)}")

    for fp in flatpaks:
        try:
            subprocess.run(f'flatpak install flathub -y {fp}', shell=True)
        except Exception as e:
            console.print(Exception(), ":x:", style='error')
            logging.critical(f"Error at Installing flatpaks: {str(e)}")

    logger.info('Installed programs in data.config')


## checks for sudo
def sudo_check():
    sudo_user = os.getenv("SUDO_USER")
    if sudo_user is None:
        console.print("This program must run as sudo -HE ./script ", style='error')
        exit()
    else:
        console.print('ok :heavy_check_mark:', style='ok')


# Oh my zsh setup + flathub
def zsh_fonts():
    console.rule("Installing Zsh fonts", style='checked')

    try:
        console.print("installing oh my zsh ", style='ok')

        # installs oh my zsh
        ohmy_url = "https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh"
        zsh_installer = requests.get(ohmy_url, timeout=10)
        open('install.sh', 'wb').write(zsh_installer.content)

        # removing exec line
        with open('install.sh', 'r') as f:
            contents = f.read()
        pattern = r'^\s*exec zsh -l$'
        matches = re.findall(pattern, contents, flags=re.MULTILINE)
        if matches:
            contents = re.sub(pattern, '', contents, flags=re.MULTILINE)
            with open('install.sh', 'w') as f:
                f.write(contents)

        subprocess.run('chmod +x install.sh ', stdout=subprocess.DEVNULL, shell=True, check=True)
        subprocess.run(f'sudo -u {user} ./install.sh', shell=True, check=True)

        console.print("installing oh my zsh auto suggestions ", style='ok')
        # zsh auto suggestions
        subprocess.run(
            f'git clone https://github.com/zsh-users/zsh-autosuggestions {home}/.oh-my-zsh/custom/plugins/zsh-autosuggestions',
            shell=True,
        )

        console.print("installing powerlevel10k ", style='ok')
        # powerlevel 10k
        subprocess.run(
            f"git clone --depth=1 https://github.com/romkatv/powerlevel10k.git {home}/.oh-my-zsh/custom/themes/powerlevel10k",
            shell=True,
        )
        logger.info('Installed  Oh_my_zsh , powerlevel10k , autosuggestions')

    except Exception as e:
        logging.warning(f"Could not set up Zsh: {str(e)}")
        console.print("Error setting up ohmyzsh :X:", style='error')

    console.print("installing flathub", style='ok')
    # flathub
    subprocess.run(
        'flatpak remote-add --if-not-exists flathub https://flatInstalled programs in data.txthub.org/repo/flathub.flatpakrepo',
        shell=True,
        stdout=subprocess.DEVNULL,
    )
    logger.info('Installed  Flathub')


# copy and override dotfiles
def copy_dotfiles(setup):
    console.rule("Copying Dotfiles", style='checked')

    # list of relevant configs
    lis = os.listdir('dotfiles/desktop')

    # filter out .
    lis = [item for item in lis if not item.startswith('.')]

    destination = os.path.join(home, '.config')

    shutil.copy2('dotfiles/.zshrc', home)
    shutil.copy2('dotfiles/.p10k.zsh', home)
    shutil.copy2('dotfiles/.vimrc', home)
    shutil.copy2('dotfiles/.ideavimrc', home)

    if setup == 'l':
        console.print("Setting up dotfiles for Laptop", style='ok')
        # copying files recursively
        for dir in lis:
            print(subprocess.run(f'cp -r {dir} {destination}', shell=True))

    elif setup == 'd':
        console.print("Setting up dotfiles for Desktop", style='ok')

        for dir in lis:
            source = os.path.join('dotfiles', dir)
            dest = os.path.join(destination, dir)

            try:
                print(shutil.copytree(source, dest, dirs_exist_ok=True))
            except NotADirectoryError:
                shutil.copy2(source, dest)

        console.print("Dotfiles copied :heavy_check_mark:", style='ok')
        logger.info('Dotfiles copied')


def executable_scripts():
    console.rule('Making scripts executable', style='checked')

    for root, _, files in os.walk(os.path.join(home, '.config')):
        for element in files:
            if '.sh' in element or '.py' in element:
                try:
                    subprocess.run(f"chmod +x {os.path.join(root, element)}", shell=True)
                except Exception as e:
                    logging.critical(f"Error at executable_scripts: {str(e)}")
    console.print("Job done :heavy_check_mark:", style='ok')
    logger.info('Made scripts in .config executable')


# need sudo
def msic_configs():
    console.rule('Setting up final configs', style='checked')

    current_dir = os.getcwd()
    try:
        os.mkdir('misic')
        os.mkdir(os.path.join(home, '.themes'))
    except FileExistsError:
        pass

    os.chdir(os.path.join(current_dir, 'misic'))

    fonts_url = "https://github.com/acidburnmonkey/fonts"
    fonts_dir = os.path.join(home, ".fonts")
    try:
        Repo.clone_from(fonts_url, fonts_dir)
    except Exception as e:
        print(f"Failed to clone repository: {e}")

    # to system
    shutil.copytree(home + "/.fonts", '/usr/share/fonts/', dirs_exist_ok=True)

    console.print("Fonts downloaded :heavy_check_mark:", style='ok')
    logger.info('Fonts downloaded ')

    # Icons
    subprocess.run(
        'git clone --depth 1 https://github.com/EliverLara/candy-icons.git /usr/share/icons/candy-icons',
        shell=True,
        stdout=subprocess.DEVNULL,
    )
    console.print("Icons have been downloaded :heavy_check_mark:", style='ok')
    logger.info('candy-icons downloaded')

    try:
        url = 'https://github.com/catppuccin/gtk/releases/download/v1.0.3/catppuccin-mocha-blue-standard+default.zip'
        urllib.request.urlretrieve(url, 'catppuccin-mocha-blue.zip')

        with zipfile.ZipFile('catppuccin-mocha-blue.zip') as zip:
            zip.extractall(os.path.join(home, '.themes'))

        # to system
        shutil.copytree(home + "/.themes", '/usr/share/themes/', dirs_exist_ok=True)

        console.print("Themes have been downloaded :heavy_check_mark:", style='ok')
        logger.info('Themes have been downloaded')

        ## Set themes Gtk
        subprocess.run("gsettings set org.gnome.desktop.interface icon-theme 'candy-icons'", shell=True)
        subprocess.run(
            "gsettings set org.gnome.desktop.interface gtk-theme 'catppuccin-mocha-blue-standard+default'", shell=True
        )
        subprocess.run("gsettings set org.gnome.desktop.interface font-name 'Roboto-Regular'", shell=True)

        # Flatpak force theme
        subprocess.run("flatpak override --filesystem=$HOME/.themes", shell=True)
        subprocess.run("flatpak override --env=GTK_THEME=catppuccin-mocha-blue-standard+default", shell=True)
        console.print("Themes have been Set :heavy_check_mark:", style='ok')
        logger.info('Themes have been Set')

    except Exception as e:
        logging.critical(f"Could not get themes :{str(e)}")
        console.print("Error with Themes :X:", style='error')

    # codec and multimedia
    try:
        subprocess.run('dnf swap ffmpeg-free ffmpeg --allowerasing', shell=True)
        subprocess.run(
            'dnf update @multimedia --setopt="install_weak_deps=False" --exclude=PackageKit-gstreamer-plugin',
            shell=True,
        )
        subprocess.run('dnf update @sound-and-video', shell=True)

        console.print("ffmpeg non free installed + all codecs :heavy_check_mark:", style='ok')
        logger.info('ffmpeg non free installed + all codecs ')

    except Exception as e:
        logging.critical(f"Could not install codecs :{str(e)}")
        console.print("Something failed with new codecs :X:", style='error')


def systemd():
    console.rule('Enabling user services', style='checked')

    user_services = [
        'gnome-keyring.service',
        'ssh-agent.service',
        'polkit-gnome-authentication-agent.service',
        'hypridle.service',
        'gnome-keyring-daemon.service',
    ]

    try:
        for services in user_services:
            subprocess.run(f'systemctl --user enable {services}', shell=True)
    except Exception as e:
        logging.critical(f"Could not start service :{str(e)}")
        console.print("Error starting some services :X:", style='error')


if __name__ == '__main__':
    main()
