from typing import Any, Dict, List, NoReturn, Optional, cast
from enum import Enum
import pathlib

from axipy.da.data_object import Table
from ..schema import Schema
from .data_provider import DataProvider
from .provider_utils import OpenMode
from .source import Source, Destination

__all__: List[str] = [
    "SqliteSource",
    "SqliteDestination",
    "SqliteDataProvider",
    "SqliteGeometryFormat",
]


class SqliteSource(Source):
    pass


class SqliteDestination(Destination):
    pass


class SqliteGeometryFormat(int, Enum):
    """
    Формат данных sqlite.
    Используется при задании типа геометрии :meth:`SqliteDataProvider.get_destination`
    """

    WKT = 1
    """Формат WKT (Well-known text)."""
    WKB = 2
    """Формат WKB (Well-Known Binary)."""
    FGF = 3
    """Формат FGF (FDO Geometry Format)."""
    SPATIALITE = 4
    """Формат Spatialite."""
    GPKG = 5
    """Формат GPKG (GeoPackage)."""


class SqliteDataProvider(DataProvider):
    """
    Векторный провайдер sqlite.

    Note:
        Ссылку на провайдер можно получить через глобальную переменную :attr:`axipy.provider_manager.sqlite`.
    """

    @staticmethod
    def _identifier() -> str:
        return "SqliteDataProvider"

    def get_source(
        self,
        filepath: str,
        dataobject: Optional[str] = None,
        sql: Optional[str] = None,
        prj: Optional[str] = None,
        alias: Optional[str] = None,
    ) -> Source:
        """
        Создает источник данных. В качестве объекта может быть указана либо таблица,
        либо текст запроса. Если указан `sql`, то он имеет более высокий приоритет по
        отношению к значению `dataobject`. Если оба параметра опущены, будет возвращен
        None.

        Args:
            filepath: Путь к файлу.
            dataobject: Имя таблицы.
            sql: SQL-запрос.
            prj: Строка Системы Координат.
            alias: Псевдоним для открываемой таблицы.

        Пример с таблицей::

            table = provider_manager.openfile('world.sqlite', dataobject='world')

        Пример с запросом и переопределенной СК::

            table = provider_manager.openfile('world.sqlite', sql="select * from world where Страна like 'Р%'", prj='12, 104, "m", 0')
        """
        return SqliteSource(
            Source._provider(self.id),
            Source._table_file(filepath),
            Source._prj(prj),
            Source._alias(alias),
            {"dataobject": dataobject, "sql": sql},
        )

    def open(
        self,
        filepath: str,
        dataobject: Optional[str] = None,
        sql: Optional[str] = None,
        prj: Optional[str] = None,
        alias: Optional[str] = None,
    ) -> Table:
        """
        Открывает объект данных.

        В качестве объекта может быть указана либо таблица, либо текст запроса.
        Если указан `sql`, то он имеет более высокий приоритет по отношению к значению `dataobject`.
        Если оба параметра опущены, будет возвращен None.

        Args:
            filepath: Путь к файлу.
            dataobject: Имя таблицы.
            sql: SQL-запрос.
            prj: Строка Системы Координат.
            alias: Псевдоним для открываемой таблицы.
        """
        return cast(Table, self.get_source(filepath, dataobject, sql, prj, alias).open())

    def get_destination(
        self, 
        filepath: str,
        dataobject: str,
        schema: Schema,
        geometry_format: SqliteGeometryFormat = SqliteGeometryFormat.GPKG
    ) -> Destination:
        """
        Создает назначение объекта данных.

        Args:
          filepath: Источник данных или имя файла.
          dataobject: Наименование таблицы
          schema: Схема таблицы.
          geometry_format: Формат данных

    .. seealso::
       Пример использования см :ref:`ref-converter-sqlite`

        """
        pars: Dict[str, Any] = {
            "dataobject": dataobject,
        }
        if geometry_format == SqliteGeometryFormat.WKT:
            pars["format"] = "WKT"
        elif geometry_format == SqliteGeometryFormat.WKB:
            pars["format"] = "WKB"
        elif geometry_format == SqliteGeometryFormat.FGF:
            pars["format"] = "FGF"
        elif geometry_format == SqliteGeometryFormat.SPATIALITE:
            pars["format"] = "Spatialite"
        else:
            pars["format"] = "GPKG"
            ext = pathlib.Path(filepath).suffix
            if ext is None or ext[1:].lower() != "gpkg":
                raise NameError("File suffix must be gpkg.")
        return SqliteDestination(schema, Source._provider(self.id), Source._table_file(filepath), pars)

    def create_open(self) -> NoReturn:
        """
        Attention:
            Не поддерживается.

        Raises:
            NotImplementedError
        """
        raise NotImplementedError
