"""
Modern file and folder dialogs using DPI-aware C# helper executable
Provides crisp, native Windows 10/11 dialogs
"""

import subprocess
import os
import time
from typing import Optional, List, Tuple, Union, Dict, Any
from pathlib import Path

# Path to the compiled dialog helper
_HELPER_EXE = Path(__file__).parent / "helpers" / "DialogHelper.exe"


def _ensure_helper_built():
    """Ensure the dialog helper executable exists"""
    if not _HELPER_EXE.exists():
        raise FileNotFoundError(
            f"DialogHelper.exe not found at {_HELPER_EXE}. "
            "Please run build_helper.ps1 to compile it."
        )


def select_file(
    title: str = "Select a file",
    initialdir: Optional[str] = None,
    filetypes: Optional[List[Tuple[str, str]]] = None,
    multiple: bool = False,
    return_path: bool = True,
    detailed: bool = False,
    verbose: bool = True
) -> Optional[Union[Path, str, List[Path], List[str], Dict[str, Any]]]:
    """
    Open a modern Windows file selection dialog with crisp DPI scaling.

    Args:
        title: Dialog window title
        initialdir: Initial directory to open (defaults to current directory)
        filetypes: List of tuples for file type filters, e.g., [("Text files", "*.txt"), ("All files", "*.*")]
        multiple: If True, allows selecting multiple files
        return_path: If True, return Path objects instead of strings (default: True)
        detailed: If True, return dict with metadata (timestamps, sizes, etc.)
        verbose: If True, print selection messages (default: True)

    Returns:
        If detailed=False:
            - Path object(s) if return_path=True
            - String path(s) if return_path=False
            - None if cancelled
        If detailed=True:
            - Dictionary with keys: 'paths', 'count', 'total_size', 'selected_at', 'file_info'

    Examples:
        >>> # Get Path object (default)
        >>> filepath = select_file()
        >>> if filepath:
        ...     print(f"Selected: {filepath}")
        ...     print(f"Exists: {filepath.exists()}")
        >>>
        >>> # Get string path
        >>> filepath = select_file(return_path=False)
        >>>
        >>> # Get detailed info
        >>> result = select_file(detailed=True)
        >>> if result:
        ...     print(f"Files: {result['count']}")
        ...     print(f"Total size: {result['total_size']} bytes")
        ...     for info in result['file_info']:
        ...         print(f"  {info['name']}: {info['size']} bytes")
    """
    _ensure_helper_built()

    if initialdir is None:
        initialdir = os.getcwd()

    # Build filter string
    if filetypes:
        filter_parts = []
        for desc, pattern in filetypes:
            filter_parts.append(f"{desc} ({pattern})|{pattern}")
        filter_str = "|".join(filter_parts)
    else:
        filter_str = "All Files (*.*)|*.*"

    # Build command
    cmd = [str(_HELPER_EXE), "openfile", f"--title={title}", f"--initialdir={initialdir}",
           f"--filter={filter_str}"]

    if multiple:
        cmd.append("--multiple")

    try:
        start_time = time.time()
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=300,
            creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
        )

        if result.returncode == 0:
            output = result.stdout.strip()
            selected_at = time.time()

            if multiple:
                file_strs = output.split('|')
                if not file_strs:
                    return None

                if verbose:
                    print(f"Selected {len(file_strs)} file(s)")

                if detailed:
                    file_info = []
                    total_size = 0
                    for file_str in file_strs:
                        file_path = Path(file_str)
                        if file_path.exists():
                            stat = file_path.stat()
                            file_info.append({
                                'path': file_path if return_path else str(file_path),
                                'name': file_path.name,
                                'size': stat.st_size,
                                'modified': stat.st_mtime,
                                'created': stat.st_ctime,
                                'extension': file_path.suffix,
                                'parent': file_path.parent
                            })
                            total_size += stat.st_size

                    return {
                        'paths': [Path(f) if return_path else f for f in file_strs],
                        'count': len(file_strs),
                        'total_size': total_size,
                        'selected_at': selected_at,
                        'dialog_time': selected_at - start_time,
                        'file_info': file_info
                    }
                else:
                    if return_path:
                        return [Path(f) for f in file_strs]
                    return file_strs
            else:
                if verbose:
                    print(f"Selected: {output}")

                if detailed:
                    file_path = Path(output)
                    file_info = {}
                    if file_path.exists():
                        stat = file_path.stat()
                        file_info = {
                            'path': file_path if return_path else str(file_path),
                            'name': file_path.name,
                            'size': stat.st_size,
                            'modified': stat.st_mtime,
                            'created': stat.st_ctime,
                            'extension': file_path.suffix,
                            'parent': file_path.parent,
                            'is_file': file_path.is_file(),
                            'is_dir': file_path.is_dir()
                        }

                    return {
                        'path': file_path if return_path else output,
                        'selected_at': selected_at,
                        'dialog_time': selected_at - start_time,
                        'file_info': file_info
                    }
                else:
                    return Path(output) if return_path else output

        elif result.returncode == 2:
            if verbose:
                print("File selection cancelled")
            return None
        else:
            if verbose:
                print(f"Error: {result.stderr.strip()}")
            return None

    except subprocess.TimeoutExpired:
        if verbose:
            print("Dialog timeout")
        return None
    except Exception as e:
        if verbose:
            print(f"Error opening file dialog: {e}")
        return None


def select_folder(
    title: str = "Select a folder",
    initialdir: Optional[str] = None,
    return_path: bool = True,
    detailed: bool = False,
    verbose: bool = True
) -> Optional[Union[Path, str, Dict[str, Any]]]:
    """
    Open a modern Windows folder selection dialog with crisp DPI scaling.

    Args:
        title: Dialog window title
        initialdir: Initial directory to open (defaults to current directory)
        return_path: If True, return Path object instead of string (default: True)
        detailed: If True, return dict with metadata
        verbose: If True, print selection messages (default: True)

    Returns:
        Path object, string, dict (if detailed=True), or None if cancelled

    Example:
        >>> folder = select_folder(title="Choose output directory")
        >>> if folder:
        ...     print(f"Selected: {folder}")
        ...     print(f"Exists: {folder.exists()}")
        >>>
        >>> # Detailed info
        >>> result = select_folder(detailed=True)
        >>> print(f"Files in folder: {result['file_count']}")
        >>> print(f"Total size: {result['total_size']} bytes")
    """
    _ensure_helper_built()

    if initialdir is None:
        initialdir = os.getcwd()

    cmd = [str(_HELPER_EXE), "folder", f"--description={title}", f"--selectedpath={initialdir}"]

    try:
        start_time = time.time()
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=300,
            creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
        )

        if result.returncode == 0:
            output = result.stdout.strip()
            selected_at = time.time()

            if verbose:
                print(f"Selected folder: {output}")

            folder_path = Path(output)

            if detailed and folder_path.exists():
                # Gather folder statistics
                file_count = 0
                dir_count = 0
                total_size = 0
                files = []

                for item in folder_path.rglob('*'):
                    if item.is_file():
                        file_count += 1
                        size = item.stat().st_size
                        total_size += size
                        files.append({
                            'path': item,
                            'name': item.name,
                            'size': size,
                            'extension': item.suffix
                        })
                    elif item.is_dir():
                        dir_count += 1

                return {
                    'path': folder_path if return_path else output,
                    'name': folder_path.name,
                    'parent': folder_path.parent,
                    'file_count': file_count,
                    'dir_count': dir_count,
                    'total_size': total_size,
                    'selected_at': selected_at,
                    'dialog_time': selected_at - start_time,
                    'exists': folder_path.exists(),
                    'is_absolute': folder_path.is_absolute()
                }
            else:
                return folder_path if return_path else output

        elif result.returncode == 2:
            if verbose:
                print("Folder selection cancelled")
            return None
        else:
            if verbose:
                print(f"Error: {result.stderr.strip()}")
            return None

    except subprocess.TimeoutExpired:
        if verbose:
            print("Dialog timeout")
        return None
    except Exception as e:
        if verbose:
            print(f"Error opening folder dialog: {e}")
        return None


def save_file_dialog(
    title: str = "Save file",
    initialdir: Optional[str] = None,
    initialfile: str = "",
    defaultextension: str = "",
    filetypes: Optional[List[Tuple[str, str]]] = None,
    return_path: bool = True,
    detailed: bool = False,
    verbose: bool = True
) -> Optional[Union[Path, str, Dict[str, Any]]]:
    """
    Open a modern Windows save file dialog with crisp DPI scaling.

    Args:
        title: Dialog window title
        initialdir: Initial directory to open (defaults to current directory)
        initialfile: Default filename
        defaultextension: Default file extension (e.g., ".txt")
        filetypes: List of tuples for file type filters

    Returns:
        Selected file path as string, or None if cancelled

    Example:
        >>> filepath = save_file_dialog(
        ...     title="Save Report",
        ...     initialfile="report.txt",
        ...     defaultextension=".txt",
        ...     filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
        ... )
        >>> if filepath:
        ...     print(f"Save to: {filepath}")
        ... else:
        ...     print("Save cancelled")
    """
    _ensure_helper_built()

    if initialdir is None:
        initialdir = os.getcwd()

    # Build filter string
    if filetypes:
        filter_parts = []
        for desc, pattern in filetypes:
            filter_parts.append(f"{desc} ({pattern})|{pattern}")
        filter_str = "|".join(filter_parts)
    else:
        filter_str = "All Files (*.*)|*.*"

    # Remove leading dot from extension
    if defaultextension and defaultextension.startswith('.'):
        defaultextension = defaultextension[1:]

    # Build command
    cmd = [str(_HELPER_EXE), "savefile", f"--title={title}", f"--initialdir={initialdir}",
           f"--filename={initialfile}", f"--defaultext={defaultextension}", f"--filter={filter_str}"]

    try:
        start_time = time.time()
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=300,
            creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
        )

        if result.returncode == 0:
            output = result.stdout.strip()
            selected_at = time.time()

            if verbose:
                print(f"Save to: {output}")

            file_path = Path(output)

            if detailed:
                file_info = {
                    'path': file_path if return_path else output,
                    'name': file_path.name,
                    'parent': file_path.parent,
                    'extension': file_path.suffix,
                    'exists': file_path.exists(),
                    'is_absolute': file_path.is_absolute(),
                    'selected_at': selected_at,
                    'dialog_time': selected_at - start_time
                }

                if file_path.exists():
                    stat = file_path.stat()
                    file_info.update({
                        'size': stat.st_size,
                        'modified': stat.st_mtime,
                        'created': stat.st_ctime
                    })

                return file_info
            else:
                return file_path if return_path else output

        elif result.returncode == 2:
            if verbose:
                print("Save cancelled")
            return None
        else:
            if verbose:
                print(f"Error: {result.stderr.strip()}")
            return None

    except subprocess.TimeoutExpired:
        if verbose:
            print("Dialog timeout")
        return None
    except Exception as e:
        if verbose:
            print(f"Error opening save dialog: {e}")
        return None
