import os
import subprocess
from ftplib import FTP
import shutil
import msvcrt
import time
import json
import sys
import signal
import logging
import pyfiglet

# Display fancy ASCII art banner with instructions
banner_text = """
Kubifyer
"""
instructions = """
Instructions:
1. Start Kobo.
2. Turn on WiFI.
3. Turn on FTP via NickelMenu ---> FTP.
4. Drop .EPUB/.PDF files into the Inbox folder.
5. Watch files being processed and uploaded.
6. When all files have been uploaded, Import Books via NickelMenu ---> Import books.
7. ????.
8. Profit.
"""
ascii_banner = pyfiglet.figlet_format(banner_text)
print(ascii_banner)
print(instructions)


# Get the absolute path to the current directory where main.py is located
current_directory = os.path.dirname(os.path.abspath(__file__))

# Load the configuration from config.json
config_file = os.path.join(current_directory, "config.json")
with open(config_file, "r") as f:
    config = json.load(f)

# Set up logging
log_file = os.path.join(current_directory, config["script_log_file"])
error_log_file = os.path.join(current_directory, config["error_log_file"])
processed_log_file = os.path.join(current_directory, config["processed_log_file"])
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s [%(levelname)s]: %(message)s",
    handlers=[
        logging.FileHandler(log_file),
        logging.StreamHandler(sys.stdout)
    ]
)

# Extract configuration values
ftp_server = config["ftp_server"]
ftp_username = config["ftp_username"]
ftp_password = config["ftp_password"]
ftp_port = config["ftp_port"]
ftp_path = config["ftp_path"]
inbox_directory = os.path.join(current_directory, config["inbox_directory"])
outbox_directory = os.path.join(current_directory, config["outbox_directory"])
archive_directory = os.path.join(current_directory, config["archive_directory"])
bin_directory = os.path.join(current_directory, config["bin_directory"])

def open_inbox_folder():
    # Open the Inbox folder in Windows Explorer
    inbox_folder = os.path.join(current_directory, config["inbox_directory"])
    try:
        os.startfile(inbox_folder)
    except Exception as e:
        logging.error(f"Error opening the Inbox folder: {str(e)}")

    # Open the user's download folder
    try:
        user_download_folder = os.path.join(os.environ["USERPROFILE"], "Downloads")
        os.startfile(user_download_folder)
    except Exception as e:
        logging.error(f"Error opening the user's download folder: {str(e)}")

open_inbox_folder()

def execute_executable(file_path):
    executable_path = os.path.join(bin_directory, "kepubify-windows-64bit.exe")
    try:
        subprocess.run([executable_path, file_path, "-o", outbox_directory], check=True)
        logging.info(f"Kubified: {file_path}")
    except subprocess.CalledProcessError as e:
        logging.error(f"Error executing the executable: {str(e)}")
        with open(error_log_file, "a") as error_log:
            error_log.write(f"Error executing the executable: {str(e)}\n")

def file_exists_on_ftp(ftp, remote_file_path):
    # Check if the file already exists on the FTP server
    try:
        ftp.cwd(os.path.dirname(remote_file_path))
        file_list = ftp.nlst()
        return os.path.basename(remote_file_path) in file_list
    except Exception as e:
        logging.error(f"Failed to check if the file exists on the FTP server: {str(e)}")
        return False  # Return False if the check fails

def upload_via_ftp(file_path):
    ftp = FTP()
    try:
        ftp.connect(ftp_server, ftp_port)
        ftp.login(ftp_username, ftp_password)
        remote_file_path = ftp_path + os.path.basename(file_path)

        # Check if the file already exists on the FTP server
        remote_directory = os.path.dirname(remote_file_path)
        ftp.cwd(remote_directory)
        if os.path.basename(remote_file_path) in ftp.nlst():
            logging.warning(f"File with the same name already exists on the FTP server: {remote_file_path}")
            # Move the outbox file to the archive folder without uploading
            shutil.move(file_path, os.path.join(archive_directory, os.path.basename(file_path)))
            logging.info(f"File archived: {file_path}")
            return False

        with open(file_path, "rb") as file:
            ftp.storbinary(f"STOR {remote_file_path}", file)
        logging.info(f"Uploaded: {file_path}")
        return True
    except Exception as e:
        logging.error("Connection ERROR!! Open Kobo and ensure that WIFI and FTP is turned on!")
        logging.error(f"FTP Error: {str(e)}")
        with open(error_log_file, "a") as error_log:
            error_log.write(f"FTP Error: {str(e)}\n")
        return False
    finally:
        try:
            ftp.quit()
        except Exception as e:
            logging.error("Connection ERROR!! Open Kobo and ensure that WIFI and FTP is turned on!")
            logging.error(f"FTP Error: {str(e)}")
            with open(error_log_file, "a") as error_log:
                error_log.write(f"FTP Error: {str(e)}\n")

def process_pdf(file_path):
    # Move the PDF file directly to the outbox folder
    shutil.move(file_path, os.path.join(outbox_directory, os.path.basename(file_path)))
    logging.info(f"Moved to outbox: {file_path}")

def load_processed_files():
    processed_files = set()
    if os.path.exists(processed_log_file):
        with open(processed_log_file, "r") as f:
            for line in f:
                file_name = line.strip()
                if os.path.exists(os.path.join(inbox_directory, file_name)) or os.path.exists(os.path.join(outbox_directory, file_name)):
                    processed_files.add(file_name)
    return processed_files

def update_log_file(processed_files):
    with open(processed_log_file, "w") as f:
        for file in processed_files:
            f.write(file + "\n")

def exit_gracefully(signal, frame):
    logging.info("\nExiting the script...")
    sys.exit(0)

def main():
    # Set up the keyboard interrupt handler
    signal.signal(signal.SIGINT, exit_gracefully)

    while True:
        processed_files = load_processed_files()
        # Check for any keypress
        if msvcrt.kbhit():
            try:
                key = msvcrt.getch().decode('utf-8').lower()
                if key == 'q':
                    logging.info("\nForceful exit requested...")
                    sys.exit(0)
            except UnicodeDecodeError:
                pass

        # Process files in the inbox directory
        inbox_files = os.listdir(inbox_directory)
        new_inbox_files = [file for file in inbox_files if file not in processed_files]

        for file in new_inbox_files:
            file_path = os.path.join(inbox_directory, file)
            if file.endswith(".pdf"):
                # Process PDF files separately
                process_pdf(file_path)
            else:
                # Process non-PDF files with the executable
                if file in processed_files:
                    logging.warning(f"File {file} has already been processed according to the process log and won't be uploaded.")
                    continue

                # Check if the file is already in the archive folder (without the "_converted.kepub.epub" extension)
                file_base_name = os.path.splitext(file)[0]
                converted_file_name = f"{file_base_name}_converted.kepub.epub"
                if converted_file_name in os.listdir(archive_directory):
                    logging.warning(f"File {file} has already been processed and is present in the archive folder. It won't be uploaded.")
                    continue

                print(f"Kubifying: {file_path}")
                execute_executable(file_path)
                print(f"Kubified: {file_path}")

                # Additional logging to indicate if the file exists in the archive folder
                if os.path.exists(os.path.join(archive_directory, file)):
                    logging.info(f"File {file} has been successfully archived after processing.")
                else:
                    logging.warning(f"File {file} was not found in the archive folder after processing.")


        # Process files in the outbox directory
        outbox_files = os.listdir(outbox_directory)
        new_outbox_files = [file for file in outbox_files if file not in processed_files]

        for file in outbox_files:
            file_path = os.path.join(outbox_directory, file)
            success = upload_via_ftp(file_path)
            if success:
                # Move the outbox file to the archive folder after successful upload
                shutil.move(file_path, os.path.join(archive_directory, file))
                logging.info(f"Archived: {file_path}")
                processed_files.add(file)  # Add to processed files only after successful upload

        # Delete files from the inbox folder after they have been successfully uploaded and archived
        for file in new_inbox_files:
            inbox_file_path = os.path.join(inbox_directory, file)
            if os.path.exists(inbox_file_path):
                os.remove(inbox_file_path)
                logging.info(f"Deleted from inbox: {inbox_file_path}")

        # Only update the log file when necessary
        if new_inbox_files or new_outbox_files:
            update_log_file(processed_files)

        # Add a small delay to avoid using too much CPU
        time.sleep(10)

if __name__ == "__main__":
    main()