"""
Snowflake Sequence API.

The Snowflake Sequence API is a REST API that you can use to access, update, and perform certain actions on Sequence resource in a Snowflake database.  # noqa: E501

The version of the OpenAPI document: 0.0.1
Contact: support@snowflake.com
Generated by: https://openapi-generator.tech

Do not edit this file manually.
"""

from typing import TYPE_CHECKING, Any, Iterator, Optional, TypeVar, Union

from snowflake.core._common import CreateMode, SchemaObjectCollectionParent, SchemaObjectReferenceMixin
from snowflake.core._generated.api_client import StoredProcApiClient
from snowflake.core._internal.telemetry import api_telemetry
from snowflake.core._operation import PollingOperation, PollingOperations
from snowflake.core._utils import get_function_name_with_args, replace_function_name_in_name_with_args
from snowflake.core.sequence._generated.models.sequence import Sequence

from .sequence_api import SequenceApi


if TYPE_CHECKING:
    from snowflake.core.schema import SchemaResource


T = TypeVar("T")


class SequenceCollectionBase(SchemaObjectCollectionParent["SequenceResource"]):
    _identifier_requires_args: bool = False

    def __init__(self, schema: "SchemaResource", resource_class: type[T]) -> None:
        super().__init__(schema, resource_class)
        self._api = SequenceApi(
            root=self.root, resource_class=self._ref_class, sproc_client=StoredProcApiClient(root=self.root)
        )

    @api_telemetry
    def create(self, sequence: Sequence, mode: Optional[Union[CreateMode, str]] = None) -> "SequenceResource":  # noqa: F821
        """Create a sequence.

        Parameters
        __________
        sequence: Sequence
             (required)
        mode: Union[CreateMode, str]
             Parameter allowing support for different modes of resource creation. Possible values include: - `errorIfExists`: Throws an error if you try to create a resource that already exists. - `orReplace`: Automatically replaces the existing resource with the current one. - `ifNotExists`: Creates a new resource when an alter is requested for a non-existent resource.
        """
        if isinstance(mode, str):
            mode = CreateMode[mode].value

        self._api.create_sequence(
            self.database.name,
            self.schema.name,
            sequence=sequence,
            create_mode=mode,
            async_req=False,
        )
        if self._identifier_requires_args:
            return self._ref_class(get_function_name_with_args(sequence), self)
        return self._ref_class(sequence.name, self)

    @api_telemetry
    def create_async(
        self, sequence: Sequence, mode: Optional[Union[CreateMode, str]] = None
    ) -> PollingOperation["SequenceResource"]:  # noqa: F821
        """An asynchronous version of :func:`create`.

        Refer to :class:`~snowflake.core.PollingOperation` for more information on asynchronous execution and
        the return type.
        """
        if isinstance(mode, str):
            mode = CreateMode[mode].value

        future = self._api.create_sequence(
            self.database.name,
            self.schema.name,
            sequence=sequence,
            create_mode=mode,
            async_req=True,
        )
        if self._identifier_requires_args:
            return PollingOperation(future, lambda _: self._ref_class(get_function_name_with_args(sequence), self))
        return PollingOperation(
            future, lambda _: self._ref_class(sequence if isinstance(sequence, str) else sequence.name, self)
        )

    @api_telemetry
    def iter(self, *, like: Optional[str] = None) -> Iterator[Sequence]:
        """List sequences.

        Parameters
        __________
        like: str
             Parameter to filter the command output by resource name. Uses case-insensitive pattern matching, with support for SQL wildcard characters.
        """
        resources = self._api.list_sequences(
            self.database.name,
            self.schema.name,
            like=like,
            async_req=False,
        )
        return iter(resources)

    @api_telemetry
    def iter_async(self, *, like: Optional[str] = None) -> PollingOperation[Iterator[Sequence]]:
        """An asynchronous version of :func:`iter`.

        Refer to :class:`~snowflake.core.PollingOperation` for more information on asynchronous execution and
        the return type.
        """
        future = self._api.list_sequences(
            self.database.name,
            self.schema.name,
            like=like,
            async_req=True,
        )

        return PollingOperations.iterator(future)


class SequenceResourceBase(SchemaObjectReferenceMixin["SequenceCollection"]):
    _collection_class: SequenceCollectionBase
    _identifier_requires_args: bool = False
    _plural_name: str

    def __init__(self, name: str, collection_class: SequenceCollectionBase) -> None:
        self.collection = collection_class
        if self._identifier_requires_args:
            self.name_with_args = name
        else:
            self.name = name

    @api_telemetry
    def clone(
        self,
        sequence: Sequence,
        create_mode: Optional[Union[CreateMode, str]] = None,
        target_database: Optional[str] = None,
        target_schema: Optional[str] = None,
    ) -> None:
        """Create a new sequence by cloning from the specified resource.

        Parameters
        __________
        sequence: Sequence
             (required)
        create_mode: Union[CreateMode, str]
             Parameter allowing support for different modes of resource creation. Possible values include: - `errorIfExists`: Throws an error if you try to create a resource that already exists. - `orReplace`: Automatically replaces the existing resource with the current one. - `ifNotExists`: Creates a new resource when an alter is requested for a non-existent resource.
        target_database: str
             Database of the target resource. Defaults to the source's database
        target_schema: str
             Schema of the target resource. Defaults to the source's schema
        """
        self.collection._api.clone_sequence(
            self.database.name,
            self.schema.name,
            self._identifier,
            sequence=sequence,
            create_mode=create_mode,
            target_database=target_database,
            target_schema=target_schema,
            async_req=False,
        )

    @api_telemetry
    def clone_async(
        self,
        sequence: Sequence,
        create_mode: Optional[Union[CreateMode, str]] = None,
        target_database: Optional[str] = None,
        target_schema: Optional[str] = None,
    ) -> PollingOperation[None]:
        """An asynchronous version of :func:`clone`.

        Refer to :class:`~snowflake.core.PollingOperation` for more information on asynchronous execution and
        the return type.
        """
        future = self.collection._api.clone_sequence(
            self.database.name,
            self.schema.name,
            self._identifier,
            sequence=sequence,
            create_mode=create_mode,
            target_database=target_database,
            target_schema=target_schema,
            async_req=True,
        )
        return PollingOperations.empty(future)

    @api_telemetry
    def fetch(
        self,
    ) -> Sequence:
        """Fetch a sequence.

        Parameters
        __________
        """
        result = self.collection._api.fetch_sequence(
            self.database.name,
            self.schema.name,
            self._identifier,
            async_req=False,
        )
        return result

    @api_telemetry
    def fetch_async(
        self,
    ) -> PollingOperation[Sequence]:
        """An asynchronous version of :func:`fetch`.

        Refer to :class:`~snowflake.core.PollingOperation` for more information on asynchronous execution and
        the return type.
        """
        future = self.collection._api.fetch_sequence(
            self.database.name,
            self.schema.name,
            self._identifier,
            async_req=True,
        )

        return PollingOperations.identity(future)

    @api_telemetry
    def rename(
        self,
        target_name: str,
        if_exists: Optional[bool] = None,
        target_database: Optional[str] = None,
        target_schema: Optional[str] = None,
    ) -> None:
        """Rename a sequence with a new identifier.

        Parameters
        __________
        target_name: str
             Name of the target resource. (required)
        if_exists: bool
             Parameter that specifies how to handle the request for a resource that does not exist: - `true`: The endpoint does not throw an error if the resource does not exist. It returns a 200 success response, but does not take any action on the resource. - `false`: The endpoint throws an error if the resource doesn't exist.
        target_database: str
             Database of the target resource. Defaults to the source's database
        target_schema: str
             Schema of the target resource. Defaults to the source's schema
        """
        self.collection._api.rename_sequence(
            self.database.name,
            self.schema.name,
            self._identifier,
            target_name=target_name,
            if_exists=if_exists,
            target_database=target_database,
            target_schema=target_schema,
            async_req=False,
        )

        self._rename_finalizer(target_name, target_schema, target_database)

    @api_telemetry
    def rename_async(
        self,
        target_name: str,
        if_exists: Optional[bool] = None,
        target_database: Optional[str] = None,
        target_schema: Optional[str] = None,
    ) -> PollingOperation[None]:
        """An asynchronous version of :func:`rename`.

        Refer to :class:`~snowflake.core.PollingOperation` for more information on asynchronous execution and
        the return type.
        """
        future = self.collection._api.rename_sequence(
            self.database.name,
            self.schema.name,
            self._identifier,
            target_name=target_name,
            if_exists=if_exists,
            target_database=target_database,
            target_schema=target_schema,
            async_req=True,
        )

        def finalize(_: Any) -> None:
            self._rename_finalizer(
                target_name=target_name, target_schema=target_schema, target_database=target_database
            )

        return PollingOperation(future, finalize)

    def _rename_finalizer(self, target_name: str, target_schema: Optional[str], target_database: Optional[str]) -> None:
        """Finalizer for rename operation."""
        self._set_new_name(target_name)
        if target_database is None:
            target_database = self.database.name
        if target_schema is None:
            target_schema = self.schema.name

        # Schema level resource
        if target_database != self.database.name or target_schema != self.schema.name:
            self.collection = getattr(self.root.databases[target_database].schemas[target_schema], self._plural_name)

    @api_telemetry
    def drop(
        self,
        if_exists: Optional[bool] = None,
    ) -> None:
        """Delete a sequence.

        Parameters
        __________
        if_exists: bool
             Parameter that specifies how to handle the request for a resource that does not exist: - `true`: The endpoint does not throw an error if the resource does not exist. It returns a 200 success response, but does not take any action on the resource. - `false`: The endpoint throws an error if the resource doesn't exist.
        """
        self.collection._api.delete_sequence(
            self.database.name,
            self.schema.name,
            self._identifier,
            if_exists=if_exists,
            async_req=False,
        )

    @api_telemetry
    def drop_async(
        self,
        if_exists: Optional[bool] = None,
    ) -> PollingOperation[None]:
        """An asynchronous version of :func:`drop`.

        Refer to :class:`~snowflake.core.PollingOperation` for more information on asynchronous execution and
        the return type.
        """
        future = self.collection._api.delete_sequence(
            self.database.name,
            self.schema.name,
            self._identifier,
            if_exists=if_exists,
            async_req=True,
        )
        return PollingOperations.empty(future)

    @property
    def _identifier(self) -> str:
        """Get the identifier for this resource."""
        if self._identifier_requires_args:
            return self.name_with_args
        return self.name

    def _set_new_name(self, target_name: str) -> None:
        """Set a new name for the resource."""
        if self._identifier_requires_args:
            old_name = self.name_with_args
            self.name_with_args = replace_function_name_in_name_with_args(self.name_with_args, target_name)
        else:
            old_name = self.name
            self.name = target_name

        # Update the collection reference to point to the new name
        self.collection.update_reference(old_name=old_name, new_name=self._identifier, resource=self)
