import argparse
import os
from pathlib import Path

import yaml
from dotenv import load_dotenv
from huggingface_hub import HfApi
from pathspec import PathSpec
from pathspec.patterns import GitWildMatchPattern  # type: ignore


def parse_readme_secrets(readme_path: Path) -> list[str]:
    """Extract secret names from README.md tags field."""
    if not readme_path.exists():
        return []

    content = readme_path.read_text()
    try:
        # Parse the YAML frontmatter
        # Find the content between first two '---' lines
        yaml_content = content.split("---")[1]
        metadata = yaml.safe_load(yaml_content)

        # Extract secrets from tags
        secrets = []
        if "tags" in metadata:
            for tag in metadata["tags"]:
                if "|" in tag:
                    prefix, secret_name = tag.split("|")
                    if prefix == "secret":
                        secrets.append(secret_name)
        return secrets
    except Exception as e:
        print(f"Warning: Could not parse README.md: {e}")
        return []


def upload_space(dir_path: str):
    NO_GRADIO_SPACE = [
        "webrtc_vs_websocket",
        "llama_code_editor",
        "phonic_chat",
        "gemini_audio_video",
        "moonshine_live",
    ]

    path: Path = Path(dir_path)
    if not path.exists():
        raise ValueError(f"Path {path} does not exist")

    # Get space name from last directory name
    space_name = path.name.replace("_", "-")

    # Load environment variables
    env_file = path.parent / ".env"
    if env_file.exists():
        load_dotenv(env_file)
    else:
        print("Warning: No .env file found")

    # Initialize Hugging Face client
    api = HfApi()

    # Create full repo ID
    repo_id = f"fastrtc/{space_name}"
    print(f"Creating/updating space: {repo_id}")

    # Create the space if it doesn't exist
    try:
        api.create_repo(
            repo_id=repo_id, repo_type="space", space_sdk="gradio", exist_ok=True
        )
        if path.name not in NO_GRADIO_SPACE:
            api.create_repo(
                repo_id=f"{repo_id}-gradio",
                repo_type="space",
                space_sdk="gradio",
                exist_ok=True,
            )
    except Exception as e:
        print(f"Error creating repo: {e}")
        return

    # Upload all files from the directory
    print("Uploading files...")
    api.upload_folder(
        repo_id=repo_id,
        repo_type="space",
        folder_path=str(path),
        ignore_patterns=["__pycache__", "*.pyc", ".env", "README_gradio.md"],
        create_pr=False,
        path_in_repo="",
    )
    api.restart_space(repo_id=repo_id, factory_reboot=True)

    readme_path = path / "README.md"

    if path.name not in NO_GRADIO_SPACE and (path / "README_gradio.md").exists():
        try:
            # Upload Gradio version with modified README
            api.upload_folder(
                repo_id=f"{repo_id}-gradio",
                repo_type="space",
                folder_path=str(path),
                ignore_patterns=["__pycache__", "*.pyc", ".env", "README.md"],
                create_pr=False,
                path_in_repo="",
            )
            api.upload_file(
                repo_id=f"{repo_id}-gradio",
                repo_type="space",
                path_or_fileobj=str(path / "README_gradio.md"),
                path_in_repo="README.md",
            )

            api.add_space_secret(
                repo_id=f"{repo_id}-gradio",
                key="MODE",
                value="UI",
            )

            api.add_space_secret(
                repo_id=f"{repo_id}-gradio",
                key="MODE",
                value="UI",
            )
            api.restart_space(repo_id=f"{repo_id}-gradio", factory_reboot=True)
        except Exception as e:
            print(f"Error uploading Gradio version: {e}")

    secret_names = parse_readme_secrets(readme_path)

    if secret_names:
        print("Setting up secrets...")
        for secret_name in secret_names:
            secret_value = os.getenv(secret_name)
            if secret_value:
                try:
                    api.add_space_secret(
                        repo_id=repo_id, key=secret_name, value=secret_value
                    )
                    if path.name not in NO_GRADIO_SPACE:
                        api.add_space_secret(
                            repo_id=f"{repo_id}-gradio",
                            key=secret_name,
                            value=secret_value,
                        )
                    print(f"Added secret: {secret_name}")
                except Exception as e:
                    print(f"Error adding secret {secret_name}: {e}")
            else:
                print(
                    f"Warning: Secret {secret_name} not found in environment variables"
                )

    print(
        f"\nSpace uploaded successfully! View it at: https://huggingface.co/spaces/{repo_id}"
    )


def is_gitignored(path: Path) -> bool:
    """Check if a path is gitignored by looking for .gitignore files in parent directories."""
    gitignore_patterns = []

    # Walk up the directory tree looking for .gitignore files
    gitignore_file = Path(__file__).parent / ".gitignore"
    if gitignore_file.exists():
        patterns = gitignore_file.read_text().splitlines()
        gitignore_patterns.extend(patterns)

    if not gitignore_patterns:
        return False

    # Create a PathSpec object with all found patterns
    spec = PathSpec.from_lines(GitWildMatchPattern, gitignore_patterns)
    # Check if the path matches any gitignore pattern
    return (
        spec.match_file(str(path.relative_to("demo")))
        or spec.match_file(str(path.relative_to("demo")) + "/")
        or spec.match_file(str(path.relative_to(".")))
        or spec.match_file(str(path.relative_to(".")) + "/")
    )


def main():
    parser = argparse.ArgumentParser(
        description="Upload a directory as a Hugging Face Space"
    )
    parser.add_argument("path", help="Path to the directory to upload")
    parser.add_argument(
        "--all",
        action="store_true",
        help="Upload all immediate child directories as separate spaces",
    )

    args = parser.parse_args()
    path = Path(args.path)

    if args.all:
        if not path.is_dir():
            raise ValueError(f"Path {path} must be a directory when using --all")

        # Process each immediate child directory
        for child_dir in path.iterdir():
            if child_dir.is_dir():
                if is_gitignored(child_dir):
                    print(f"Skipping gitignored directory: {child_dir}")
                    continue
                print(f"\nProcessing directory: {child_dir}")
                try:
                    upload_space(str(child_dir))
                except Exception as e:
                    print(f"Error processing {child_dir}: {e}")
    else:
        if is_gitignored(path):
            print(f"Cannot upload gitignored directory: {path}")
            return
        upload_space(args.path)


if __name__ == "__main__":
    main()
