from contextlib import asynccontextmanager
from fastapi import FastAPI
import os
from functions.utils import clean_directory
import asyncio

from settings import (save_directory, output_directory, workflow_directory,
    waiting_queue, in_process_queue, MAX_PROCESSING_QUEUE_LENGTH, TIMEOUT_LIMIT, FINAL_FILE_NAME)
from pathlib import Path
import asyncio
import time
import datetime

@asynccontextmanager
async def lifespan(app: FastAPI):
    os.makedirs(save_directory, exist_ok=True)
    os.makedirs(output_directory, exist_ok=True)
    os.makedirs(workflow_directory, exist_ok=True)
    # # Clean the directories
    # clean_directory(workflow_directory)
    # clean_directory(save_directory)
    # clean_directory(output_directory)
    asyncio.create_task(process_files_queue())
    asyncio.create_task(clean_old_folders())
    yield


def delete_old_folders(directory_path, seconds):
    """Delete folders inside a directory if they were created more than 'days' ago."""

    # Calculate the cutoff time
    cutoff_time = time.time() - seconds  

    for folder_name in os.listdir(directory_path):
        folder_path = os.path.join(directory_path, folder_name)

        if os.path.isdir(folder_path):
            # Get the folder's creation time (platform-dependent)
            if os.name == 'nt':  # for Windows
                creation_time = os.path.getctime(folder_path)
            else:  # for Unix-based systems
                creation_time = os.path.getmtime(folder_path)

            # Check if folder is older than the cutoff time
            if creation_time < cutoff_time:
                try:
                    # Remove the directory recursively
                    os.rmdir(folder_path)
                    print(f"Deleted folder: {folder_path}")
                except Exception as e:
                    print(f"Could not delete folder {folder_path}. Reason: {e}")


def get_first_entry_with_new_workflow_id() -> dict | None:
    """Get the first workflow with a new workflow ID from the waiting queue."""
    workflows_id_in_process = [workflow_entry['workflow_id'] for workflow_entry in in_process_queue.queue]
    for entry in waiting_queue.queue:
        if entry['workflow_id'] not in workflows_id_in_process:
            return entry
    return None

# Background Processing Task
async def process_files_queue() -> None:
    """
    Background task to continuously move files from the waiting queue to
    the in-process queue when there's capacity.
    """
    
    while True:
        if not waiting_queue.queue or len(in_process_queue.queue) >= MAX_PROCESSING_QUEUE_LENGTH:
            await asyncio.sleep(1)
            continue

        # Try to get the first workflow with a new workflow ID (if it exists)
        workflow_entry = get_first_entry_with_new_workflow_id()
        if workflow_entry is None:
            # No new workflow with a new workflow ID found. Waiting...
            await asyncio.sleep(1)
            continue

        waiting_queue.remove(workflow_entry)
        in_process_queue.add(workflow_entry)
        
        # Launch a parallel task to monitor the workflow
        asyncio.create_task(monitor_workflow(workflow_entry))

async def monitor_workflow(workflow_entry: dict) -> None:
    """
    Monitors the workflow by waiting for the expected number of output files 
    to be generated, and marks its completion by creating a done file.
    """
    expected_nb_outputs: int = workflow_entry['expected_nb_outputs']
    output_folder: Path = Path(workflow_entry['output_folder'])
    timeout_limit = workflow_entry.get('timeout_limit', TIMEOUT_LIMIT)

    # Wait for the expected number of output files to be generated by the workflow
    processtimeout = True
    start_time: float = time.time()
    while time.time() - start_time < timeout_limit:
        if len(list(output_folder.glob('*'))) == expected_nb_outputs:
            processtimeout = False
            break
        elif len(list(output_folder.glob('*'))) > expected_nb_outputs:
            raise Exception("Too many files found in the output folder. Error.")
            
        await asyncio.sleep(1)
    
    if processtimeout:
        print("Timeout reached, the workflow did not complete in time. Adjust the timeout limit, and \
              check that the correct number of output files are being generated.")
        print("Expected number of outputs: ", expected_nb_outputs)
        print("Timeout: ", timeout_limit)
    
    # Remove from in-process queue and signal completion
    in_process_queue.remove(workflow_entry)
    (output_folder / FINAL_FILE_NAME).touch()

# Second background task to clean old folders
async def clean_old_folders(timeout_limit_hours: int = 24):
    """
    Background task to clean old folders in the save_directory and output_directory.
    This task will delete folders that were created more than 24 hours ago.
    """
    timeout_limit = timeout_limit_hours * 3600  # convert hours to seconds


    while True:
        delete_old_folders(save_directory, seconds=timeout_limit)
        delete_old_folders(output_directory, seconds=timeout_limit)
        await asyncio.sleep(timeout_limit/2)
