import os
import sys
import glob
import shutil
import subprocess
from datetime import datetime
import re
import json
import time
from typing import Optional

import pandas as pd
from PIL import Image
import tkinter as tk
from tkinter import filedialog

PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))

OUTPUT_DIRS = {
    "excel": os.path.join(PROJECT_ROOT, "outputs", "excel"),
    "images": os.path.join(PROJECT_ROOT, "outputs", "images"),
    "ocr": os.path.join(PROJECT_ROOT, "outputs", "ocr"),
    "ocr_cache": os.path.join(PROJECT_ROOT, "outputs", "ocr_cache"),
    "pdf_extract": os.path.join(PROJECT_ROOT, "outputs", "pdf_extract"),
    "pdf_text": os.path.join(PROJECT_ROOT, "outputs", "pdf_text"),
    "pdf_to_excel": os.path.join(PROJECT_ROOT, "outputs", "pdf_to_excel"),
    "text": os.path.join(PROJECT_ROOT, "outputs", "text"),
    "done": os.path.join(PROJECT_ROOT, "outputs", "done"),
}

TESSERACT_CMD = os.environ.get("TESSERACT_CMD", "tesseract")
GS_PATH = os.environ.get("GS_PATH", "gswin64c")
DPI = int(os.environ.get("OCR_DPI", "300"))
CROP_TOP_RATIO = float(os.environ.get("CROP_TOP_RATIO", "0.2"))
CROP_BOTTOM_RATIO = float(os.environ.get("CROP_BOTTOM_RATIO", "0.9"))


def create_output_dirs():
    for path in OUTPUT_DIRS.values():
        os.makedirs(path, exist_ok=True)


def _resolve_dependency(cmd):
    return shutil.which(cmd) or cmd


def check_dependencies():
    missing = []
    tesseract = _resolve_dependency(TESSERACT_CMD)
    gs = _resolve_dependency(GS_PATH)

    if not shutil.which(tesseract):
        missing.append("Tesseract (TESSERACT_CMD)")
    if not shutil.which(gs):
        missing.append("Ghostscript (GS_PATH)")

    if missing:
        print("Missing dependencies:")
        for dep in missing:
            print(f"- {dep}")
        print("Update PATH or set TESSERACT_CMD/GS_PATH environment variables.")
        return False

    return True


base = sys.modules.setdefault("ocr_to_excel", sys.modules[__name__])

try:
    import colorama
    colorama.init()
    _COLORAMA_AVAILABLE = True
except Exception:
    _COLORAMA_AVAILABLE = False

try:
    from rich.console import Console
    from rich.panel import Panel
    from rich.text import Text
    from rich import box
    _RICH_AVAILABLE = True
except Exception:
    _RICH_AVAILABLE = False

ENABLE_COLOR = True

COLOR = {
    "reset": "\033[0m",
    "bold": "\033[1m",
    "blue": "\033[94m",
    "yellow": "\033[93m",
    "green": "\033[92m",
    "gray": "\033[90m",
}


def colorize(text: str, color: Optional[str] = None, bold: bool = False) -> str:
    if not ENABLE_COLOR or not sys.stdout.isatty():
        return text

    if os.name == "nt" and not _COLORAMA_AVAILABLE:
        return text

    prefix = ""
    if bold:
        prefix += COLOR["bold"]
    if color and color in COLOR:
        prefix += COLOR[color]
    return f"{prefix}{text}{COLOR['reset']}"


def print_banner():
    banner = [
        "██████╗ ███████╗██████╗ ███████╗██████╗ ",
        "██╔══██╗██╔════╝██╔══██╗██╔════╝██╔══██╗",
        "██║  ██║█████╗  ██████╔╝█████╗  ██║  ██║",
        "██║  ██║██╔══╝  ██╔═══╝ ██╔══╝  ██║  ██║",
        "██████╔╝███████╗██║     ███████╗██████╔╝",
        "╚═════╝ ╚══════╝╚═╝     ╚══════╝╚═════╝ ",
        "",
        "██████╗ ██████╗ ███████╗██████╗ ███████╗██╗  ██╗ ██████╗███████╗██╗     ",
        "██╔══██╗██╔══██╗██╔════╝╚════██╗██╔════╝╚██╗██╔╝██╔════╝██╔════╝██║     ",
        "██████╔╝██║  ██║█████╗   █████╔╝█████╗   ╚███╔╝ ██║     █████╗  ██║     ",
        "██╔═══╝ ██║  ██║██╔══╝  ██╔═══╝ ██╔══╝   ██╔██╗ ██║     ██╔══╝  ██║     ",
        "██║     ██████╔╝██║     ███████╗███████╗██╔╝ ██╗╚██████╗███████╗███████╗",
        "╚═╝     ╚═════╝ ╚═╝     ╚══════╝╚══════╝╚═╝  ╚═╝ ╚═════╝╚══════╝╚══════╝",
    ]

    if _RICH_AVAILABLE:
        console = Console()
        console.print(Text("\n".join(banner), style="bold yellow"))
        return

    for line in banner:
        print(colorize(line, "yellow", bold=True))


def clear_screen():
    if _RICH_AVAILABLE:
        Console().clear()
        return

    os.system("cls" if os.name == "nt" else "clear")


def ensure_output_dir():
    base.create_output_dirs()
    return base.OUTPUT_DIRS["excel"]


def ensure_aux_dirs():
    log_dir = os.path.join(base.PROJECT_ROOT, "outputs", "logs")
    error_dir = os.path.join(base.PROJECT_ROOT, "outputs", "error_reports")
    os.makedirs(log_dir, exist_ok=True)
    os.makedirs(error_dir, exist_ok=True)
    return log_dir, error_dir


def get_state_path():
    return os.path.join(base.PROJECT_ROOT, "outputs", "cli_state.json")


def load_state():
    state_path = get_state_path()
    if not os.path.exists(state_path):
        return {}
    try:
        with open(state_path, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception:
        return {}


def save_state(state):
    state_path = get_state_path()
    os.makedirs(os.path.dirname(state_path), exist_ok=True)
    with open(state_path, "w", encoding="utf-8") as f:
        json.dump(state, f, indent=2)


def update_last_folder(folder_path):
    state = load_state()
    state["last_folder"] = folder_path
    save_state(state)


def get_last_folder():
    state = load_state()
    return state.get("last_folder")


def log_message(message, log_path=None):
    print(message)
    if log_path:
        with open(log_path, "a", encoding="utf-8") as f:
            f.write(message + "\n")


def build_log_path(pdf_path, log_dir):
    base_name = os.path.splitext(os.path.basename(pdf_path))[0]
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"{base_name}_{timestamp}.log"
    return os.path.join(log_dir, filename)


def normalize_path(user_input):
    value = user_input.strip().strip('"').strip("'")
    return value


def is_pdf_file(file_path):
    return os.path.isfile(file_path) and file_path.lower().endswith(".pdf")


def build_output_path(pdf_path, output_dir):
    base_name = os.path.splitext(os.path.basename(pdf_path))[0]
    date_stamp = datetime.now().strftime("%Y-%m-%d")
    filename = f"{base_name}_{date_stamp}.xlsx"
    output_path = os.path.join(output_dir, filename)

    if not os.path.exists(output_path):
        return output_path

    counter = 1
    while True:
        filename = f"{base_name}_{date_stamp}_{counter}.xlsx"
        output_path = os.path.join(output_dir, filename)
        if not os.path.exists(output_path):
            return output_path
        counter += 1


def parse_name_components(full_name):
    if not full_name or not isinstance(full_name, str):
        return ("", "", "")

    name = full_name.strip()
    name = re.sub(r"\s+", " ", name)
    name = re.sub(r"[^\w\s,.-]", "", name)

    lastname = ""
    firstname = ""
    middlename = ""

    if "," in name:
        parts = name.split(",", 1)
        lastname = parts[0].strip()
        rest = parts[1].strip() if len(parts) > 1 else ""

        name_parts = rest.split()
        if len(name_parts) >= 2:
            firstname = name_parts[0].strip()
            middlename = " ".join(name_parts[1:]).strip()
        elif len(name_parts) == 1:
            firstname = name_parts[0].strip()
    else:
        name_parts = name.split()
        if len(name_parts) >= 3:
            firstname = name_parts[0].strip()
            middlename = " ".join(name_parts[1:-1]).strip()
            lastname = name_parts[-1].strip()
        elif len(name_parts) == 2:
            firstname = name_parts[0].strip()
            lastname = name_parts[1].strip()
        elif len(name_parts) == 1:
            lastname = name_parts[0].strip()

    lastname = re.sub(r"^\W+|\W+$", "", lastname).strip()
    firstname = re.sub(r"^\W+|\W+$", "", firstname).strip()
    middlename = re.sub(r"^\W+|\W+$", "", middlename).strip()

    return (lastname, firstname, middlename)


def fix_tin_number(tin):
    if not tin or pd.isna(tin) or tin == "":
        return ""

    tin_str = str(tin)
    tin_clean = re.sub(r"\D", "", tin_str)

    if not tin_clean:
        return ""

    if len(tin_clean) == 12:
        return tin_clean

    if len(tin_clean) >= 9:
        return tin_clean[:9] + "000"

    if len(tin_clean) < 9:
        padded = tin_clean.ljust(9, "0")
        return padded + "000"

    return tin_clean


def extract_tin_from_dob_position(record_text, dob):
    if not dob:
        return ""

    dob_pos = record_text.find(dob)
    if dob_pos == -1:
        return ""

    after_dob = record_text[dob_pos + len(dob):dob_pos + len(dob) + 30].strip()

    tin_match = re.search(r"(\d{12})", after_dob)
    if tin_match:
        return tin_match.group(1)

    tin_patterns = [
        r"(\d{3}[- ]?\d{3}[- ]?\d{3}[- ]?\d{3})",
        r"(\d{3}\s\d{3}\s\d{3}\s\d{3})",
        r"(\d{9})",
        r"(\d{10,11})",
    ]

    for pattern in tin_patterns:
        tin_match = re.search(pattern, after_dob)
        if tin_match:
            tin_raw = tin_match.group(1)
            tin_clean = re.sub(r"\D", "", tin_raw)
            return tin_clean

    return ""


def parse_single_record(record_text):
    record_text = re.sub(r"\s+", " ", record_text).strip()
    fields = [""] * 17

    item_match = re.search(r"(OSEC-DECSB-[A-Z0-9]+-\d+-\d{4})", record_text)
    if item_match:
        fields[0] = item_match.group(1)

    if item_match:
        rest = record_text[item_match.end():]
        salary_match = re.search(r"(\d{1,3}(?:,\d{3})+)", rest)
        if salary_match:
            position_text = rest[:salary_match.start()].strip()
            position_text = re.sub(r"\s+", " ", position_text)
            fields[1] = position_text

    salary_numbers = re.findall(r"(\d{1,3}(?:,\d{3})+)", record_text)
    if len(salary_numbers) >= 1:
        fields[2] = salary_numbers[0]
    if len(salary_numbers) >= 2:
        fields[3] = salary_numbers[1]

    if len(salary_numbers) >= 2:
        second_salary = salary_numbers[1]
        salary_pos = record_text.find(second_salary)
        if salary_pos != -1:
            after_salary = record_text[salary_pos + len(second_salary):]
            step_match = re.search(r"(\d)", after_salary[:20])
            if step_match:
                fields[4] = step_match.group(1)

    area_match = re.search(r"1533", record_text)
    if area_match:
        fields[5] = "1533"

    ppa_match = re.search(r"(\d{15})", record_text)
    if ppa_match:
        fields[8] = ppa_match.group(1)
    else:
        ppa_match = re.search(r"(\d{13,14})", record_text)
        if ppa_match:
            fields[8] = ppa_match.group(1)

    if ppa_match:
        text_before_ppa = record_text[:ppa_match.start()].strip()

        type_level_pattern = re.search(r"([MF])\s+([KSAM])\s*$", text_before_ppa)
        if type_level_pattern:
            fields[6] = type_level_pattern.group(1)
            fields[7] = type_level_pattern.group(2)
        else:
            type_level_pattern = re.search(r"([MF])([KSAM])\s*$", text_before_ppa)
            if type_level_pattern:
                fields[6] = type_level_pattern.group(1)
                fields[7] = type_level_pattern.group(2)

    if not fields[6]:
        type_match = re.search(r"\b([MF])\b", record_text)
        if type_match:
            fields[6] = type_match.group(1)

    if not fields[7]:
        level_match = re.search(r"\b([KSAM])\b", record_text)
        if level_match:
            fields[7] = level_match.group(1)

    name = ""
    sex = ""
    dob = ""

    if ppa_match:
        ppa_end = ppa_match.end()
        rest_after_ppa = record_text[ppa_end:]

        name_pattern = r"(.+?)\s+([MF])\s+(\d{2}/\d{2}/\d{2})"
        name_match = re.search(name_pattern, rest_after_ppa)

        if name_match:
            name = name_match.group(1).strip()
            sex = name_match.group(2)
            dob = name_match.group(3)
        else:
            alt_patterns = [
                r"(.+?)\s+([MF])\s+\d{12}",
                r"(.+?)([MF])(\d{2}/\d{2}/\d{2})",
            ]

            for pattern in alt_patterns:
                alt_match = re.search(pattern, rest_after_ppa)
                if alt_match:
                    name = alt_match.group(1).strip()
                    sex = alt_match.group(2)
                    if len(alt_match.groups()) >= 3 and "/" in alt_match.group(3):
                        dob = alt_match.group(3)
                    break

        if name:
            name = re.sub(r"^\W+|\W+$", "", name)
            name = re.sub(r"\s+", " ", name)

    fields[9] = name
    fields[10] = sex
    fields[11] = dob

    if not dob:
        dob_match = re.search(r"(\d{2}/\d{2}/\d{2})", record_text)
        if dob_match:
            dob = dob_match.group(1)
            fields[11] = dob

    tin = ""
    if dob:
        tin = extract_tin_from_dob_position(record_text, dob)

    if not tin:
        tin_patterns = [
            r"TIN[:\s]*(\d{3}[- ]?\d{3}[- ]?\d{3}[- ]?\d{3})",
            r"(\d{3}[- ]\d{3}[- ]\d{3}[- ]\d{3})",
            r"(\d{12})",
            r"TIN\s*[:.]?\s*(\d+)",
        ]

        for pattern in tin_patterns:
            tin_match = re.search(pattern, record_text, re.IGNORECASE)
            if tin_match:
                tin_raw = tin_match.group(1)
                tin = re.sub(r"\D", "", tin_raw)
                if tin:
                    break

    if tin:
        tin = fix_tin_number(tin)

    fields[12] = tin

    all_dates = re.findall(r"(\d{2}/\d{2}/\d{2})", record_text)
    if all_dates:
        date_list = []
        for date in all_dates:
            if not dob or date != dob:
                date_list.append(date)

        if len(date_list) >= 1:
            fields[13] = date_list[0]
        if len(date_list) >= 2:
            fields[14] = date_list[1]

    status_match = re.search(r"\b([A-Z])\s*$", record_text)
    if status_match:
        fields[15] = status_match.group(1)
    else:
        status_match = re.search(r"\b([A-Z])\s+(?:PBET|CESE|RA1080|LET|CSP)\b", record_text)
        if status_match:
            fields[15] = status_match.group(1)

    eligibility_pattern = r"\b(PBET|CESE|RA1080|LET|CSP|RA1080|PBET|CESE)\b"
    eligibility_match = re.search(eligibility_pattern, record_text)
    if eligibility_match:
        fields[16] = eligibility_match.group(1)
    else:
        words = record_text.split()
        for word in reversed(words):
            if (
                len(word) >= 3
                and word.isalpha()
                and word not in ["P", "M", "F", "K", "S", "A"]
                and not re.match(r"^\d", word)
            ):
                fields[16] = word
                break

    return fields


def parse_ocr_to_records(ocr_text):
    records = []
    item_numbers_seen = set()

    lines = [line.strip() for line in ocr_text.split("\n") if line.strip()]
    print(f"Total lines in OCR text: {len(lines)}")

    record_starts = []
    for i, line in enumerate(lines):
        if re.match(r"OSEC-DECSB-[A-Z0-9]+-\d+-\d{4}", line):
            record_starts.append(i)

    print(f"Found {len(record_starts)} record starting points across all pages")

    for idx, start_idx in enumerate(record_starts):
        end_idx = record_starts[idx + 1] if idx + 1 < len(record_starts) else len(lines)
        record_lines = lines[start_idx:end_idx]
        record_text = " ".join(record_lines)

        record = parse_single_record(record_text)

        if record and any(record):
            item_number = record[0]
            if item_number and item_number in item_numbers_seen:
                print(f"  Skipping duplicate item number: {item_number}")
                continue

            if item_number:
                item_numbers_seen.add(item_number)

            if record[9]:
                lastname, firstname, middlename = parse_name_components(record[9])
                record[9] = f"{lastname}|||{firstname}|||{middlename}"

            records.append(record)

            if len(records) % 10 == 0:
                print(f"  Parsed {len(records)} unique records...")

    print(f"Total unique records after removing duplicates: {len(records)}")
    return records


def convert_pdf_to_excel(pdf_path, output_dir, log_dir, error_dir):
    pdf_name = os.path.splitext(os.path.basename(pdf_path))[0]
    temp_dir = os.path.join(base.PROJECT_ROOT, "temp_images")
    os.makedirs(temp_dir, exist_ok=True)
    log_path = build_log_path(pdf_path, log_dir)
    log_message(f"Starting conversion: {pdf_path}", log_path)
    log_message(f"Log file: {log_path}", log_path)

    try:
        log_message("Converting PDF to images...", log_path)
        gs_command = (
            f'"{base.GS_PATH}" -dSAFER -dBATCH -dNOPAUSE '
            f'-sDEVICE=pnggray -r{base.DPI} '
            f'-sOutputFile="{temp_dir}\\page_%03d.png" "{pdf_path}"'
        )
        subprocess.run(gs_command, shell=True, capture_output=True)

        png_files = sorted(glob.glob(os.path.join(temp_dir, "page_*.png")))
        if not png_files:
            log_message("No images created from PDF.", log_path)
            return None

        log_message(f"Processing {len(png_files)} page(s)...", log_path)
        for img_file in png_files:
            img = Image.open(img_file)
            w, h = img.size
            cropped = img.crop((0, int(h * base.CROP_TOP_RATIO), w, int(h * base.CROP_BOTTOM_RATIO)))
            cropped.save(img_file)

        log_message("Running OCR on all pages...", log_path)
        all_text = []
        for i, img_file in enumerate(png_files):
            log_message(f"  Page {i + 1}/{len(png_files)}", log_path)
            base_path = os.path.splitext(img_file)[0]
            cmd = f'"{base.TESSERACT_CMD}" "{img_file}" "{base_path}" -l eng --oem 3 --psm 6'
            result = subprocess.run(cmd, shell=True, capture_output=True, text=True)

            if result.returncode == 0:
                txt_file = f"{base_path}.txt"
                if os.path.exists(txt_file):
                    with open(txt_file, "r", encoding="utf-8") as f:
                        all_text.append(f.read())
                    os.remove(txt_file)
            else:
                error_base = os.path.join(error_dir, f"{pdf_name}_page_{i + 1:03d}")
                shutil.copy2(img_file, f"{error_base}.png")
                with open(f"{error_base}.txt", "w", encoding="utf-8") as f:
                    f.write(result.stderr or "OCR failed without stderr.")
                log_message(f"  OCR failed on page {i + 1}. Saved error report.", log_path)

        if not all_text:
            log_message("No text extracted from PDF.", log_path)
            return None

        full_text = "\n".join(all_text)
        records = parse_ocr_to_records(full_text)
        if not records:
            error_txt = os.path.join(error_dir, f"{pdf_name}_ocr_text.txt")
            with open(error_txt, "w", encoding="utf-8") as f:
                f.write(full_text)
            log_message("No structured data found in PDF. OCR text saved.", log_path)
            return None

        columns = [
            "ITEM NUMBER", "POSITION TITLE and SALARY GRADE",
            "ANNUAL SALARY: AUTHORIZED", "ANNUAL SALARY: ACTUAL",
            "STEP", "AREA CODE", "TYPE", "LEVEL", "P/P/A ATTRIBUTION",
            "NAME OF INCUMBENT", "SEX", "DATE OF BIRTH", "TIN",
            "DATE OF ORIGINAL APPOINTMENT", "DATE OF LAST PROMOTION",
            "STATUS", "CIVIL SERVICE ELIGIBILITY"
        ]

        df = pd.DataFrame(records, columns=columns)

        for col in df.columns:
            df[col] = df[col].astype(str).str.strip()
            df[col] = df[col].replace("", pd.NA)

        if "AREA CODE" in df.columns:
            df["AREA CODE"] = df["AREA CODE"].fillna("1533")

        log_message("Fixing TIN numbers...", log_path)
        df["TIN"] = df["TIN"].apply(fix_tin_number)

        lastnames, firstnames, middlenames = [], [], []
        for name in df["NAME OF INCUMBENT"]:
            if pd.isna(name) or name == "":
                lastnames.append("")
                firstnames.append("")
                middlenames.append("")
            else:
                parts = name.split("|||")
                if len(parts) >= 3:
                    lastnames.append(parts[0])
                    firstnames.append(parts[1])
                    middlenames.append(parts[2])
                else:
                    lastnames.append(name)
                    firstnames.append("")
                    middlenames.append("")

        df["LASTNAME"] = lastnames
        df["FIRSTNAME"] = firstnames
        df["MIDDLENAME"] = middlenames

        column_order = columns[:10] + ["LASTNAME", "FIRSTNAME", "MIDDLENAME"] + columns[10:]
        df = df[column_order]

        df = df.drop_duplicates(subset=["ITEM NUMBER"], keep="first")

        output_path = build_output_path(pdf_path, output_dir)
        df.to_excel(output_path, index=False)

        log_message(f"Excel file created: {output_path}", log_path)
        log_message(f"Total records: {len(df)}", log_path)
        log_message(f"Total pages: {len(png_files)}", log_path)
        return output_path

    except Exception as exc:
        log_message(f"Conversion failed: {exc}", log_path)
        return None

    finally:
        if os.path.exists(temp_dir):
            shutil.rmtree(temp_dir)


def open_folder(path):
    try:
        os.startfile(path)
    except Exception as exc:
        print(f"Could not open folder: {exc}")


def open_file(path):
    try:
        os.startfile(path)
    except Exception as exc:
        print(f"Could not open file: {exc}")


def upload_pdf_flow():
    output_dir = ensure_output_dir()
    log_dir, error_dir = ensure_aux_dirs()
    last_folder = get_last_folder() or base.PROJECT_ROOT

    root = tk.Tk()
    root.withdraw()
    root.attributes("-topmost", True)
    pdf_path = filedialog.askopenfilename(
        title="Select a PDF file",
        filetypes=[("PDF files", "*.pdf")],
        initialdir=last_folder,
    )
    root.destroy()

    if not pdf_path:
        return

    pdf_path = normalize_path(pdf_path)
    if not is_pdf_file(pdf_path):
        print("Invalid file. Please select a valid PDF.")
        return

    update_last_folder(os.path.dirname(pdf_path))

    if not base.check_dependencies():
        return

    output_path = convert_pdf_to_excel(pdf_path, output_dir, log_dir, error_dir)
    if output_path:
        open_file(output_path)
        open_folder(output_dir)


def batch_upload_flow():
    output_dir = ensure_output_dir()
    log_dir, error_dir = ensure_aux_dirs()
    last_folder = get_last_folder() or base.PROJECT_ROOT

    root = tk.Tk()
    root.withdraw()
    root.attributes("-topmost", True)
    pdf_paths = filedialog.askopenfilenames(
        title="Select PDF files",
        filetypes=[("PDF files", "*.pdf")],
        initialdir=last_folder,
    )
    root.destroy()

    if not pdf_paths:
        return

    pdf_paths = [normalize_path(path) for path in pdf_paths if is_pdf_file(path)]
    if not pdf_paths:
        print("No valid PDF files selected.")
        return

    update_last_folder(os.path.dirname(pdf_paths[0]))

    if not base.check_dependencies():
        return

    for pdf_path in pdf_paths:
        convert_pdf_to_excel(pdf_path, output_dir, log_dir, error_dir)

    open_folder(output_dir)


def watch_folder_flow():
    output_dir = ensure_output_dir()
    log_dir, error_dir = ensure_aux_dirs()
    last_folder = get_last_folder() or base.PROJECT_ROOT

    root = tk.Tk()
    root.withdraw()
    root.attributes("-topmost", True)
    watch_dir = filedialog.askdirectory(title="Select a folder to watch", initialdir=last_folder)
    root.destroy()

    if not watch_dir:
        return

    update_last_folder(watch_dir)

    if not base.check_dependencies():
        return

    print("Watching folder for new PDFs. Press Ctrl+C to stop.")
    seen = {}
    try:
        while True:
            pdf_files = glob.glob(os.path.join(watch_dir, "*.pdf"))
            for pdf_path in pdf_files:
                mtime = os.path.getmtime(pdf_path)
                if pdf_path in seen and seen[pdf_path] == mtime:
                    continue

                seen[pdf_path] = mtime
                convert_pdf_to_excel(pdf_path, output_dir, log_dir, error_dir)

            time.sleep(3)
    except KeyboardInterrupt:
        print("Stopped watching folder.")


def show_all_files_flow():
    output_dir = ensure_output_dir()
    open_folder(output_dir)


def display_menu(output_dir):
    if _RICH_AVAILABLE:
        console = Console()
        body = Text(
            "1. Upload PDF\n"
            "2. Batch Convert\n"
            "3. Watch Folder\n"
            "4. Show All Files\n"
            "5. Exit",
            style="bright_blue",
        )
        status = Text()
        status.append("Output folder:\n", style="bold")
        status.append(f"{output_dir}\n", style="green")
        last_folder = get_last_folder() or "(none)"
        status.append(f"Last folder: {last_folder}\n", style="grey50")
        status.append("Tip: Close Excel before converting for best results.", style="grey50")

        console.print(
            Panel(
                body,
                title=Text("Main Menu", style="bold green"),
                border_style="bright_blue",
                box=box.DOUBLE,
            )
        )
        console.print(
            Panel(
                status,
                title=Text("Status", style="bold green"),
                border_style="bright_blue",
                box=box.ROUNDED,
            )
        )
        return

    border = "+" + "-" * 58 + "+"
    section = "|" + " MAIN MENU".ljust(58) + "|"
    line_1 = "|  1. Upload PDF".ljust(59) + "|"
    line_2 = "|  2. Batch Convert".ljust(59) + "|"
    line_3 = "|  3. Watch Folder".ljust(59) + "|"
    line_4 = "|  4. Show All Files".ljust(59) + "|"
    line_5 = "|  5. Exit".ljust(59) + "|"

    print(colorize(border, "blue"))
    print(colorize(section, "green", bold=True))
    print(colorize(border, "blue"))
    print(colorize(line_1, "blue"))
    print(colorize(line_2, "blue"))
    print(colorize(line_3, "blue"))
    print(colorize(line_4, "blue"))
    print(colorize(line_5, "blue"))
    print(colorize(border, "blue"))


def main():
    output_dir = ensure_output_dir()
    clear_screen()
    print_banner()
    if _RICH_AVAILABLE:
        Console().print("Initializing menu...", style="grey50")
    else:
        print(colorize("Initializing menu...", "gray"))

    while True:
        clear_screen()
        print_banner()
        display_menu(output_dir)
        choice = input("Enter your choice [1-5]: ").strip()

        if choice == "1":
            upload_pdf_flow()
        elif choice == "2":
            batch_upload_flow()
        elif choice == "3":
            watch_folder_flow()
        elif choice == "4":
            show_all_files_flow()
        elif choice == "5":
            print("Goodbye!")
            break
        else:
            print("Invalid choice. Please enter 1-5.")


if __name__ == "__main__":
    sys.path.insert(0, base.PROJECT_ROOT)
    main()
