"""Functions for rclone and local filesystem interaction."""

import os
import subprocess
import re
from typing import List, Optional, Tuple


def check_rclone_version() -> Tuple[bool, str, str]:
    """Check rclone version and return (is_ok, version_string, message).

    Returns:
        Tuple of (is_valid: bool, version: str, message: str)
    """
    try:
        result = subprocess.run(
            ["rclone", "version"], capture_output=True, text=True, timeout=10
        )
        if result.returncode != 0:
            return False, "", "rclone not found or error executing"

        # Parse version from output like "rclone v1.73.3"
        version_match = re.search(r"rclone v(\d+\.\d+)", result.stdout)
        if not version_match:
            return False, "", "Could not parse rclone version"

        version_str = version_match.group(1)
        major, minor = map(int, version_str.split("."))

        # Check if version >= 1.73
        if major > 1 or (major == 1 and minor >= 73):
            return True, version_str, f"rclone v{version_str} (OK)"
        else:
            return False, version_str, f"rclone v{version_str} (minimum required: 1.73)"
    except FileNotFoundError:
        return False, "", "rclone not found in PATH"
    except Exception as e:
        return False, "", f"Error checking rclone: {e}"


def check_restic_version() -> Tuple[bool, str, str]:
    """Check restic version and return (is_ok, version_string, message).

    Returns:
        Tuple of (is_valid: bool, version: str, message: str)
    """
    try:
        result = subprocess.run(
            ["restic", "version"], capture_output=True, text=True, timeout=10
        )
        if result.returncode != 0:
            return False, "", "restic not found or error executing"

        # Parse version from output like "restic 0.18.1 compiled with go1.25.1 on linux/amd64"
        version_match = re.search(r"restic (\d+\.\d+)", result.stdout)
        if not version_match:
            return False, "", "Could not parse restic version"

        version_str = version_match.group(1)
        major, minor = map(int, version_str.split("."))

        # Check if version >= 0.18
        if major > 0 or (major == 0 and minor >= 18):
            return True, version_str, f"restic v{version_str} (OK)"
        else:
            return False, version_str, f"restic v{version_str} (minimum required: 0.18)"
    except FileNotFoundError:
        return False, "", "restic not found in PATH"
    except Exception as e:
        return False, "", f"Error checking restic: {e}"


def prompt_download(tool: str, download_url: str) -> bool:
    """Prompt user to download a tool.

    Args:
        tool: Name of the tool (rclone/restic)
        download_url: URL to download from

    Returns:
        True if user wants to download
    """
    import sys

    print(f"\n{'=' * 60}")
    print(f"  {tool.upper()} VERSION CHECK FAILED")
    print(f"{'=' * 60}")
    print(f"\nDownload URL: {download_url}")
    print("\nWould you like to download it now? (Y/n): ", end="")
    sys.stdout.flush()

    try:
        response = input().strip().lower()
        return response in ("", "y", "yes")
    except (EOFError, KeyboardInterrupt):
        return False


def run_command(command: List[str], timeout: int = 10) -> Tuple[int, str]:
    """Execute a command and return (returncode, output)."""
    try:
        result = subprocess.run(
            command,
            capture_output=True,
            text=True,
            timeout=timeout,
            check=False,
        )
        return result.returncode, result.stdout + result.stderr
    except subprocess.TimeoutExpired:
        return 1, "Command timed out"
    except Exception as e:
        return 1, str(e)


def list_remotes() -> List[str]:
    """Get list of rclone remotes."""
    rc, output = run_command(["rclone", "listremotes"])
    if rc != 0:
        return []
    return [line.strip() for line in output.splitlines() if line.strip()]


def list_remote_dir(remote: str, path: str = "") -> List[str]:
    """List directories and files in a remote path."""
    # The user specifically mentioned lsd, so let's ensure we get directories
    full_remote_path = f"{remote}{path}"

    # Try using lsf as primary because it's much easier to parse and handles both
    # But we'll fallback to lsd for directories as suggested if lsf seems empty/failed
    rc, output = run_command(
        ["rclone", "lsf", "--dir-slash", "--format", "p", full_remote_path]
    )

    if rc == 0 and output.strip():
        items = [line.strip() for line in output.splitlines() if line.strip()]
        dirs = sorted([i for i in items if i.endswith("/")])
        files = sorted([i for i in items if not i.endswith("/")])
        return dirs + files

    # Fallback/Supplemental: Use lsd as specifically suggested
    rc_lsd, output_lsd = run_command(["rclone", "lsd", full_remote_path])
    if rc_lsd == 0:
        dirs = []
        for line in output_lsd.splitlines():
            if line.strip():
                # lsd output looks like: "          -1 2023-10-10 10:10:10         -1 My Directory"
                # We need to correctly capture the name after the first 4 columns
                parts = line.split(None, 4)
                if len(parts) >= 5:
                    dirs.append(parts[4].strip() + "/")
        return sorted(dirs)

    return []


def list_local_dir(path: str) -> List[str]:
    """List directories and files in a local path."""
    try:
        items = os.listdir(path)
        # Add trailing slash to directories
        formatted_items = []
        for item in items:
            if os.path.isdir(os.path.join(path, item)):
                formatted_items.append(item + "/")
            else:
                formatted_items.append(item)

        # Sort: directories first, then files
        dirs = sorted([i for i in formatted_items if i.endswith("/")])
        files = sorted([i for i in formatted_items if not i.endswith("/")])
        return dirs + files
    except Exception:
        return []


def run_rclone_copy(mode: int, src: str, dst: str, dry_run: bool = False):
    """Run rclone copy command based on mode.

    Modes:
    1: Single file (copyto)
    2: Directory traversed (copy)
    3: Directory no-traverse (copy --no-traverse)
    """
    import sys
    import subprocess
    import os

    # Common exclude patterns - use /** for directories to exclude their contents
    exclude_patterns = [
        "--exclude",
        ".git/**",
        "--exclude",
        ".venv/**",
        "--exclude",
        "venv/**",
        "--exclude",
        "env/**",
        "--exclude",
        ".opencode/**",
        "--exclude",
        ".openai_*",
        "--exclude",
        ".*~",
        "--exclude",
        ".python-version",
        "--exclude",
        ".python-cache/**",
        "--exclude",
        ".pytest_cache/**",
    ]

    if mode == 1:
        # Single file copy - exclude patterns not supported
        cmd = ["rclone", "copyto", src, dst, "--progress"]
    elif mode == 2:
        cmd = ["rclone", "copy", src, dst, "--progress"] + exclude_patterns
    elif mode == 3:
        cmd = [
            "rclone",
            "copy",
            src,
            dst,
            "--progress",
            "--no-traverse",
        ] + exclude_patterns
    else:
        return

    if dry_run:
        cmd.append("--dry-run")

    # Clear screen for a clean output and ensure sane terminal state
    os.system("stty sane && clear")
    print(f"Executing: {' '.join(cmd)}\n")
    if dry_run:
        print("!!! DRY RUN MODE - No files will be copied !!!\n")
    print("-" * 60)

    try:
        # Run and allow user to see output
        result = subprocess.run(cmd, check=False)

        # Call stty sane again after rclone completes
        os.system("stty sane")

        print("-" * 60)
        if result.returncode == 0:
            print(
                "\n\033[92mOperation finished successfully.\033[0m Press Enter to return to mcrc..."
            )
        else:
            print(
                f"\n\033[91mOperation failed (exit code {result.returncode}).\033[0m Press Enter to return to mcrc..."
            )

        # Use a more robust input waiting mechanism
        try:
            sys.stdin.read(1)
        except:
            pass

    except KeyboardInterrupt:
        os.system("stty sane")
        print("\n\n\033[91mOperation cancelled by user.\033[0m")
        print("Press Enter to return to mcrc...")
        try:
            sys.stdin.read(1)
        except:
            pass
    except Exception as e:
        os.system("stty sane")
        print(f"\n\n\033[91mError in rclone execution: {e}\033[0m")
        print("Press Enter to return to mcrc...")
        try:
            sys.stdin.read(1)
        except:
            pass


def run_rclone_deletefile(remote_path: str):
    """Run rclone deletefile command.

    Args:
        remote_path: The remote path to the file (remote:path/to/file)
    """
    import sys
    import subprocess
    import os

    cmd = ["rclone", "deletefile", remote_path, "--progress"]

    # Clear screen for a clean output and ensure sane terminal state
    os.system("stty sane && clear")
    print(f"Executing: {' '.join(cmd)}\n")
    print("!!! WARNING: REAL DELETE MODE - File will be permanently deleted !!!\n")
    print("-" * 60)

    try:
        # Run and allow user to see output
        result = subprocess.run(cmd, check=False)

        # Call stty sane again after rclone completes
        os.system("stty sane")

        print("-" * 60)
        if result.returncode == 0:
            print(
                "\n\033[92mOperation finished successfully.\033[0m Press Enter to return to mcrc..."
            )
        else:
            print(
                f"\n\033[91mOperation failed (exit code {result.returncode}).\033[0m Press Enter to return to mcrc..."
            )

        # Use a more robust input waiting mechanism
        try:
            sys.stdin.read(1)
        except:
            pass

    except KeyboardInterrupt:
        os.system("stty sane")
        print("\n\n\033[91mOperation cancelled by user.\033[0m")
        print("Press Enter to return to mcrc...")
        try:
            sys.stdin.read(1)
        except:
            pass
    except Exception as e:
        os.system("stty sane")
        print(f"\n\n\033[91mError in rclone execution: {e}\033[0m")
        print("Press Enter to return to mcrc...")
        try:
            sys.stdin.read(1)
        except:
            pass


def run_rclone_purge(remote_path: str, dry_run: bool = True):
    """Run rclone purge command.

    Args:
        remote_path: The remote path to purge (remote:path/to/dir)
        dry_run: If True, adds --dry-run flag
    """
    import sys
    import subprocess
    import os

    cmd = ["rclone", "purge", remote_path, "--progress"]
    if dry_run:
        cmd.append("--dry-run")

    # Clear screen for a clean output and ensure sane terminal state
    os.system("stty sane && clear")
    print(f"Executing: {' '.join(cmd)}\n")
    if dry_run:
        print("!!! DRY RUN MODE - No files will be deleted !!!\n")
    else:
        print("!!! WARNING: REAL PURGE MODE - Files will be permanently deleted !!!\n")
    print("-" * 60)

    try:
        # Run and allow user to see output
        result = subprocess.run(cmd, check=False)

        # Call stty sane again after rclone completes
        os.system("stty sane")

        print("-" * 60)
        if result.returncode == 0:
            print(
                "\n\033[92mOperation finished successfully.\033[0m Press Enter to return to mcrc..."
            )
        else:
            print(
                f"\n\033[91mOperation failed (exit code {result.returncode}).\033[0m Press Enter to return to mcrc..."
            )

        # Use a more robust input waiting mechanism
        try:
            sys.stdin.read(1)
        except:
            pass

    except KeyboardInterrupt:
        os.system("stty sane")
        print("\n\n\033[91mOperation cancelled by user.\033[0m")
        print("Press Enter to return to mcrc...")
        try:
            sys.stdin.read(1)
        except:
            pass
    except Exception as e:
        os.system("stty sane")
        print(f"\n\n\033[91mError in rclone execution: {e}\033[0m")
        print("Press Enter to return to mcrc...")
        try:
            sys.stdin.read(1)
        except:
            pass


def run_rclone_sync(src: str, dst: str, dry_run: bool = True):
    """Run rclone sync command.

    Args:
        src: Source path
        dst: Destination path
        dry_run: If True, adds --dry-run flag
    """
    import sys
    import subprocess
    import os

    # Common exclude patterns - use /** for directories to exclude their contents
    exclude_patterns = [
        "--exclude",
        ".git/**",
        "--exclude",
        ".venv/**",
        "--exclude",
        "venv/**",
        "--exclude",
        "env/**",
        "--exclude",
        ".opencode/**",
        "--exclude",
        ".openai_*",
        "--exclude",
        ".*~",
        "--exclude",
        ".python-version",
        "--exclude",
        ".python-cache/**",
        "--exclude",
        ".pytest_cache/**",
    ]

    cmd = ["rclone", "sync", src, dst, "--metadata", "--progress"] + exclude_patterns
    if dry_run:
        cmd.append("--dry-run")

    # Clear screen for a clean output and ensure sane terminal state
    os.system("stty sane && clear")
    print(f"Executing: {' '.join(cmd)}\n")
    if dry_run:
        print("!!! DRY RUN MODE - No files will be deleted or moved !!!\n")
    else:
        print(
            "!!! WARNING: REAL SYNC MODE - Files in destination may be DELETED to match source !!!\n"
        )
    print("-" * 60)

    try:
        # Run and allow user to see output
        result = subprocess.run(cmd, check=False)

        # Call stty sane again after rclone completes
        os.system("stty sane")

        print("-" * 60)
        if result.returncode == 0:
            print(
                "\n\033[92mOperation finished successfully.\033[0m Press Enter to return to mcrc..."
            )
        else:
            print(
                f"\n\033[91mOperation failed (exit code {result.returncode}).\033[0m Press Enter to return to mcrc..."
            )

        # Use a more robust input waiting mechanism
        try:
            sys.stdin.read(1)
        except:
            pass

    except KeyboardInterrupt:
        os.system("stty sane")
        print("\n\n\033[91mOperation cancelled by user.\033[0m")
        print("Press Enter to return to mcrc...")
        try:
            sys.stdin.read(1)
        except:
            pass
    except Exception as e:
        os.system("stty sane")
        print(f"\n\n\033[91mError in rclone execution: {e}\033[0m")
        print("Press Enter to return to mcrc...")
        try:
            sys.stdin.read(1)
        except:
            pass


def run_rclone_mkdir(remote_path: str):
    """Run rclone mkdir command.

    Args:
        remote_path: The remote path to create (remote:path/to/dir)
    """
    import sys
    import subprocess
    import os

    cmd = ["rclone", "mkdir", remote_path]

    # Clear screen for a clean output and ensure sane terminal state
    os.system("stty sane && clear")
    print(f"Executing: {' '.join(cmd)}\n")
    print("-" * 60)

    try:
        # Run and allow user to see output
        result = subprocess.run(cmd, capture_output=True, text=True, check=False)

        # Call stty sane again after rclone completes
        os.system("stty sane")

        print("-" * 60)
        if result.returncode == 0:
            print(
                "\n\033[92mDirectory created successfully.\033[0m Press Enter to return to mcrc..."
            )
        else:
            print(f"\n\033[91mError: {result.stderr}\033[0m")
            print("\n\033[91mOperation failed.\033[0m Press Enter to return to mcrc...")

        # Use a more robust input waiting mechanism
        try:
            sys.stdin.read(1)
        except:
            pass

    except KeyboardInterrupt:
        os.system("stty sane")
        print("\n\n\033[91mOperation cancelled by user.\033[0m")
        print("Press Enter to return to mcrc...")
        try:
            sys.stdin.read(1)
        except:
            pass
    except Exception as e:
        os.system("stty sane")
        print(f"\n\n\033[91mError in rclone execution: {e}\033[0m")
        print("Press Enter to return to mcrc...")
        try:
            sys.stdin.read(1)
        except:
            pass


def run_rclone_check(src: str, dst: str):
    """Run rclone check command.

    Args:
        src: Source path
        dst: Destination path
    """
    import sys
    import subprocess
    import os
    import re
    import json
    from datetime import datetime

    # Common exclude patterns - use /** for directories to exclude their contents
    exclude_patterns = [
        "--exclude",
        ".git/**",
        "--exclude",
        ".venv/**",
        "--exclude",
        "venv/**",
        "--exclude",
        "env/**",
        "--exclude",
        ".opencode/**",
        "--exclude",
        ".openai_*",
        "--exclude",
        ".*~",
        "--exclude",
        ".python-version",
        "--exclude",
        ".python-cache/**",
        "--exclude",
        ".pytest_cache/**",
    ]

    cmd = ["rclone", "check", src, dst, "--size-only", "--progress"] + exclude_patterns

    # Clear screen for a clean output and ensure sane terminal state
    os.system("stty sane && clear")
    print(f"Executing: {' '.join(cmd)}\n")
    print("!!! INFO: Check compares files by size only, not content !!!\n")
    print("-" * 60)

    try:
        # Run and capture output while still displaying it
        result = subprocess.run(cmd, capture_output=True, text=True, check=False)

        # Display the output to user
        if result.stdout:
            print(result.stdout)
        if result.stderr:
            print(result.stderr, file=sys.stderr)

        # Call stty sane again after rclone completes
        os.system("stty sane")

        print("-" * 60)
        if result.returncode == 0:
            print(
                "\n\033[92mCheck completed - no differences found.\033[0m Press Enter to return to mcrc..."
            )
            try:
                sys.stdin.read(1)
            except:
                pass
        else:
            print(
                f"\n\033[93mCheck completed with differences (exit code {result.returncode}).\033[0m Press Enter to see details..."
            )
            try:
                sys.stdin.read(1)
            except:
                pass

            # Parse output for files with size differences
            differing_files = []
            output_text = result.stdout + result.stderr

            # Look for lines like: "2026-02-26 09:39:37 ERROR : filename: sizes differ"
            for line in output_text.split("\n"):
                if "ERROR" in line and "sizes differ" in line:
                    # Extract filename - it's between "ERROR : " and ": sizes differ"
                    match = re.search(r"ERROR\s*:\s*(.+?):\s*sizes differ", line)
                    if match:
                        filename = match.group(1).strip()
                        differing_files.append(filename)

            # Remove duplicates while preserving order
            seen = set()
            differing_files = [
                f for f in differing_files if not (f in seen or seen.add(f))
            ]

            if differing_files:
                print("\n\033[93m" + "=" * 80 + "\033[0m")
                print("\033[93mFILES WITH SIZE DIFFERENCES:\033[0m")
                print("\033[93m" + "=" * 80 + "\033[0m\n")

                for filename in differing_files:
                    print(f"\033[96mFile: {filename}\033[0m")
                    print("-" * 80)

                    # Get source file info
                    src_path = f"{src}{filename}"
                    dst_path = f"{dst}{filename}"

                    # Use rclone lsjson to get file info
                    src_info = None
                    dst_info = None

                    try:
                        src_result = subprocess.run(
                            ["rclone", "lsjson", "--stat", src_path],
                            capture_output=True,
                            text=True,
                            timeout=10,
                        )
                        if src_result.returncode == 0:
                            src_info = json.loads(src_result.stdout)
                    except:
                        pass

                    try:
                        dst_result = subprocess.run(
                            ["rclone", "lsjson", "--stat", dst_path],
                            capture_output=True,
                            text=True,
                            timeout=10,
                        )
                        if dst_result.returncode == 0:
                            dst_info = json.loads(dst_result.stdout)
                    except:
                        pass

                    # Display comparison
                    if src_info:
                        size_src = src_info.get("Size", "N/A")
                        if isinstance(size_src, int):
                            size_src = f"{size_src:,} bytes"
                        mtime_src = src_info.get("ModTime", "N/A")
                        if mtime_src and mtime_src != "N/A":
                            try:
                                # Parse ISO format timestamp
                                dt = datetime.fromisoformat(
                                    mtime_src.replace("Z", "+00:00")
                                )
                                mtime_src = dt.strftime("%Y-%m-%d %H:%M:%S")
                            except:
                                pass
                        print(f"  local:  Size: {size_src:<20}  Modified: {mtime_src}")
                    else:
                        print(
                            f"  local:  \033[91mFile not found or inaccessible\033[0m"
                        )

                    if dst_info:
                        size_dst = dst_info.get("Size", "N/A")
                        if isinstance(size_dst, int):
                            size_dst = f"{size_dst:,} bytes"
                        mtime_dst = dst_info.get("ModTime", "N/A")
                        if mtime_dst and mtime_dst != "N/A":
                            try:
                                dt = datetime.fromisoformat(
                                    mtime_dst.replace("Z", "+00:00")
                                )
                                mtime_dst = dt.strftime("%Y-%m-%d %H:%M:%S")
                            except:
                                pass
                        print(f"  remote: Size: {size_dst:<20}  Modified: {mtime_dst}")
                    else:
                        print(
                            f"  remote: \033[91mFile not found or inaccessible\033[0m"
                        )

                    print()

                print("\033[93m" + "=" * 80 + "\033[0m")
                print("Press Enter to return to mcrc...")
                try:
                    sys.stdin.read(1)
                except:
                    pass

    except KeyboardInterrupt:
        os.system("stty sane")
        print("\n\n\033[91mCheck cancelled by user.\033[0m")
        print("Press Enter to return to mcrc...")
        try:
            sys.stdin.read(1)
        except:
            pass
    except Exception as e:
        os.system("stty sane")
        print(f"\n\n\033[91mError in rclone execution: {e}\033[0m")
        print("Press Enter to return to mcrc...")
        try:
            sys.stdin.read(1)
        except:
            pass


def run_rclone_mount(remote_name: str, mountpoint: str) -> Tuple[bool, str]:
    """Mount a remote rclone storage at a local mountpoint.

    Args:
        remote_name: The rclone remote name (e.g., "mydrive:")
        mountpoint: The local path to mount at (e.g., "/mnt/mydrive")

    Returns:
        Tuple of (success: bool, message: str)
    """
    import os
    import subprocess
    import pwd

    try:
        # Get current user info
        user_info = pwd.getpwuid(os.getuid())
        username = user_info.pw_name
        user_id = user_info.pw_uid
        group_id = user_info.pw_gid

        # Create mountpoint directory if it doesn't exist
        if not os.path.exists(mountpoint):
            print(f"Creating mountpoint: {mountpoint}")
            try:
                os.makedirs(mountpoint, exist_ok=True)
            except Exception as e:
                return False, f"Failed to create mountpoint: {e}"

        # Set sticky bit (chmod +t)
        try:
            current_mode = os.stat(mountpoint).st_mode
            os.chmod(mountpoint, current_mode | 0o1000)  # Add sticky bit
            print(f"Set sticky bit on {mountpoint}")
        except Exception as e:
            return False, f"Failed to set sticky bit: {e}"

        # Change ownership to current user
        try:
            os.chown(mountpoint, user_id, group_id)
            print(f"Changed ownership of {mountpoint} to {username}:{username}")
        except Exception as e:
            return False, f"Failed to change ownership: {e}"

        # Build rclone mount command
        cmd = [
            "rclone",
            "mount",
            remote_name,
            mountpoint,
            "--vfs-cache-mode",
            "writes",
            "--daemon",
        ]

        print(f"\nExecuting: {' '.join(cmd)}\n")
        print("-" * 60)

        # Run mount command
        result = subprocess.run(cmd, capture_output=True, text=True, check=False)

        print("-" * 60)

        if result.returncode == 0:
            return True, f"Remote '{remote_name}' mounted successfully at {mountpoint}"
        else:
            error_msg = result.stderr if result.stderr else result.stdout
            return False, f"Mount failed: {error_msg}"

    except KeyboardInterrupt:
        return False, "Mount cancelled by user"
    except Exception as e:
        return False, f"Error in mount execution: {e}"


def run_rclone_view(
    src_path: str, is_remote: bool, editor: Optional[str] = None
) -> Tuple[bool, str]:
    """View a file using the default editor.

    For remote files, copies to /tmp first then opens.
    Supports .txt, .md, .org, .config, and files named 'config'.

    Args:
        src_path: Path to the file (local or remote)
        is_remote: True if remote file, False if local
        editor: Editor to use (defaults to $EDITOR, vi, or nano)

    Returns:
        Tuple of (success: bool, message: str)
    """
    import os
    import subprocess
    import tempfile
    import shutil

    # Determine editor
    if editor is None:
        editor = os.environ.get("EDITOR", "vi")
        # Fallback chain
        for fallback in ["nano", "vim", "vi"]:
            if shutil.which(editor):
                break
            editor = fallback

    # Check if it's a viewable file
    viewable_extensions = {".txt", ".md", ".org", ".config"}
    basename = os.path.basename(src_path)
    _, ext = os.path.splitext(basename)

    is_viewable = (
        ext.lower() in viewable_extensions
        or basename == "config"
        or ".config" in basename
    )

    if not is_viewable:
        return False, f"File type not supported for viewing: {basename}"

    tmp_file = None
    try:
        if is_remote:
            # Copy remote file to tmp
            fd, tmp_file = tempfile.mkstemp(prefix="mcrc_view_", suffix=ext)
            os.close(fd)

            print(f"Copying remote file to {tmp_file}...")
            copy_cmd = ["rclone", "copyto", src_path, tmp_file]
            result = subprocess.run(
                copy_cmd, capture_output=True, text=True, check=False
            )

            if result.returncode != 0:
                os.unlink(tmp_file)
                return False, f"Failed to copy remote file: {result.stderr}"

            file_to_edit = tmp_file
        else:
            file_to_edit = src_path

        # Open with editor
        print(f"\nOpening with {editor}: {basename}")
        print("-" * 60)

        # Clear screen and reset terminal
        os.system("stty sane && clear")

        edit_cmd = [editor, file_to_edit]
        result = subprocess.run(edit_cmd, check=False)

        print("-" * 60)

        if result.returncode == 0:
            return True, f"Viewed {basename}"
        else:
            return True, f"Editor exited with code {result.returncode}"

    except KeyboardInterrupt:
        return False, "View cancelled by user"
    except Exception as e:
        return False, f"Error viewing file: {e}"
    finally:
        # Cleanup tmp file if it was created
        if tmp_file and os.path.exists(tmp_file):
            try:
                os.unlink(tmp_file)
            except:
                pass


def run_restic_backup(
    remote_name: str,
    local_folder: str,
    backup_name: str,
    password: Optional[str] = None,
) -> Tuple[bool, str]:
    """Run restic backup from local folder to remote repository.

    Changes to the local folder and runs 'restic backup .' to ensure
    relative paths are stored in the backup for easier restoration.

    Args:
        remote_name: The rclone remote name (e.g., "mydrive:")
        local_folder: Local folder path to backup
        backup_name: Name for the backup (will be prefixed with "restic_")
        password: Optional password for repository. If None, uses --insecure-no-password

    Returns:
        Tuple of (success: bool, message: str)
    """
    import os
    import subprocess

    # Ensure backup_name starts with restic_
    if not backup_name.startswith("restic_"):
        backup_name = f"restic_{backup_name}"

    repo_path = f"rclone:{remote_name}{backup_name}"

    # Environment variables for restic
    env = os.environ.copy()
    env["RESTIC_REPOSITORY"] = repo_path

    # Configure password: use provided password or insecure mode
    if password:
        env["RESTIC_PASSWORD"] = password
        insecure_flag = []
    else:
        insecure_flag = ["--insecure-no-password"]

    # Save current directory and change to target folder
    original_dir = os.getcwd()
    try:
        os.chdir(local_folder)
    except Exception as e:
        return False, f"Failed to change to directory {local_folder}: {e}"

    try:
        # Check if repository exists by running snapshots
        print(f"Checking if repository {backup_name} exists...")
        check_cmd = ["restic", "snapshots"] + insecure_flag
        check_result = subprocess.run(
            check_cmd, capture_output=True, text=True, env=env, timeout=30
        )

        if check_result.returncode != 0:
            print(f"Repository does not exist. Initializing {backup_name}...")
            init_cmd = ["restic", "init"] + insecure_flag
            init_result = subprocess.run(
                init_cmd, capture_output=True, text=True, env=env
            )
            if init_result.returncode != 0:
                os.chdir(original_dir)
                return False, f"Failed to initialize repository: {init_result.stderr}"
            print(f"Repository {backup_name} initialized successfully.")

        # Run backup
        print(f"\nStarting backup of {local_folder} to {backup_name}...")
        if password:
            print("(Using password-protected mode)")
        print("-" * 60)

        # Clear screen for clean output
        os.system("stty sane && clear")

        backup_cmd = (
            [
                "restic",
                "backup",
                ".",
                "--verbose",
            ]
            + insecure_flag
            + [
                "--exclude=.venv/*",
                "--exclude=uv.lock",
                "--exclude=.*~",
                "--exclude=.ruff_cache/*",
            ]
        )

        result = subprocess.run(backup_cmd, env=env, check=False)

        print("-" * 60)

        if result.returncode == 0:
            return True, f"Backup completed successfully to {backup_name}"
        else:
            return False, f"Backup failed with exit code {result.returncode}"

    except KeyboardInterrupt:
        return False, "Backup cancelled by user"
    except Exception as e:
        return False, f"Error during backup: {e}"
    finally:
        # Always return to original directory
        os.chdir(original_dir)


def run_restic_restore(
    remote_name: str,
    backup_name: str,
    target_folder: str,
    password: Optional[str] = None,
) -> Tuple[bool, str]:
    """Run restic restore from remote repository to local folder.

    Args:
        remote_name: The rclone remote name (e.g., "mydrive:")
        backup_name: Backup repository name (must start with "restic_")
        target_folder: Local folder to restore to
        password: Optional password for repository. If None, uses --insecure-no-password

    Returns:
        Tuple of (success: bool, message: str)
    """
    import os
    import subprocess

    # Ensure backup_name starts with restic_
    if not backup_name.startswith("restic_"):
        return False, "Invalid backup name - must start with 'restic_'"

    repo_path = f"rclone:{remote_name}{backup_name}"

    # Environment variables for restic
    env = os.environ.copy()
    env["RESTIC_REPOSITORY"] = repo_path

    # Configure password: use provided password or insecure mode
    if password:
        env["RESTIC_PASSWORD"] = password
        insecure_flag = []
    else:
        insecure_flag = ["--insecure-no-password"]

    # Clear screen for clean output
    os.system("stty sane && clear")

    print(f"Restoring from {backup_name} to {target_folder}...")
    if password:
        print("(Using password-protected mode)")
    print("-" * 60)

    restore_cmd = [
        "restic",
        "restore",
        "latest",
        "--target",
        target_folder,
    ] + insecure_flag

    try:
        result = subprocess.run(restore_cmd, env=env, check=False)

        print("-" * 60)

        if result.returncode == 0:
            return True, f"Restore completed successfully to {target_folder}"
        else:
            return False, f"Restore failed with exit code {result.returncode}"

    except KeyboardInterrupt:
        return False, "Restore cancelled by user"
    except Exception as e:
        return False, f"Error during restore: {e}"


def run_restic_snapshots(
    remote_name: str, backup_name: str, password: Optional[str] = None
) -> Tuple[bool, str]:
    """Run restic snapshots command and return the output.

    Args:
        remote_name: The rclone remote name (e.g., "mydrive:")
        backup_name: Backup repository name (must start with "restic_")
        password: Optional password for repository. If None, uses --insecure-no-password

    Returns:
        Tuple of (success: bool, output: str)
    """
    import os
    import subprocess

    # Ensure backup_name starts with restic_
    if not backup_name.startswith("restic_"):
        return False, "Invalid backup name - must start with 'restic_'"

    repo_path = f"rclone:{remote_name}{backup_name}"

    # Environment variables for restic
    env = os.environ.copy()
    env["RESTIC_REPOSITORY"] = repo_path

    # Configure password: use provided password or insecure mode
    if password:
        env["RESTIC_PASSWORD"] = password
        insecure_flag = []
    else:
        insecure_flag = ["--insecure-no-password"]

    # Run snapshots command
    snapshots_cmd = ["restic", "snapshots"] + insecure_flag

    try:
        result = subprocess.run(
            snapshots_cmd, capture_output=True, text=True, env=env, timeout=60
        )

        if result.returncode == 0:
            return True, result.stdout
        else:
            return False, f"Error: {result.stderr}"

    except subprocess.TimeoutExpired:
        return False, "Timeout: Command took too long to execute"
    except Exception as e:
        return False, f"Error running snapshots: {e}"
