import argparse
import os
import sys
import tempfile
from pytest_cream.fetch import fetch_tests
from pytest_cream.run import run_tests
from pytest_cream.filter import filter_tests
from pytest_cream import workflow


def main():
    parser = argparse.ArgumentParser(
        description="pytest-cream: The cream of test execution - smooth pytest workflows with intelligent orchestration",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Basic usage - initialize (fetch + install)
  pytest-cream init --repo owner/project --branch main --ws ~/workspace

  # With custom installation command
  pytest-cream init --repo owner/project \\
    --install-cmd "uv sync --extra cpu" \\
    --ws ~/workspace

  # Manual workflow for more control
  pytest-cream init --repo owner/project --ws ~/workspace
  pytest-cream profile --ws ~/workspace/owner_project_main --python python3.11
  pytest-cream run --ws ~/workspace/owner_project_main --python python3.11

For more examples and troubleshooting, see the documentation.
        """
    )
    subparsers = parser.add_subparsers(dest="command")

    # Fetch command
    fetch_parser = subparsers.add_parser(
        "fetch", 
        help="Fetch tests from a repository",
        description="Clone a repository branch to a workspace directory for testing."
    )
    fetch_parser.add_argument(
        "--repo",
        required=True,
        help="Repository to fetch from in format 'owner/repo' or full GitHub URL"
    )
    fetch_parser.add_argument(
        "--branch", 
        default="main",
        help="Git branch to checkout and fetch tests from. Default: main"
    )
    fetch_parser.add_argument(
        "--ws",
        "--workspace",
        dest="workspace",
        required=True,
        help="Directory path where the repository will be cloned. Use the same workspace across fetch/run/filter commands."
    )

    # Profile command (was 'run')
    profile_parser = subparsers.add_parser(
        "profile", 
        help="Profile tests and collect duration statistics",
        description="Execute tests from a workspace directory and generate duration logs for analysis."
    )
    profile_parser.add_argument(
        "--ws",
        "--workspace",
        dest="workspace",
        required=True,
        help="Workspace directory containing the fetched tests. Use the same workspace from fetch command."
    )
    profile_parser.add_argument(
        "--output", 
        default="test_durations.log", 
        help="Output file name for test durations (saved in workspace). Default: test_durations.log"
    )
    profile_parser.add_argument(
        "--tests-dir", 
        default="tests", 
        help="Subdirectory within workspace containing test files. Default: tests"
    )
    profile_parser.add_argument(
        "--python",
        dest="python_executable",
        default=None,
        help="Python executable to use (e.g., 'python3.11'). If not specified, uses current Python."
    )

    # Filter command
    filter_parser = subparsers.add_parser(
        "filter", 
        help="Filter tests by duration threshold",
        description="Parse test duration logs and filter tests into long/short categories based on execution time."
    )
    filter_parser.add_argument(
        "--ws",
        "--workspace",
        dest="workspace",
        required=True,
        help="Workspace directory containing the test durations log. Use the same workspace from fetch/run commands."
    )
    filter_parser.add_argument(
        "--input", 
        default="test_durations.log",
        help="Input file name with test durations (relative to workspace). Default: test_durations.log"
    )
    filter_parser.add_argument(
        "--output", 
        default="filtered_tests.txt",
        help="Output file name for filtered test results (saved in workspace). Default: filtered_tests.txt"
    )
    filter_parser.add_argument(
        "--threshold", 
        type=float, 
        required=True, 
        help="Duration threshold in seconds. Tests >= threshold are 'long', tests < threshold are 'short'."
    )

    # Run command (orchestration - was 'execute')
    run_parser = subparsers.add_parser(
        "run",
        help="Run tests with intelligent orchestration based on duration analysis",
        description="Execute tests with intelligent orchestration: long tests sequentially, short tests in parallel."
    )
    run_parser.add_argument(
        "--ws",
        "--workspace",
        dest="workspace",
        required=True,
        help="Workspace directory containing the tests and duration logs."
    )
    run_parser.add_argument(
        "--durations",
        default="test_durations.log",
        help="Duration log file (relative to workspace). Default: test_durations.log"
    )
    run_parser.add_argument(
        "--threshold",
        type=float,
        default=0.5,
        help="Duration threshold in seconds for splitting long/short tests. Default: 0.5"
    )
    run_parser.add_argument(
        "--workers",
        type=int,
        default=4,
        help="Number of parallel workers for short tests. Default: 4"
    )
    run_parser.add_argument(
        "--python",
        dest="python_executable",
        default=None,
        help="Python executable to use (e.g., 'python3.11')."
    )
    run_parser.add_argument(
        "--limit",
        type=int,
        default=None,
        help="Maximum number of tests to run."
    )
    run_parser.add_argument(
        "--exclude-tests",
        dest="exclude_tests",
        default=None,
        help="Comma-separated list of test patterns to exclude."
    )
    run_parser.add_argument(
        "--short-only",
        dest="short_only",
        action="store_true",
        help="Run only short tests (skip long tests)."
    )

    # Init command (setup: fetch + install)
    init_parser = subparsers.add_parser(
        "init",
        help="Initialize: fetch tests and install dependencies from a repository",
        description="Setup workflow: fetch tests from repository and install dependencies. Does not profile or run tests.",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Basic usage (uses uv by default)
  pytest-cream init --repo owner/project --ws ~/workspace

  # With specific branch
  pytest-cream init --repo owner/project --branch develop --ws ~/workspace

  # Use pip instead of uv (for compatibility)
  pytest-cream init --repo owner/project --use-pip --ws ~/workspace

  # Project with custom dependencies using uv
  pytest-cream init --repo owner/project \\
    --install-cmd "uv sync --extra cpu" \\
    --ws ~/workspace

  # Development workflow with editable install
  pytest-cream init --repo owner/project \\
    --install-mode editable --ws ~/workspace
        """
    )
    init_parser.add_argument(
        "--repo",
        default="owner/project",
        help="Repository to fetch in format 'owner/repo' or full GitHub URL.",
    )
    init_parser.add_argument(
        "--branch", 
        default="main", 
        help="Git branch to checkout. Default: main"
    )
    init_parser.add_argument(
        "--ws",
        "--workspace",
        dest="workspace",
        default=None,
        help="Directory path where the repository will be cloned. Required.",
    )
    init_parser.add_argument(
        "--install-repo",
        dest="install_repo",
        default=None,
        help="Optional repository to install dependencies from (format: 'owner/repo'). Useful when tests need a specific version of a package from a different repo.",
    )
    init_parser.add_argument(
        "--install-branch",
        dest="install_branch",
        default=None,
        help="Git branch to use when installing from --install-repo. If not specified, uses the same branch as --branch.",
    )
    init_parser.add_argument(
        "--install-mode",
        dest="install_mode",
        default="pip",
        choices=["pip", "editable", "wheel"],
        help="Installation method: 'pip' (direct install), 'editable' (pip install -e, for development), 'wheel' (build wheel first). Default: pip.",
    )
    init_parser.add_argument(
        "--use-pip",
        dest="use_pip",
        action="store_true",
        help="Use pip instead of uv package manager. By default, uv is used for faster and more reliable installations.",
    )
    init_parser.add_argument(
        "--install-cmd",
        dest="install_cmd",
        default=None,
        help="Custom shell command for dependency installation (e.g., 'uv sync --extra cpu', 'poetry install'). Overrides --install-mode.",
    )
    init_parser.add_argument(
        "--install-fallback",
        dest="install_fallback",
        action="store_true",
        help="If --install-cmd fails, fallback to standard pip installation instead of aborting.",
    )
    init_parser.add_argument(
        "--python",
        dest="python_executable",
        default=None,
        help="Python executable to use (e.g., 'python3.11'). Useful for install verification.",
    )

    args = parser.parse_args()

    if args.command == "fetch":
        # Validate and create workspace
        os.makedirs(args.workspace, exist_ok=True)
        output_dir = os.path.join(args.workspace, f"{args.repo.replace('/', '_')}_{args.branch}")
        extracted_path = fetch_tests(branch=args.branch, repo=args.repo, output_dir=output_dir)
        print(f"Tests fetched to: {extracted_path}")
        
    elif args.command == "profile":
        # Validate workspace exists
        if not os.path.exists(args.workspace):
            print(f"Error: Workspace '{args.workspace}' does not exist. Run fetch command first.", file=sys.stderr)
            sys.exit(1)
        
        # Resolve paths relative to workspace
        tests_path = os.path.join(args.workspace, args.tests_dir)
        output_path = os.path.join(args.workspace, args.output)
        
        run_tests(
            output_file=output_path, 
            tests_dir=tests_path,
            python_executable=args.python_executable
        )
        print(f"Test durations saved to: {output_path}")
        
    elif args.command == "filter":
        # Validate workspace exists
        if not os.path.exists(args.workspace):
            print(f"Error: Workspace '{args.workspace}' does not exist. Run fetch/run commands first.", file=sys.stderr)
            sys.exit(1)
        
        # Resolve paths relative to workspace
        input_path = os.path.join(args.workspace, args.input)
        output_path = os.path.join(args.workspace, args.output)
        
        if not os.path.exists(input_path):
            print(f"Error: Input file '{input_path}' does not exist. Run the run command first.", file=sys.stderr)
            sys.exit(1)
        
        filter_tests(
            input_file=input_path, 
            output_file=output_path, 
            threshold=args.threshold
        )
        print(f"Filtered tests saved to: {output_path}")
        
    elif args.command == "run":
        # Validate workspace exists
        if not os.path.exists(args.workspace):
            print(f"Error: Workspace '{args.workspace}' does not exist.", file=sys.stderr)
            sys.exit(1)
        
        # Resolve durations file path
        durations_path = os.path.join(args.workspace, args.durations)
        if not os.path.exists(durations_path):
            print(f"Error: Durations file '{durations_path}' does not exist. Run profile command first.", file=sys.stderr)
            sys.exit(1)
        
        # Parse exclude patterns if provided
        exclude_tests = []
        if args.exclude_tests:
            exclude_tests = [pattern.strip() for pattern in args.exclude_tests.split(",") if pattern.strip()]
        
        # Execute tests with orchestration
        from pytest_cream.workflow import _parse_durations, _split_tests, _filter_excluded_tests, _run_tests_orchestrated
        
        parsed = _parse_durations(durations_path)
        long_tests, short_tests = _split_tests(parsed, args.threshold)
        
        # Filter excluded tests
        if exclude_tests:
            long_tests = _filter_excluded_tests(long_tests, exclude_tests)
            short_tests = _filter_excluded_tests(short_tests, exclude_tests)
        
        # Apply test limit if specified
        if args.limit is not None and args.limit > 0:
            total_tests = len(long_tests) + len(short_tests)
            if total_tests > args.limit:
                print(f"Limiting tests from {total_tests} to {args.limit}")
                if len(long_tests) >= args.limit:
                    long_tests = long_tests[:args.limit]
                    short_tests = []
                else:
                    remaining = args.limit - len(long_tests)
                    short_tests = short_tests[:remaining]
        
        # Apply short-only filter if specified
        if args.short_only:
            print(f"Running only short tests (skipping {len(long_tests)} long tests)")
            long_tests = []
        
        print(f"Found {len(long_tests)} long tests and {len(short_tests)} short tests (threshold={args.threshold})")
        
        # Run tests with orchestration
        _run_tests_orchestrated(
            workspace=args.workspace,
            long_tests=long_tests,
            short_tests=short_tests,
            workers=args.workers,
            python_executable=args.python_executable,
            exclude_tests=exclude_tests
        )
        
    elif args.command == "init":
        # If workspace is provided, validate it upfront and fail fast if it's not usable.
        if args.workspace:
            try:
                # Try to create the workspace directory if missing
                os.makedirs(args.workspace, exist_ok=True)
                # Try creating and removing a temporary file to verify writability
                fd, tmpfile = tempfile.mkstemp(dir=args.workspace)
                os.close(fd)
                os.remove(tmpfile)
            except Exception as e:
                print(
                    f"Error: cannot use workspace '{args.workspace}': {e}",
                    file=sys.stderr,
                )
                sys.exit(2)

        workflow.init_only(
            repo_url=args.repo,
            branch=args.branch,
            workspace=args.workspace,
            install_repo=args.install_repo,
            install_branch=args.install_branch,
            install_mode=args.install_mode,
            install_cmd=args.install_cmd,
            install_fallback=args.install_fallback,
            install_uv=not args.use_pip,  # Default to uv unless --use-pip is specified
        )
        
    else:
        parser.print_help()
