from datetime import datetime
import os
import time
import zipfile
import shutil
from pathlib import Path
import requests
from circup.commands import main as circup_cli

# TODO: maybe change these to use the first URLs i.e. https://learn.adafruit.com/elements/3198279/download?type=zip
#  instead of the redirect URLs that are direct to the CDN. That will make easier for users to add apps here.
#  The code will need to follow the redirect and get the filename from the next URL.
LEARN_PROJECT_URLS = [
    "https://cdn-learn.adafruit.com/downloads/zip/3194974/Metro/Metro_RP2350_Snake.zip?timestamp={}",
    "https://cdn-learn.adafruit.com/downloads/zip/3195762/Metro/Metro_RP2350_Memory/memory_game.zip?timestamp={}",
    "https://cdn-learn.adafruit.com/downloads/zip/3195805/Metro/Metro_RP2350_CircuitPython_Matrix.zip?timestamp={}",
    "https://cdn-learn.adafruit.com/downloads/zip/3194658/Metro/Metro_RP2350_FlappyNyanCat.zip?timestamp={}",
    "https://cdn-learn.adafruit.com/downloads/zip/3196927/Metro/Metro_RP2350_Match3/match3_game.zip?timestamp={}",
    "https://cdn-learn.adafruit.com/downloads/zip/3194422/Metro/Metro_RP2350_Breakout.zip?timestamp={}",
    "https://cdn-learn.adafruit.com/downloads/zip/3196755/Metro/Metro_RP2350_Chips_Challenge.zip?timestamp={}",
    "https://cdn-learn.adafruit.com/downloads/zip/3198116/Metro/Metro_RP2350_Minesweeper.zip?timestamp={}",
    "https://cdn-learn.adafruit.com/downloads/zip/3198279/Fruit_Jam/Larsio_Paint_Music.zip?timestamp={}",
    "https://cdn-learn.adafruit.com/downloads/zip/3203853/Fruit_Jam/Fruit_Jam_IRC_Client.zip?timestamp={}",
]

def create_font_specific_zip(font_path: Path, src_dir: Path, learn_projects_dir: Path, output_dir: Path):
    # Get font name without extension
    font_name = font_path.stem
    
    # Create output zip filename
    output_zip = output_dir / f"fruit_jam_{font_name}.zip"
    
    # Create a clean temporary directory for building the zip
    temp_dir = output_dir / "temp"
    if temp_dir.exists():
        shutil.rmtree(temp_dir)
    temp_dir.mkdir(parents=True)
    
    try:
        # Copy src contents
        shutil.copytree(src_dir, temp_dir, dirs_exist_ok=True)
        
        # Create fonts directory and copy the specific font
        fonts_dir = temp_dir / "fonts"
        fonts_dir.mkdir(parents=True, exist_ok=True)
        shutil.copy2(font_path, fonts_dir / "terminal.lvfontbin")
        
        # Extract learn-projects contents into apps directory
        apps_dir = temp_dir / "apps"
        apps_dir.mkdir(parents=True, exist_ok=True)
        for zip_path in learn_projects_dir.glob("*.zip"):
            # Create app-specific directory using zip name without extension
            app_name = zip_path.stem
            app_dir = apps_dir / app_name
            app_dir.mkdir(parents=True, exist_ok=True)
            
            # Extract zip contents and process them
            with zipfile.ZipFile(zip_path, 'r') as zf:
                # Find the directory containing code.py
                code_dir = None
                for path in zf.namelist():
                    if path.endswith('/code.py'):
                        code_dir = str(Path(path).parent) + '/'
                        break
                
                if not code_dir:
                    print(f"Warning: No code.py found in {zip_path}")
                    continue
                
                # Extract files from the code.py directory to app directory
                for path in zf.namelist():
                    if path.startswith(code_dir):
                        # Skip the lib directory as we'll handle it separately
                        if 'lib/' in path:
                            continue
                        if path.endswith("/"):
                            # skip directories, they will get created by
                            # mkdir(parents=True) below
                            continue
                        
                        # Get the relative path from code_dir
                        rel_path = path[len(code_dir):]
                        if rel_path:
                            # Extract the file
                            source = zf.open(path)
                            target = app_dir / rel_path
                            target.parent.mkdir(parents=True, exist_ok=True)
                            with open(target, 'wb') as f:
                                f.write(source.read())
                
                # Handle lib directory specially - move to root
                for path in zf.namelist():
                    if '/lib/' in path:
                        # Get the part of the path after 'lib/'
                        lib_index = path.index('/lib/') + 5  # skip past '/lib/'
                        rel_path = path[lib_index:]
                        
                        # Skip directory entries
                        if not rel_path or path.endswith('/'):
                            continue
                            
                        # Extract the file to root lib directory
                        source = zf.open(path)
                        target = temp_dir / 'lib' / rel_path
                        # Ensure parent directory exists
                        target.parent.mkdir(parents=True, exist_ok=True)
                        # Write the file
                        with open(target, 'wb') as f:
                            f.write(source.read())

        # copy builtin apps
        shutil.copytree("builtin_apps", apps_dir, dirs_exist_ok=True)
        shutil.copyfile("mock_boot_out.txt", temp_dir / "boot_out.txt")

        # install launcher required libs
        circup_cli(["--path", temp_dir, "install", "--auto"],
                   standalone_mode=False)

        # install builtin apps required libs
        for builtin_app_dir in os.listdir("builtin_apps"):
            circup_cli(["--path", temp_dir, "install", "--auto", "--auto-file", f"apps/{builtin_app_dir}/code.py"],
                       standalone_mode=False)
        os.remove(temp_dir / "boot_out.txt")
        # Create the final zip file
        with zipfile.ZipFile(output_zip, 'w', zipfile.ZIP_DEFLATED) as zf:
            for file_path in temp_dir.rglob("*"):
                if file_path.is_file():
                    modification_time = datetime(2000, 1, 1, 0, 0, 0)
                    modification_timestamp = modification_time.timestamp()
                    os.utime(file_path, (modification_timestamp, modification_timestamp))
                    arcname = file_path.relative_to(temp_dir)
                    zf.write(file_path, arcname)
                    
        print(f"Created {output_zip}")
        
    finally:
        # Clean up temporary directory
        shutil.rmtree(temp_dir, ignore_errors=True)


def download_learn_projects():
    for url in LEARN_PROJECT_URLS:
        response = requests.get(url.format(int(time.time())), allow_redirects=True)
        resp_url = response.url
        #print(resp_url)
        filename = resp_url.split("/")[-1].split("?")[0]
        with open(f"learn-projects/{filename}", 'wb') as f:
            f.write(response.content)


def main():

    # download all learn project zips
    download_learn_projects()

    # Get the project root directory
    root_dir = Path(__file__).parent
    
    # Set up paths
    fonts_dir = root_dir / "fonts"
    src_dir = root_dir / "src"
    learn_projects_dir = root_dir / "learn-projects"
    output_dir = root_dir / "dist"

    # delete output dir if it exists
    if output_dir.exists():
        shutil.rmtree(output_dir)

    # Create output directory
    output_dir.mkdir(parents=True, exist_ok=True)
    
    # Process each font
    for font_path in fonts_dir.glob("*.lvfontbin"):
        create_font_specific_zip(font_path, src_dir, learn_projects_dir, output_dir)

if __name__ == "__main__":
    main()
