"""
This type stub file was generated by pyright.
"""

import plotly.graph_objects as go
import rich.repr
from collections.abc import Generator
from contextlib import contextmanager
from pathlib import Path
from typing import Generic, Literal, TYPE_CHECKING, TypeVar, Unpack, overload
from git import Commit as GitCommit, Diff
from git.remote import PushInfoList
from networkx import Graph
from rich.console import Console
from typing_extensions import deprecated
from codegen.git import RepoOperator
from codegen.git.schemas.enums import CheckoutResult
from graph_sitter._proxy import proxy_property
from graph_sitter.ai.helpers import AbstractAIHelper
from graph_sitter.codebase.config import CodebaseConfig, ProjectConfig, SessionOptions
from graph_sitter.codebase.flagging.code_flag import CodeFlag
from graph_sitter.codebase.flagging.enums import FlagKwargs
from graph_sitter.codebase.span import Span
from graph_sitter.core.assignment import Assignment
from graph_sitter.core.class_definition import Class
from graph_sitter.core.detached_symbols.code_block import CodeBlock
from graph_sitter.core.detached_symbols.parameter import Parameter
from graph_sitter.core.directory import Directory
from graph_sitter.core.external_module import ExternalModule
from graph_sitter.core.file import File, SourceFile
from graph_sitter.core.function import Function
from graph_sitter.core.import_resolution import Import
from graph_sitter.core.interface import Interface
from graph_sitter.core.interfaces.editable import Editable
from graph_sitter.core.symbol import Symbol
from graph_sitter.core.type_alias import TypeAlias
from graph_sitter.perf import stopwatch
from graph_sitter.python.assignment import PyAssignment
from graph_sitter.python.class_definition import PyClass
from graph_sitter.python.detached_symbols.code_block import PyCodeBlock
from graph_sitter.python.detached_symbols.parameter import PyParameter
from graph_sitter.python.file import PyFile
from graph_sitter.python.function import PyFunction
from graph_sitter.python.import_resolution import PyImport
from graph_sitter.python.symbol import PySymbol
from graph_sitter.typescript.assignment import TSAssignment
from graph_sitter.typescript.class_definition import TSClass
from graph_sitter.typescript.detached_symbols.code_block import TSCodeBlock
from graph_sitter.typescript.detached_symbols.parameter import TSParameter
from graph_sitter.typescript.file import TSFile
from graph_sitter.typescript.function import TSFunction
from graph_sitter.typescript.import_resolution import TSImport
from graph_sitter.typescript.interface import TSInterface
from graph_sitter.typescript.symbol import TSSymbol
from graph_sitter.typescript.type_alias import TSTypeAlias
from graph_sitter.writer_decorators import apidoc, noapidoc
from graph_visualization.visualization_manager import VisualizationManager
from app.codemod.types import Group

"""Codebase - main interface for Codemods to interact with the codebase"""
if TYPE_CHECKING:
    ...
logger = ...
MAX_LINES = ...
TSourceFile = TypeVar("TSourceFile", bound="SourceFile")
TDirectory = TypeVar("TDirectory", bound="Directory")
TSymbol = TypeVar("TSymbol", bound="Symbol")
TClass = TypeVar("TClass", bound="Class")
TFunction = TypeVar("TFunction", bound="Function")
TImport = TypeVar("TImport", bound="Import")
TGlobalVar = TypeVar("TGlobalVar", bound="Assignment")
TInterface = TypeVar("TInterface", bound="Interface")
TTypeAlias = TypeVar("TTypeAlias", bound="TypeAlias")
TParameter = TypeVar("TParameter", bound="Parameter")
TCodeBlock = TypeVar("TCodeBlock", bound="CodeBlock")
@apidoc
class Codebase(Generic[TSourceFile, TDirectory, TSymbol, TClass, TFunction, TImport, TGlobalVar, TInterface, TTypeAlias, TParameter, TCodeBlock]):
    """Main interface for codemods to interact with codebases, including utility methods etc.."""
    _op: RepoOperator
    viz: VisualizationManager
    repo_path: Path
    console: Console
    @overload
    def __init__(self, repo_path: None = ..., *, projects: list[ProjectConfig], config: CodebaseConfig = ...) -> None:
        ...
    
    @overload
    def __init__(self, repo_path: str, *, projects: None = ..., config: CodebaseConfig = ...) -> None:
        ...
    
    def __init__(self, repo_path: str | None = ..., *, projects: list[ProjectConfig] | None = ..., config: CodebaseConfig = ...) -> None:
        ...
    
    @noapidoc
    def __str__(self) -> str:
        ...
    
    def __rich_repr__(self) -> rich.repr.Result:
        ...
    
    @property
    @deprecated("Please do not use the local repo operator directly")
    @noapidoc
    def op(self) -> RepoOperator:
        ...
    
    @overload
    def files(self, *, extensions: list[str]) -> list[File]:
        ...
    
    @overload
    def files(self, *, extensions: Literal["*"]) -> list[File]:
        ...
    
    @overload
    def files(self, *, extensions: None = ...) -> list[TSourceFile]:
        ...
    
    @proxy_property
    def files(self, *, extensions: list[str] | Literal["*"] | None = ...) -> list[TSourceFile] | list[File]:
        """A list property that returns all files in the codebase.

        By default, this only returns source files. Setting `extensions='*'` will return all files in the codebase, and
        `extensions=[...]` will return all files with the specified extensions.

        `extensions='*'` is REQUIRED for listing all non source code files. Or else, codebase.files will ONLY return source files (e.g. .py, .ts).

        Returns all Files in the codebase, sorted alphabetically. For Python codebases, returns PyFiles (python files).
        For Typescript codebases, returns TSFiles (typescript files).

        Returns:
            list[TSourceFile]: A sorted list of source files in the codebase.
        """
        ...
    
    @property
    def directories(self) -> list[TDirectory]:
        """List all directories in the codebase.

        Returns a list of all Directory objects present in the codebase. Each Directory object represents a directory in the codebase.
        This property is used to access and navigate the directory structure of the codebase.

        Returns:
            list[TDirectory]: A list of Directory objects in the codebase.
        """
        ...
    
    @property
    def imports(self) -> list[TImport]:
        """Returns a list of all Import nodes in the codebase.

        Retrieves all Import nodes from the codebase graph. These imports represent all import statements across all files in the codebase,
        including imports from both internal modules and external packages.

        Args:
            None

        Returns:
            list[TImport]: A list of Import nodes representing all imports in the codebase.
            TImport can be PyImport for Python codebases or TSImport for TypeScript codebases.
        """
        ...
    
    @property
    def external_modules(self) -> list[ExternalModule]:
        """Returns a list of all external modules in the codebase.

        An external module represents a dependency that is imported but not defined within the codebase itself (e.g. third-party packages like 'requests' or 'numpy').

        Returns:
            list[ExternalModule]: List of external module nodes from the codebase graph.
        """
        ...
    
    @property
    def symbols(self) -> list[TSymbol]:
        """List of all top-level Symbols (Classes, Functions, etc.) in the codebase. Excludes Class
        methods.

        Returns:
            list[TSymbol]: A list of Symbol objects of all top-level symbols in the codebase. Includes classes, functions, and global variables defined at the module level, excludes methods.
        """
        ...
    
    @property
    def classes(self) -> list[TClass]:
        """List of all Classes in the codebase.

        Returns a sorted list of all Class nodes in the codebase. Class nodes represent class definitions in source files.
        Only includes top-level classes, not inner/nested classes.

        Returns:
            list[TClass]: A sorted list of all Class nodes in the codebase.
        """
        ...
    
    @property
    def functions(self) -> list[TFunction]:
        """List of all Functions in the codebase.

        Returns a sorted list of all top-level Function objects in the codebase, excluding class methods.

        Returns:
            list[TFunction]: A list of Function objects representing all functions in the codebase, sorted alphabetically.
        """
        ...
    
    @property
    def global_vars(self) -> list[TGlobalVar]:
        """List of all GlobalVars in the codebase.

        A GlobalVar represents a global variable assignment in the source code. These are variables defined at the module level.

        Returns:
            list[TGlobalVar]: A list of all global variable assignments in the codebase.
        """
        ...
    
    @property
    def interfaces(self) -> list[TInterface]:
        """Retrieves all interfaces in the codebase.

        Returns a list of all Interface symbols defined at the top-level of source files in the codebase.
        This property is only applicable for TypeScript codebases and will return an empty list for Python codebases.

        Returns:
            list[TInterface]: A list of Interface objects defined in the codebase's source files.
        """
        ...
    
    @property
    def types(self) -> list[TTypeAlias]:
        """List of all Types in the codebase (Typescript only).

        Returns a list of all type aliases defined at the top level in the codebase. This method is only applicable
        for TypeScript codebases.

        Returns:
            list[TTypeAlias]: A list of all type aliases defined in the codebase.
        """
        ...
    
    def create_file(self, filepath: str, content: str = ..., sync: bool = ...) -> File:
        """Creates a new file in the codebase with specified content.

        Args:
            filepath (str): The path where the file should be created.
            content (str): The content of the file to be created. Defaults to empty string.
            sync (bool): Whether to sync the graph after creating the file. Defaults to True.

        Returns:
            File: The newly created file object.

        Raises:
            ValueError: If the provided content cannot be parsed according to the file extension.
        """
        ...
    
    def create_directory(self, dir_path: str, exist_ok: bool = ..., parents: bool = ...) -> None:
        """Creates a directory at the specified path.

        Args:
            dir_path (str): The path where the directory should be created.
            exist_ok (bool): If True, don't raise an error if the directory already exists. Defaults to False.
            parents (bool): If True, create any necessary parent directories. Defaults to False.

        Raises:
            FileExistsError: If the directory already exists and exist_ok is False.
        """
        ...
    
    def has_file(self, filepath: str, ignore_case: bool = ...) -> bool:
        """Determines if a file exists in the codebase.

        Args:
            filepath (str): The filepath to check for existence.
            ignore_case (bool): If True, ignore case when checking for file existence. Defaults to False.

        Returns:
            bool: True if the file exists in the codebase, False otherwise.
        """
        ...
    
    @overload
    def get_file(self, filepath: str, *, optional: Literal[False] = ..., ignore_case: bool = ...) -> TSourceFile:
        ...
    
    @overload
    def get_file(self, filepath: str, *, optional: Literal[True], ignore_case: bool = ...) -> TSourceFile | None:
        ...
    
    def get_file(self, filepath: str, *, optional: bool = ..., ignore_case: bool = ...) -> TSourceFile | None:
        """Retrieves a file from the codebase by its filepath.

        This method first attempts to find the file in the graph, then checks the filesystem if not found. Files can be either source files (e.g. .py, .ts) or binary files.

        Args:
            filepath (str): The path to the file, relative to the codebase root.
            optional (bool): If True, return None if file not found. If False, raise ValueError.
            ignore_case (bool): If True, ignore case when checking for file existence. Defaults to False.

        Returns:
            TSourceFile | None: The source file if found, None if optional=True and file not found.

        Raises:
            ValueError: If file not found and optional=False.
        """
        ...
    
    def has_directory(self, dir_path: str, ignore_case: bool = ...) -> bool:
        """Returns a boolean indicating if a directory exists in the codebase.

        Checks if a directory exists at the specified path within the codebase.

        Args:
            dir_path (str): The path to the directory to check for, relative to the codebase root.

        Returns:
            bool: True if the directory exists in the codebase, False otherwise.
        """
        ...
    
    def get_directory(self, dir_path: str, optional: bool = ..., ignore_case: bool = ...) -> TDirectory | None:
        """Returns Directory by `dir_path`, or full path to the directory from codebase root.

        Args:
            dir_path (str): The path to the directory to retrieve.
            optional (bool): If True, return None when directory is not found. If False, raise ValueError.

        Returns:
            TDirectory | None: The Directory object if found, None if optional=True and directory not found.

        Raises:
            ValueError: If directory not found and optional=False.
        """
        ...
    
    def has_symbol(self, symbol_name: str) -> bool:
        """Returns whether a symbol exists in the codebase.

        This method checks if a symbol with the given name exists in the codebase.

        Args:
            symbol_name (str): The name of the symbol to look for.

        Returns:
            bool: True if a symbol with the given name exists in the codebase, False otherwise.
        """
        ...
    
    def get_symbol(self, symbol_name: str, optional: bool = ...) -> TSymbol | None:
        """Returns a Symbol by name from the codebase.

        Returns the first Symbol that matches the given name. If multiple symbols are found with the same name, raises a ValueError.
        If no symbol is found, returns None if optional is True, otherwise raises a ValueError.

        Args:
            symbol_name (str): The name of the symbol to find.
            optional (bool): If True, returns None when symbol is not found. If False, raises ValueError. Defaults to False.

        Returns:
            TSymbol | None: The matched Symbol if found, None if not found and optional=True.

        Raises:
            ValueError: If multiple symbols are found with the same name, or if no symbol is found and optional=False.
        """
        ...
    
    def get_symbols(self, symbol_name: str) -> list[TSymbol]:
        """Retrieves all symbols in the codebase that match the given symbol name.

        This method is used when there may be multiple symbols with the same name, in which case get_symbol() would raise a ValueError.

        Args:
            symbol_name (str): The name of the symbols to retrieve.

        Returns:
            list[TSymbol]: A list of Symbol objects that match the given name, sorted alphabetically.

        Note:
            When a unique symbol is required, use get_symbol() instead. It will raise ValueError if multiple symbols are found.
        """
        ...
    
    def get_class(self, class_name: str, optional: bool = ...) -> TClass | None:
        """Returns a class that matches the given name.

        Args:
            class_name (str): The name of the class to find.
            optional (bool): If True, return None when class is not found instead of raising ValueError. Defaults to False.

        Returns:
            TClass | None: The class with the given name, or None if optional=True and class not found.

        Raises:
            ValueError: If the class is not found and optional=False, or if multiple classes with the same name exist.
        """
        ...
    
    def get_function(self, function_name: str, optional: bool = ...) -> TFunction | None:
        """Retrieves a function from the codebase by its name.

        This method searches through all functions in the codebase to find one matching the given name.
        If multiple functions with the same name exist, a ValueError is raised.

        Args:
            function_name (str): The name of the function to retrieve.
            optional (bool): If True, returns None when function is not found instead of raising ValueError.
                            Defaults to False.

        Returns:
            TFunction | None: The matching function if found. If optional=True and no match is found,
                             returns None.

        Raises:
            ValueError: If function is not found and optional=False, or if multiple matching functions exist.
        """
        ...
    
    def get_relative_path(self, from_file: str, to_file: str) -> str:
        """Calculates a relative path from one file to another, removing the extension from the target file.

        This method splits both `from_file` and `to_file` by forward slashes, finds their common path prefix,
        and determines how many directories to traverse upward from `from_file` before moving into the
        remaining directories of `to_file` (with its extension removed).

        Args:
            from_file (str): The file path from which the relative path will be computed.
            to_file (str): The file path (whose extension will be removed) to which the relative path will be computed.

        Returns:
            str: The relative path from `from_file` to `to_file` (with the extension removed from `to_file`).
        """
        ...
    
    def git_commit(self, message: str, *, verify: bool = ...) -> GitCommit | None:
        """Commits all staged changes to the codebase and git.

        Args:
            message (str): The commit message
            verify (bool): Whether to verify the commit before committing. Defaults to False.

        Returns:
            GitCommit | None: The commit object if changes were committed, None otherwise.
        """
        ...
    
    @noapidoc
    def commit(self, sync_graph: bool = ...) -> None:
        """Commits all staged changes to the codebase and synchronizes the graph if specified.

        This method must be called when multiple overlapping edits are made on a single entity to ensure proper tracking of changes.
        For example, when renaming a symbol and then moving it to a different file, commit must be called between these operations.

        Args:
            sync_graph (bool): Whether to synchronize the graph after committing changes. Defaults to True.

        Returns:
            None
        """
        ...
    
    @noapidoc
    def git_push(self, *args, **kwargs) -> PushInfoList:
        """Git push."""
        ...
    
    @property
    def default_branch(self) -> str:
        """The default branch of this repository.

        Returns the name of the default branch (e.g. 'main' or 'master') for the current repository.

        Returns:
            str: The name of the default branch.
        """
        ...
    
    @property
    def current_commit(self) -> GitCommit | None:
        """Returns the current Git commit that is checked out in the repository.

        Args:
            None

        Returns:
            GitCommit | None: The currently checked out Git commit object, or None if no commit is checked out.
        """
        ...
    
    @stopwatch
    @noapidoc
    def reset(self) -> None:
        """Resets the codebase by:
        - Discarding any staged/unstaged changes
        - Resetting stop codemod limits: (max seconds, max transactions, max AI requests)
        - Clearing logs
        - Clearing pending transactions + pending files
        - Syncing graph to synced_commit
        """
        ...
    
    def checkout(self, *, commit: str | GitCommit | None = ..., branch: str | None = ..., create_if_missing: bool = ..., remote: bool = ...) -> CheckoutResult:
        """Checks out a git branch or commit and syncs the codebase graph to the new state.

        This method discards any pending changes, performs a git checkout of the specified branch or commit,
        and then syncs the codebase graph to reflect the new state.

        Args:
            commit (str | GitCommit | None): Hash or GitCommit object to checkout. Cannot be used with branch.
            branch (str | None): Name of branch to checkout. Cannot be used with commit.
            create_if_missing (bool): If True, creates the branch if it doesn't exist. Defaults to False.
            remote (bool): If True, attempts to pull from remote when checking out branch. Defaults to False.

        Returns:
            CheckoutResult: The result of the checkout operation.

        Raises:
            AssertionError: If neither commit nor branch is specified, or if both are specified.
        """
        ...
    
    @noapidoc
    def sync_to_commit(self, target_commit: GitCommit) -> None:
        """Updates the current base to a new commit."""
        ...
    
    @noapidoc
    def get_diffs(self, base: str | None = ...) -> list[Diff]:
        """Get all changed files."""
        ...
    
    @noapidoc
    def get_diff(self, base: str | None = ...) -> str:
        """Produce a single git diff for all files."""
        ...
    
    @noapidoc
    def clean_repo(self): # -> None:
        """Cleaning a codebase repo by:
        1. Deleting all branches except the checked out one
        2. Deleting all remotes except origin

        NOTE: doesn't discard changes b/c this should be handled by self.reset
        NOTE: doesn't checkout onto the default branch b/c this should be handled by self.checkout
        """
        ...
    
    @noapidoc
    def stash_changes(self): # -> None:
        """Stash all changes in the codebase."""
        ...
    
    @noapidoc
    def restore_stashed_changes(self): # -> None:
        """Restore the most recent stash in the codebase."""
        ...
    
    def visualize(self, G: Graph | go.Figure, root: Editable | str | int | None = ...) -> None:
        """Visualizes a NetworkX graph or Plotly figure.

        Creates a visualization of the provided graph using GraphViz. This is useful for visualizing dependency graphs, call graphs,
        directory structures, or other graph-based representations of code relationships.

        Args:
            G (Graph | go.Figure): A NetworkX graph or Plotly figure to visualize
            root (Editable | str | int | None): The root node to visualize around. When specified, the visualization will be centered on this node. Defaults to None.

        Returns:
            None
        """
        ...
    
    @noapidoc
    def flags(self) -> list[CodeFlag]:
        """Returns all collected code flags in find mode.

        Returns:
            list[CodeFlag]: A list of all flags in the codebase.
        """
        ...
    
    @noapidoc
    def flag_instance(self, symbol: TSymbol | None = ..., **kwargs: Unpack[FlagKwargs]) -> CodeFlag:
        """Flags a symbol, file or import to enable enhanced tracking of changes and splitting into
        smaller PRs.

        This method should be called once per flaggable entity and should be called before any edits are made to the entity.
        Flags enable tracking of changes and can be used for various purposes like generating pull requests or applying changes selectively.

        Args:
            symbol (TSymbol | None): The symbol to flag. Can be None if just flagging a message.
            **kwargs: Arguments used to construct the flag
        Returns:
            CodeFlag: A flag object representing the flagged entity.
        """
        ...
    
    def should_fix(self, flag: CodeFlag) -> bool:
        """Returns True if the flag should be fixed based on the current mode and active group.

        Used to filter out flags that are not in the active group and determine if the flag should be processed or ignored.

        Args:
            flag (CodeFlag): The code flag to check.

        Returns:
            bool: True if the flag should be fixed, False if it should be ignored.
            Returns False in find mode.
            Returns True if no active group is set.
            Returns True if the flag's hash exists in the active group hashes.
        """
        ...
    
    @noapidoc
    def set_find_mode(self, find_mode: bool) -> None:
        ...
    
    @noapidoc
    def set_active_group(self, group: Group) -> None:
        """Will only fix these flags."""
        ...
    
    _logs = ...
    def __is_markup_loggable__(self, arg) -> bool:
        ...
    
    @noapidoc
    def log(self, *args) -> None:
        """Logs a message as a string.

        At the end, we will save a tail of these logs on the CodemodRun
        """
        ...
    
    @noapidoc
    def reset_logs(self) -> None:
        """Resets the logs."""
        ...
    
    @noapidoc
    def get_finalized_logs(self) -> str:
        """Returns the logs as a string, truncating if necessary."""
        ...
    
    @contextmanager
    @noapidoc
    def session(self, sync_graph: bool = ..., commit: bool = ..., session_options: SessionOptions = ...) -> Generator[None, None, None]:
        ...
    
    _ai_helper: AbstractAIHelper = ...
    _num_ai_requests: int = ...
    @property
    @noapidoc
    def ai_client(self) -> AbstractAIHelper:
        """Enables calling AI/LLM APIs - re-export of the initialized `openai` module"""
        ...
    
    def ai(self, prompt: str, target: Editable | None = ..., context: Editable | list[Editable] | dict[str, Editable | list[Editable]] | None = ..., model: str = ...) -> str:
        """Generates a response from the AI based on the provided prompt, target, and context.

        A method that sends a prompt to the AI client along with optional target and context information to generate a response.
        Used for tasks like code generation, refactoring suggestions, and documentation improvements.

        Args:
            prompt (str): The text prompt to send to the AI.
            target (Editable | None): An optional editable object (like a function, class, etc.) that provides the main focus for the AI's response.
            context (Editable | list[Editable] | dict[str, Editable | list[Editable]] | None): Additional context to help inform the AI's response.
            model (str): The AI model to use for generating the response. Defaults to "gpt-4o".

        Returns:
            str: The generated response from the AI.

        Raises:
            MaxAIRequestsError: If the maximum number of allowed AI requests (default 150) has been exceeded.
        """
        ...
    
    def set_ai_key(self, key: str) -> None:
        """Sets the AI key for the current codebase instance."""
        ...
    
    def find_by_span(self, span: Span) -> list[Editable]:
        """Finds editable objects that overlap with the given source code span.

        Searches for editable objects (like functions, classes, variables) within a file
        that overlap with the specified byte range span. Returns an empty list if no
        matching file is found.

        Args:
            span (Span): The span object containing the filepath and byte range to search within.

        Returns:
            list[Editable]: A list of Editable objects that overlap with the given span.
        """
        ...
    
    def set_session_options(self, **kwargs: Unpack[SessionOptions]) -> None:
        """Sets the Session options for the current codebase."""
        ...
    


CodebaseType = Codebase[SourceFile, Directory, Symbol, Class, Function, Import, Assignment, Interface, TypeAlias, Parameter, CodeBlock]
PyCodebaseType = Codebase[PyFile, Directory, PySymbol, PyClass, PyFunction, PyImport, PyAssignment, Interface, TypeAlias, PyParameter, PyCodeBlock]
TSCodebaseType = Codebase[TSFile, Directory, TSSymbol, TSClass, TSFunction, TSImport, TSAssignment, TSInterface, TSTypeAlias, TSParameter, TSCodeBlock]
