import os
import json
import time
import pyfiglet
import subprocess
import shutil
from pathlib import Path

# Get the directory where this module is located
MODULE_DIR = Path(__file__).parent

# ============================
# Load Configuration
# ============================
def load_config():
    config_path = MODULE_DIR / "config.json"
    with open(config_path, "r") as f:
        return json.load(f)

def load_themes():
    themes_path = MODULE_DIR / "themes.json"
    with open(themes_path, "r") as f:
        return json.load(f)

config = load_config()
themes = load_themes()

theme = themes[config["default_theme"]]

TITLE_COLOR = theme["title"]
SUB_COLOR = theme["subtitle"]
CREATOR_COLOR = theme["creator"]
NUM_COLOR = theme["menu_number"]
TEXT_COLOR = theme["menu_text"]
HEADER_COLOR = theme["header"]
RESET = theme["reset"]

ANIM_SPEED = config["animation_speed"]

# ============================
# File Type Mappings
# ============================
FILE_EXECUTABLES = {
    ".py": "python3",
    ".js": "node",
    ".sh": "bash",
    ".rb": "ruby",
    ".go": "go run",
}

FILE_MEDIA = {".png", ".jpg", ".jpeg", ".gif", ".mp4", ".webm", ".mkv", ".avi", ".mov", ".bmp", ".svg"}

FILE_TEXT = {".txt", ".md", ".json", ".yaml", ".yml", ".xml", ".html", ".css", ".log", ".csv"}

# ============================
# Utility Functions
# ============================
def clear():
    os.system("cls" if os.name == "nt" else "clear")

def loading(text="Loading"):
    clear()
    for i in range(3):
        print(f"{TEXT_COLOR}{text}{'.' * (i+1)}{RESET}")
        time.sleep(ANIM_SPEED)
        clear()

# ============================
# Title Rendering
# ============================
def render_title():
    fonts_to_try = ["block", "slant", "banner"]
    ascii_banner = config["main_title"]
    
    for font in fonts_to_try:
        try:
            ascii_banner = pyfiglet.figlet_format(config["main_title"], font=font)
            break
        except:
            continue
    print(TITLE_COLOR + ascii_banner + RESET)
    print(SUB_COLOR + config["subtitle"] + RESET)
    print(CREATOR_COLOR + config["creator"] + RESET + "\n")

# ============================
# Auto Detect Categories
# ============================
def get_categories():
    base_path = MODULE_DIR / "Python"
    items = []

    for item in os.listdir(base_path):
        full = os.path.join(base_path, item)
        if os.path.isdir(full):
            items.append(item)

    return sorted(items)

# ============================
# Get All Files (Recursive)
# ============================
def get_all_files(path):
    """Recursively get all files and directories"""
    items = []
    try:
        for item in os.listdir(path):
            full_path = os.path.join(path, item)
            if os.path.isdir(full_path):
                items.append({"name": item, "type": "dir", "path": full_path})
            else:
                items.append({"name": item, "type": "file", "path": full_path})
    except PermissionError:
        pass
    return sorted(items, key=lambda x: (x["type"] != "dir", x["name"]))

# ============================
# Determine File Action
# ============================
def get_file_action(filepath):
    """Determine what action to take based on file extension"""
    ext = Path(filepath).suffix.lower()
    
    if ext in FILE_EXECUTABLES:
        return "execute"
    elif ext in FILE_MEDIA:
        return "open"
    elif ext in FILE_TEXT:
        return "view"
    else:
        return "unknown"

# ============================
# Handle File Execution
# ============================
def handle_file(filepath):
    """Execute or open a file based on its type"""
    action = get_file_action(filepath)
    
    loading("Processing")
    
    if action == "execute":
        ext = Path(filepath).suffix.lower()
        interpreter = FILE_EXECUTABLES[ext]
        os.system(f"{interpreter} {filepath}")
    elif action == "open":
        # Handle different OS for opening media files
        if os.name == "nt":  # Windows
            os.startfile(filepath)
        else:  # Linux/Mac
            os.system(f"xdg-open '{filepath}' 2>/dev/null || open '{filepath}' 2>/dev/null")
    elif action == "view":
        clear()
        try:
            with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
                print(f.read())
        except Exception as e:
            print(f"Error reading file: {e}")
    else:
        clear()
        print(f"No handler for file type: {Path(filepath).suffix}")
    
    input("\nPress ENTER to return to the menu...")

# ============================
# README Viewer
# ============================
def view_readme():
    loading("Opening README")
    os.system("clear")
    with open("README.md", "r") as f:
        print(f.read())
    input("\nPress ENTER to return to the menu...")

# ============================
# File Management Functions
# ============================
def delete_file(filepath):
    """Delete a file or directory"""
    try:
        if os.path.isdir(filepath):
            confirm = input(f"{TEXT_COLOR}Delete directory '{os.path.basename(filepath)}'? (yes/no): {RESET}")
            if confirm.lower() == "yes":
                shutil.rmtree(filepath)
                print(f"{TEXT_COLOR}✓ Directory deleted{RESET}")
            else:
                print(f"{TEXT_COLOR}Cancelled{RESET}")
        else:
            confirm = input(f"{TEXT_COLOR}Delete file '{os.path.basename(filepath)}'? (yes/no): {RESET}")
            if confirm.lower() == "yes":
                os.remove(filepath)
                print(f"{TEXT_COLOR}✓ File deleted{RESET}")
            else:
                print(f"{TEXT_COLOR}Cancelled{RESET}")
    except Exception as e:
        print(f"{TEXT_COLOR}Error: {e}{RESET}")
    time.sleep(1)

def rename_file(filepath):
    """Rename a file or directory"""
    try:
        old_name = os.path.basename(filepath)
        new_name = input(f"{TEXT_COLOR}Enter new name (current: {old_name}): {RESET}")
        
        if new_name and new_name != old_name:
            parent_dir = os.path.dirname(filepath)
            new_path = os.path.join(parent_dir, new_name)
            os.rename(filepath, new_path)
            print(f"{TEXT_COLOR}✓ Renamed to '{new_name}'{RESET}")
        else:
            print(f"{TEXT_COLOR}Cancelled{RESET}")
    except Exception as e:
        print(f"{TEXT_COLOR}Error: {e}{RESET}")
    time.sleep(1)

def move_file(filepath):
    """Move a file or directory to another location"""
    try:
        dest = input(f"{TEXT_COLOR}Enter destination path: {RESET}")
        
        if dest and os.path.exists(dest):
            if os.path.isdir(dest):
                shutil.move(filepath, os.path.join(dest, os.path.basename(filepath)))
                print(f"{TEXT_COLOR}✓ Moved successfully{RESET}")
            else:
                print(f"{TEXT_COLOR}Destination must be a directory{RESET}")
        else:
            print(f"{TEXT_COLOR}Invalid destination{RESET}")
    except Exception as e:
        print(f"{TEXT_COLOR}Error: {e}{RESET}")
    time.sleep(1)

def copy_file(filepath):
    """Copy a file or directory"""
    try:
        dest = input(f"{TEXT_COLOR}Enter destination path: {RESET}")
        
        if dest and os.path.exists(dest):
            if os.path.isdir(filepath):
                shutil.copytree(filepath, os.path.join(dest, os.path.basename(filepath)))
                print(f"{TEXT_COLOR}✓ Directory copied successfully{RESET}")
            else:
                shutil.copy2(filepath, os.path.join(dest, os.path.basename(filepath)))
                print(f"{TEXT_COLOR}✓ File copied successfully{RESET}")
        else:
            print(f"{TEXT_COLOR}Invalid destination{RESET}")
    except Exception as e:
        print(f"{TEXT_COLOR}Error: {e}{RESET}")
    time.sleep(1)

def edit_file_with_editor(filepath, editor_choice):
    """Edit a file with chosen editor"""
    try:
        if editor_choice == "nano":
            os.system(f"nano '{filepath}'")
            print(f"{TEXT_COLOR}✓ File saved{RESET}")
        elif editor_choice == "vim":
            os.system(f"vim '{filepath}'")
            print(f"{TEXT_COLOR}✓ File saved{RESET}")
        elif editor_choice == "builtin":
            edit_file_builtin(filepath)
        time.sleep(1)
    except Exception as e:
        print(f"{TEXT_COLOR}Error: {e}{RESET}")
        time.sleep(1)

def edit_file_builtin(filepath):
    """Simple built-in terminal editor"""
    try:
        with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
            content = f.read()
        
        clear()
        print(HEADER_COLOR + f"EDITING: {os.path.basename(filepath)}" + RESET)
        print(f"{TEXT_COLOR}(Type your content, then type 'SAVE' on a new line to save, or 'CANCEL' to discard){RESET}\n")
        print(f"{TEXT_COLOR}Current content:{RESET}")
        print(f"{SUB_COLOR}{content}{RESET}\n")
        
        print(f"{TEXT_COLOR}Enter new content (or press Enter to keep existing):{RESET}")
        new_content = []
        while True:
            line = input()
            if line.upper() == "SAVE":
                with open(filepath, "w", encoding="utf-8") as f:
                    f.write("\n".join(new_content))
                print(f"{TEXT_COLOR}✓ File saved{RESET}")
                break
            elif line.upper() == "CANCEL":
                print(f"{TEXT_COLOR}Cancelled - no changes made{RESET}")
                break
            else:
                new_content.append(line)
        time.sleep(1)
    except Exception as e:
        print(f"{TEXT_COLOR}Error: {e}{RESET}")
        time.sleep(1)

def create_file(current_path):
    """Create a new file"""
    try:
        filename = input(f"{TEXT_COLOR}Enter filename (with extension): {RESET}")
        if not filename:
            print(f"{TEXT_COLOR}Cancelled{RESET}")
            return
        
        filepath = os.path.join(current_path, filename)
        
        if os.path.exists(filepath):
            print(f"{TEXT_COLOR}File already exists!{RESET}")
            time.sleep(1)
            return
        
        # Ask if user wants to add content
        add_content = input(f"{TEXT_COLOR}Add content? (yes/no): {RESET}")
        
        if add_content.lower() == "yes":
            print(f"{TEXT_COLOR}Enter content (type 'END' on a new line to finish):{RESET}")
            content_lines = []
            while True:
                line = input()
                if line.upper() == "END":
                    break
                content_lines.append(line)
            
            with open(filepath, "w", encoding="utf-8") as f:
                f.write("\n".join(content_lines))
        else:
            open(filepath, "w").close()
        
        print(f"{TEXT_COLOR}✓ File created: {filename}{RESET}")
        time.sleep(1)
    except Exception as e:
        print(f"{TEXT_COLOR}Error: {e}{RESET}")
        time.sleep(1)

def view_file_info(filepath):
    """Display file information"""
    try:
        size = os.path.getsize(filepath)
        if size > 1024*1024:
            size_str = f"{size/(1024*1024):.2f} MB"
        elif size > 1024:
            size_str = f"{size/1024:.2f} KB"
        else:
            size_str = f"{size} B"
        
        mod_time = time.ctime(os.path.getmtime(filepath))
        file_type = "Directory" if os.path.isdir(filepath) else f"File ({Path(filepath).suffix})"
        
        clear()
        print(HEADER_COLOR + "FILE INFORMATION" + RESET)
        print(f"{TEXT_COLOR}Name: {os.path.basename(filepath)}{RESET}")
        print(f"{TEXT_COLOR}Type: {file_type}{RESET}")
        print(f"{TEXT_COLOR}Size: {size_str}{RESET}")
        print(f"{TEXT_COLOR}Modified: {mod_time}{RESET}")
        print(f"{TEXT_COLOR}Path: {filepath}{RESET}")
    except Exception as e:
        print(f"{TEXT_COLOR}Error: {e}{RESET}")

def manage_file(filepath, display_name=""):
    """Show file management menu"""
    while True:
        clear()
        render_title()
        view_file_info(filepath)
        
        print(f"\n{HEADER_COLOR}[::] File Operations [::]" + RESET)
        print(f"{NUM_COLOR}[01]{RESET} {TEXT_COLOR}View/Execute/Open{RESET}")
        print(f"{NUM_COLOR}[02]{RESET} {TEXT_COLOR}Edit (Nano){RESET}")
        print(f"{NUM_COLOR}[03]{RESET} {TEXT_COLOR}Edit (Vim){RESET}")
        print(f"{NUM_COLOR}[04]{RESET} {TEXT_COLOR}Edit (Built-in){RESET}")
        print(f"{NUM_COLOR}[05]{RESET} {TEXT_COLOR}Rename{RESET}")
        print(f"{NUM_COLOR}[06]{RESET} {TEXT_COLOR}Delete{RESET}")
        print(f"{NUM_COLOR}[07]{RESET} {TEXT_COLOR}Move{RESET}")
        print(f"{NUM_COLOR}[08]{RESET} {TEXT_COLOR}Copy{RESET}")
        print(f"{NUM_COLOR}[00]{RESET} {TEXT_COLOR}Back{RESET}\n")
        
        choice = input(NUM_COLOR + "Select operation: " + RESET)
        
        if choice == "00":
            break
        elif choice == "01":
            if not os.path.isdir(filepath):
                handle_file(filepath)
            else:
                print(f"{TEXT_COLOR}(Directory - cannot execute){RESET}")
                time.sleep(1)
        elif choice == "02":
            edit_file_with_editor(filepath, "nano")
            break
        elif choice == "03":
            edit_file_with_editor(filepath, "vim")
            break
        elif choice == "04":
            edit_file_builtin(filepath)
            break
        elif choice == "05":
            rename_file(filepath)
            break
        elif choice == "06":
            delete_file(filepath)
            break
        elif choice == "07":
            move_file(filepath)
            break
        elif choice == "08":
            copy_file(filepath)
        else:
            continue


def browse_directory(path, display_name=""):
    """Browse and interact with files in a directory"""
    while True:
        clear()
        render_title()
        
        items = get_all_files(path)
        
        print(HEADER_COLOR + f"📁 {display_name or path}" + RESET)
        print()
        
        if not items:
            print(TEXT_COLOR + "(empty directory)" + RESET)
        else:
            for i, item in enumerate(items, start=1):
                icon = "📁" if item["type"] == "dir" else "📄"
                print(f"{NUM_COLOR}[{i:02d}]{RESET} {icon} {TEXT_COLOR}{item['name']}{RESET}")
        
        print(f"\n{NUM_COLOR}[+]{RESET} {TEXT_COLOR}Create New File{RESET}")
        print(f"{NUM_COLOR}[00]{RESET} Back\n")
        
        choice = input(NUM_COLOR + "Select an item: " + RESET)
        
        if choice == "00":
            break
        elif choice == "+":
            create_file(path)
        else:
            try:
                idx = int(choice) - 1
                selected = items[idx]
                
                if selected["type"] == "dir":
                    browse_directory(selected["path"], selected["name"])
                else:
                    manage_file(selected["path"], selected["name"])
            except:
                pass


# ============================
# About Section
# ============================
def about():
    clear()
    print(HEADER_COLOR + "ABOUT PY-HUB" + RESET)
    print(TEXT_COLOR + "A modular Python-based launcher hub." + RESET)
    print(f"{TEXT_COLOR}Version: 1.0{RESET}")
    print(f"{CREATOR_COLOR}{config['creator']}{RESET}")
    input("\nPress ENTER to return to the menu...")

# ============================
# Main Menu
# ============================
def main():
    while True:
        clear()
        render_title()

        categories = get_categories()

        print(HEADER_COLOR + "[::] Select a category [::]" + RESET)
        print()

        for i, cat in enumerate(categories, start=1):
            print(f"{NUM_COLOR}[{i:02d}]{RESET} {TEXT_COLOR}{cat.capitalize()}{RESET}")

        print(f"\n{NUM_COLOR}[98]{RESET} View README")
        print(f"{NUM_COLOR}[99]{RESET} About")
        print(f"{NUM_COLOR}[00]{RESET} Exit\n")

        choice = input(NUM_COLOR + "[-] Select an option: " + RESET)

        if choice == "00":
            loading("Exiting")
            clear()
            print(TEXT_COLOR + "Goodbye!" + RESET)
            break

        elif choice == "98":
            view_readme()

        elif choice == "99":
            about()

        else:
            try:
                idx = int(choice) - 1
                category = categories[idx]
            except:
                continue

            browse_directory(os.path.join(MODULE_DIR, "Python", category), category)


if __name__ == "__main__":
    main()
