"""
:mod:`tests.unit.file.test_u_file_rda` module.

Unit tests for :mod:`etlplus.file.rda`.
"""

from __future__ import annotations

from pathlib import Path

from etlplus.file import rda as mod
from etlplus.file.base import WriteOptions

from .pytest_file_contracts import RDataModuleContract
from .pytest_file_support import DictRecordsFrameStub
from .pytest_file_support import PyreadrStub
from .pytest_file_support import RDataNoWriterStub
from .pytest_file_support import RDataPandasStub
from .pytest_file_types import OptionalModuleInstaller

# SECTION: PRAGMAS ========================================================== #

# pylint: disable=import-outside-toplevel,protected-access,unused-argument

# SECTION: HELPERS ========================================================== #


class _PyreadrFallbackStub:
    """Stub exposing ``write_rda`` only."""

    def __init__(self) -> None:
        self.writes: list[tuple[str, object]] = []

    def read_r(
        self,
        path: str,
    ) -> dict[str, object]:  # noqa: ARG002
        """Simulate reading an R data file by returning an empty mapping."""
        return {}

    def write_rda(
        self,
        path: str,
        frame: object,
    ) -> None:
        """Simulate writing an R data file by recording the call."""
        self.writes.append((path, frame))


# SECTION: TESTS ============================================================ #


class TestRda(RDataModuleContract):
    """Unit tests for :mod:`etlplus.file.rda`."""

    module = mod
    format_name = 'rda'
    writer_missing_pattern = 'write_rdata'

    def build_frame(
        self,
        records: list[dict[str, object]],
    ) -> DictRecordsFrameStub:
        """Build a frame-like stub."""
        return DictRecordsFrameStub(records)

    def build_pandas_stub(self) -> object:
        """Build pandas stub."""
        return RDataPandasStub()

    def build_pyreadr_stub(
        self,
        result: dict[str, object],
    ) -> PyreadrStub:
        """Build pyreadr stub exposing ``write_rdata``."""
        return PyreadrStub(result)

    def build_reader_only_stub(self) -> RDataNoWriterStub:
        """Build pyreadr-like stub without write methods."""
        return RDataNoWriterStub()

    def test_write_falls_back_to_write_rda(
        self,
        tmp_path: Path,
        optional_module_stub: OptionalModuleInstaller,
    ) -> None:
        """
        Test that :func:`write` falls back to :meth:`write_rda` when needed.
        """
        pyreadr = _PyreadrFallbackStub()
        optional_module_stub({'pyreadr': pyreadr, 'pandas': RDataPandasStub()})
        path = self.format_path(tmp_path)

        written = mod.RdaFile().write(path, [{'id': 1}])

        assert written == 1
        assert pyreadr.writes

    def assert_write_success(
        self,
        pyreadr_stub: object,
        path: Path,  # noqa: ARG002
    ) -> None:
        """Assert RDA write route using ``write_rdata`` with ``df_name``."""
        stub = pyreadr_stub
        assert isinstance(stub, PyreadrStub)
        assert stub.write_rdata_calls
        _, _, kwargs = stub.write_rdata_calls[0]
        assert kwargs.get('df_name') == 'data'

    def test_list_datasets_returns_default_key_for_empty_payload(
        self,
        tmp_path: Path,
        optional_module_stub: OptionalModuleInstaller,
    ) -> None:
        """
        Test that :meth:`list_datasets` returns default key when file has no
        objects.
        """
        pyreadr = self.build_pyreadr_stub({})
        self._install_optional_dependencies(
            optional_module_stub,
            pyreadr_stub=pyreadr,
        )

        result = mod.RdaFile().list_datasets(self.format_path(tmp_path))

        assert result == ['data']

    def test_list_datasets_returns_object_names(
        self,
        tmp_path: Path,
        optional_module_stub: OptionalModuleInstaller,
    ) -> None:
        """
        Test that :meth:`list_datasets` exposes keys from pyreadr payloads.
        """
        pyreadr = self.build_pyreadr_stub({'first': object(), 'second': 1})
        self._install_optional_dependencies(
            optional_module_stub,
            pyreadr_stub=pyreadr,
        )

        result = mod.RdaFile().list_datasets(self.format_path(tmp_path))

        assert result == ['first', 'second']

    def test_write_dataset_uses_dataset_option_name(
        self,
        tmp_path: Path,
        optional_module_stub: OptionalModuleInstaller,
    ) -> None:
        """
        Test that :meth:`write_dataset` forwards explicit dataset names to
        :mod:`pyreadr`.
        """
        pyreadr = self.build_pyreadr_stub({})
        self._install_optional_dependencies(
            optional_module_stub,
            pyreadr_stub=pyreadr,
        )

        written = mod.RdaFile().write_dataset(
            self.format_path(tmp_path),
            [{'id': 1}],
            options=WriteOptions(dataset='metrics'),
        )

        assert written == 1
        assert isinstance(pyreadr, PyreadrStub)
        assert pyreadr.write_rdata_calls
        _, _, kwargs = pyreadr.write_rdata_calls[-1]
        assert kwargs.get('df_name') == 'metrics'
