"""Load and manage PyRel configuration.

Most projects only need the :func:`create_config` function:

- Call ``create_config()`` to auto-discover and load configuration (for example ``raiconfig.yaml``),
  or set the ``RAI_CONFIG_FILE_PATH`` environment variable to point to a specific file.
- Or pass keyword arguments to build a configuration in code.

This package re-exports the most commonly used config and connection classes so
you can import from this package instead of importing from submodules
like :mod:`~relationalai.config.config` or :mod:`~relationalai.config.connections`.

Examples
--------
To load a config from a file, create a ``raiconfig.yaml`` in your project:

```yaml
default_connection: sf
connections:
    sf:
        type: snowflake
        authenticator: username_password
        account: my_account
        warehouse: my_warehouse
        user: my_user
        password: ${SNOWFLAKE_PASSWORD}
```

Then load it  with :func:`~relationalai.config.config.create_config` (searches upwards from the current working directory):

>>> from relationalai.config import create_config
>>> cfg = create_config()
>>> session = cfg.get_session(name="sf")

Profiles let you define named overrides (for example, ``dev`` vs ``prod``) on top
of a shared base config. Select which overlay to apply with ``active_profile``.

```yaml
default_connection: sf
connections:
    sf:
        type: snowflake
        authenticator: username_password
        account: my_account
        warehouse: base_warehouse
        user: my_user
        password: ${SNOWFLAKE_PASSWORD}

profile:
    dev:
        connections:
            sf:
                warehouse: dev_warehouse
    prod:
        connections:
            sf:
                warehouse: prod_warehouse

active_profile: dev
```

When you load this config with :func:`~relationalai.config.config.create_config`, the ``active_profile``
is applied on top of the base config so that :meth:`Config.get_session` creates a session using
the overrides in the active profile.

You can also build a config programmatically by passing connection definitions as plain dictionaries.
For example, to create a Snowflake connection config:

>>> from relationalai.config import create_config
>>> cfg = create_config(
...     connections={
...         "sf": {
...             "type": "snowflake",
...             "authenticator": "username_password",
...             "account": "my_account",
...             "warehouse": "my_warehouse",
...             "user": "my_user",
...             "password": "my_password",
...         }
...     }
... )

Alternatively, you can instantiate connection config classes and use them to build the config:

>>> from relationalai.config import create_config, UsernamePasswordAuth
>>> cfg = create_config(
...     connections={
...         "sf": UsernamePasswordAuth(
...             account="my_account",
...             warehouse="my_warehouse",
...             user="my_user",
...             password="my_password",
...         )
...     }
... )

See the :mod:`~relationalai.config.config` and :mod:`~relationalai.config.connections`
submodules for more details on the available config and connection types.

Notes
-----
- Set ``RAI_CONFIG_FILE_PATH`` to an absolute path to bypass auto-discovery entirely.
- If ``raiconfig.yaml``/``raiconfig.yml`` is not found, :func:`~relationalai.config.config.create_config`
  falls back to other supported sources (Snowflake and DBT), converting them into the same validated config shape.
  See the :func:`~relationalai.config.config.create_config` docs for details.
"""

from .config import Config, create_config, RAIConfig, ConfigFromDBT, ConfigFromSnowflake, ConfigFromActiveSession, NoOpConfig
from .config_fields import DataConfig, CompilerConfig, ModelConfig, DebugConfig, ExecutionConfig, JobsConfig
from .config_reasoners_fields import (
    ReasonersConfig,
    ReasonerConfig,
    LogicReasonerConfig,
    ReasonerLogicLqpConfig,
    EngineSize,
    LqpSemanticsVersion,
)
from .connections import (
    ConnectionConfig,
    SnowflakeConnection,
    DuckDBConnection,
    UsernamePasswordAuth,
    UsernamePasswordMFAAuth,
    ExternalBrowserAuth,
    JWTAuth,
    OAuthAuth,
    ProgrammaticAccessTokenAuth,
)
from .errors import (
    ConfigError,
    ConfigFileNotFoundError,
    ConfigValidationError,
    ConfigFieldMissingError,
    ConfigFieldTypeError,
    ConfigErrorContext,
    ConfigSourceType,
)

# include this package in the docs
__include_in_docs__ = True

__all__ = [
    # Main classes and factory
    "Config",
    "create_config",
    "RAIConfig",
    "ConfigFromDBT",
    "ConfigFromSnowflake",
    "ConfigFromActiveSession",
    "NoOpConfig",

    # Nested config models
    "DataConfig",
    "CompilerConfig",
    "ModelConfig",
    "DebugConfig",
    "ExecutionConfig",
    "JobsConfig",

    # Reasoner config models
    "ReasonersConfig",
    "ReasonerConfig",
    "LogicReasonerConfig",
    "ReasonerLogicLqpConfig",

    # Type aliases
    "EngineSize",
    "LqpSemanticsVersion",

    # Connection types
    "ConnectionConfig",
    "SnowflakeConnection",
    "DuckDBConnection",

    # Snowflake authenticators
    "UsernamePasswordAuth",
    "UsernamePasswordMFAAuth",
    "ExternalBrowserAuth",
    "JWTAuth",
    "OAuthAuth",
    "ProgrammaticAccessTokenAuth",

    # Error classes
    "ConfigError",
    "ConfigFileNotFoundError",
    "ConfigValidationError",
    "ConfigFieldMissingError",
    "ConfigFieldTypeError",
    "ConfigErrorContext",
    "ConfigSourceType",
]
