import streamlit as st
import os
import img2pdf
from pypdf import PdfWriter, PdfReader
import io
import subprocess
import sys
import re

# Define the output directory within the app folder of the project
# Assumes the script is in /path/to/project/app/app_pdf_compiler.py
# The output will be in /path/to/project/app/
# Get the directory where the script is located
SCRIPT_DIR = os.path.dirname(__file__)
# Assume the 'app' folder is where the script is, and project root is one level up
APP_FOLDER = SCRIPT_DIR 
OUTPUT_PDF_NAME = "compiled_document.pdf"
OUTPUT_PDF_PATH = os.path.abspath(os.path.join(APP_FOLDER, OUTPUT_PDF_NAME))

# Ensure the app folder exists (it should, as the script is in it)
# os.makedirs(APP_FOLDER, exist_ok=True) # Not strictly necessary if script is in APP_FOLDER

def natural_sort_key(s):
    """
    Natural sort key that handles numeric substrings so that 'file2' comes before 'file10'.
    """
    return [int(text) if text.isdigit() else text.lower() for text in re.split(r'(\d+)', s)]

def compile_pdfs_and_images(folder_path, output_path):
    """
    Compiles all supported files (PDF, PNG, JPG, JPEG) from a given folder
    into a single PDF file, sorted by filename.
    Scales all pages to a consistent width while maintaining aspect ratio.
    """
    allowed_extensions = ('.pdf', '.png', '.jpg', '.jpeg')
    files_to_process = []

    try:
        for item in os.listdir(folder_path):
            if os.path.isfile(os.path.join(folder_path, item)):
                if item.lower().endswith(allowed_extensions):
                    files_to_process.append(item)
    except FileNotFoundError:
        st.error(f"Error: Folder not found at '{folder_path}'. Please check the path.")
        return False, 0
    except PermissionError:
        st.error(f"Error: Permission denied to access folder '{folder_path}'.")
        return False, 0
    except Exception as e:
        st.error(f"An unexpected error occurred while listing files: {e}")
        return False, 0

    if not files_to_process:
        st.warning("No compatible files (PDF, PNG, JPG, JPEG) found in the selected folder.")
        return False, 0

    # Sort files by name in ascending natural order (handles numbers in filenames)
    files_to_process.sort(key=natural_sort_key)

    pdf_writer = PdfWriter()
    processed_file_count = 0
    total_files = len(files_to_process)

    # Define a target width for all pages. 2000px at 96 DPI is ~1500 points.
    TARGET_WIDTH = 1500.0

    st.info(f"Found {total_files} files to process. Starting compilation...")
    progress_bar = st.progress(0)
    status_text = st.empty()

    for i, filename in enumerate(files_to_process):
        file_path = os.path.join(folder_path, filename)
        status_text.text(f"Processing file {i+1}/{total_files}: {filename}")
        try:
            if filename.lower().endswith('.pdf'):
                reader = PdfReader(file_path)
                for page in reader.pages:
                    original_width = page.mediabox.width
                    if original_width > 0:
                        scale_factor = TARGET_WIDTH / float(original_width)
                        page.scale(sx=scale_factor, sy=scale_factor)
                    pdf_writer.add_page(page)
            elif filename.lower().endswith(('.png', '.jpg', '.jpeg')):
                # Convert image to PDF bytes
                pdf_bytes = img2pdf.convert(file_path)
                
                # Read the converted PDF
                img_pdf_stream = io.BytesIO(pdf_bytes)
                img_pdf_reader = PdfReader(img_pdf_stream)
                
                if img_pdf_reader.pages:
                    page = img_pdf_reader.pages[0]
                    original_width = page.mediabox.width
                    if original_width > 0:
                        scale_factor = TARGET_WIDTH / float(original_width)
                        page.scale(sx=scale_factor, sy=scale_factor)
                    pdf_writer.add_page(page)
                else:
                    st.warning(f"Could not convert image {filename} to a PDF page. Skipping.")
                    continue # Skip this file
            processed_file_count += 1
        except Exception as e:
            st.error(f"Could not process file {filename}: {e}")
        progress_bar.progress((i + 1) / total_files)
    
    status_text.text("Compilation finished.")

    if processed_file_count == 0 and total_files > 0:
        st.error("No files were successfully processed. Output PDF not created.")
        return False, 0

    try:
        with open(output_path, "wb") as f_out:
            pdf_writer.write(f_out)
        return True, processed_file_count
    except Exception as e:
        st.error(f"Error writing compiled PDF: {e}")
        return False, processed_file_count

st.set_page_config(layout="wide")
st.title("📄 PDF and Image Compiler")

# Initialize session state for folder path if it doesn't exist
if 'folder_path' not in st.session_state:
    st.session_state.folder_path = ""
st.markdown("""
Select a folder containing PDF, PNG, JPG, or JPEG files.
This app will sort them by name (ascending) and compile them into a single PDF document.
The output file will be named `compiled_document.pdf` and saved in the same folder as this script (the `app` folder).
""")

# Function to open folder dialog using helper script and update session state
def select_folder():
    try:
        # Determine the path to the helper script (assuming it's in the same 'app' directory)
        helper_script_path = os.path.join(os.path.dirname(__file__), "folder_picker_helper.py")
        
        # Run the helper script as a subprocess
        # Use sys.executable to ensure it runs with the same Python interpreter
        process = subprocess.Popen(
            [sys.executable, helper_script_path],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        stdout, stderr = process.communicate(timeout=30) # Added timeout

        if process.returncode == 0 and stdout:
            folder_selected = stdout.strip()
            if folder_selected:
                st.session_state.folder_path = folder_selected
            else:
                # User might have cancelled the dialog, which is not an error
                # st.session_state.folder_path = "" # Or keep previous selection
                pass 
        elif stderr:
            st.error(f"Error selecting folder: {stderr}")
        # else: # No stdout, no stderr, but also no success - could be cancellation
            # st.warning("Folder selection was cancelled or failed without error message.")

    except subprocess.TimeoutExpired:
        st.error("Folder selection timed out.")
    except FileNotFoundError:
        st.error(f"Error: The helper script 'folder_picker_helper.py' was not found in the app directory.")
    except Exception as e:
        st.error(f"An unexpected error occurred during folder selection: {e}")

# Button to trigger folder selection
if st.button("Browse and Select Folder", key="browse_folder"):
    select_folder()

# Display the selected folder path
if st.session_state.folder_path:
    st.info(f"Selected folder: `{st.session_state.folder_path}`")
else:
    st.warning("No folder selected yet. Please select a folder to proceed.")

# Compile button - disabled if no folder is selected
if st.button("Compile to PDF", type="primary", disabled=not st.session_state.folder_path):
    if st.session_state.folder_path: # Redundant check due to 'disabled' but good for clarity
        if os.path.isdir(st.session_state.folder_path):
            st.markdown("---")
            success, count = compile_pdfs_and_images(st.session_state.folder_path, OUTPUT_PDF_PATH)
            if success:
                st.success(f"Successfully compiled {count} files into '{os.path.basename(OUTPUT_PDF_PATH)}' (located at: {OUTPUT_PDF_PATH}).")
                try:
                    with open(OUTPUT_PDF_PATH, "rb") as pdf_file:
                        st.download_button(
                            label="Download Compiled PDF",
                            data=pdf_file,
                            file_name=OUTPUT_PDF_NAME,
                            mime="application/pdf"
                        )
                except PermissionError:
                    st.error(
                        f"PermissionError: Could not open the compiled PDF at '{OUTPUT_PDF_PATH}' for download. "
                        f"On macOS, this might be due to missing 'Full Disk Access' for your Terminal or Python. "
                        f"Please check System Settings > Privacy & Security > Full Disk Access. "
                        f"The file was created, but the app couldn't read it back for the download button."
                    )
                except Exception as e:
                    st.error(f"An unexpected error occurred while preparing the PDF for download: {e}")
            elif count == 0 and not success: # Handles cases where no files were found or folder error
                pass # Messages are already shown by compile_pdfs_and_images
            else:
                st.error("PDF compilation failed. Check messages above.")
        else:
            st.error("The provided path is not a valid directory. Please enter a correct folder path.")
    else:
        st.warning("Please enter a folder path.")

st.markdown("---")
st.caption("Ensure the folder path is accessible by this application.")
