from pathlib import Path
from typing import Iterator, List, NoReturn, Optional, Union

from axipy._internal._decorator import _experimental, _experimental_class
from axipy.cpp_core_dp import ShadowConverter, ShadowServiceOperation
from axipy.cs import CoordSystem

from ..feature import Feature, _FeatureIterator
from ..schema import Schema
from .data_provider import DataProvider
from .source import Destination, Source

__all__: List[str] = [
    "MifMidDestination",
    "MifMidDataProvider",
    "_MifMidSource",
]


@_experimental_class()
class _MifMidSource(Source):

    def features(self) -> Iterator[Feature]:
        """
        Последовательно запрашивает все записи.

        Пример::

            src = provider_manager.mif.get_source(r'inputfile.mif')
            for f in src.features():
                print('Feature Id:', f.id)
        """
        return _FeatureIterator(ShadowConverter.mifAsIterator(self))

    def schema(self) -> Schema:
        """
        Схема (структура) таблицы.
        """
        return Schema._from_dict(ShadowConverter.mifSchema(self))


class MifMidDestination(Destination):
    pass


class MifMidDataProvider(DataProvider):
    """
    Провайдер данных MIF-MID.

    Note:
        Поддерживает экспорт только в TAB. См. :meth:`convert_to_tab`.

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

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

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

        Raises:
            NotImplementedError
        """
        raise NotImplementedError

    @_experimental()
    def _get_source(self, filepath: Union[str, Path]) -> Source:
        return _MifMidSource(Source._provider(self.id), Source._table_file(filepath))

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

        Raises:
            NotImplementedError
        """
        raise NotImplementedError

    def get_destination(self, filepath: Union[str, Path], schema: Schema) -> Destination:
        """
        Создает назначение объекта данных.

        Args:
            filepath: Путь к файлу.
            schema: Схема таблицы.
        """
        return MifMidDestination(schema, Source._provider(self.id), Source._table_file(filepath))

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

        Raises:
            NotImplementedError
        """
        raise NotImplementedError

    def convert_to_tab(self, mif_filepath: Union[str, Path], tab_filepath: Union[str, Path]) -> None:
        """
        Конвертирует из MIF в TAB.

        .. literalinclude:: /../../tests/doc_examples/da/test_example_export.py
            :pyobject: test_run_example_export_mif
            :lines: 2-5, 11-
            :dedent: 4
            :caption: Пример экспорта

        Args:
            mif_filepath: Путь к исходному файлу.
            tab_filepath: Путь к выходному файлу.
        Raises:
            Exception: Если при конвертации произошла ошибка.
        """
        mif_source = MifMidDestination(
            Schema(),
            Source._provider(self.id),
            Source._table_file(mif_filepath),
            {"access": "ro"},
        )

        tab_dest = MifMidDestination(
            Schema(),
            Source._provider("TabDataProvider"),
            Source._table_file(tab_filepath),
        )

        ShadowConverter.convertMifToTab(mif_source, tab_dest)

    @_experimental()
    def _get_cs_from_file(self, file: Union[str, Path]) -> Optional[CoordSystem]:
        """
        Получение СК из файла MIF

        Args:
            file: путь к файлу MIF
        """
        data = {
            "openWith": self.id,
            "src": str(file),
        }
        cs_str = ShadowServiceOperation.getCoordSystemString(data)
        if cs_str is not None:
            return CoordSystem.from_prj(cs_str)
        return None
