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

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

from __future__ import annotations

from collections.abc import Callable
from pathlib import Path
from typing import Any
from typing import cast

import pytest

from etlplus.file import xlsm as mod

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


class TestXlsmRead:
    """Unit tests for :func:`etlplus.file.xlsm.read`."""

    # pylint: disable=unused-argument

    def test_read_returns_records(
        self,
        tmp_path: Path,
        optional_module_stub: Callable[[dict[str, object]], None],
        make_records_frame: Callable[[list[dict[str, object]]], object],
        make_pandas_stub: Callable[[object], object],
    ) -> None:
        """Test that :func:`read` returns the records from the Excel file."""
        frame = make_records_frame([{'id': 1}])
        pandas = cast(Any, make_pandas_stub(frame))
        optional_module_stub({'pandas': pandas})

        result = mod.read(tmp_path / 'data.xlsm')

        assert result == [{'id': 1}]
        assert pandas.read_calls

    def test_read_wraps_import_error(
        self,
        tmp_path: Path,
        monkeypatch: pytest.MonkeyPatch,
        make_import_error_reader: Callable[[str], object],
    ) -> None:
        """
        Test that :func:`read` wraps :class:`ImportError` from :mod:`pandas`.
        """
        monkeypatch.setattr(
            mod,
            'get_pandas',
            lambda *_: make_import_error_reader('read_excel'),
        )

        with pytest.raises(ImportError, match='openpyxl'):
            mod.read(tmp_path / 'data.xlsm')


class TestXlsmWrite:
    """Unit tests for :func:`etlplus.file.xlsm.write`."""

    # pylint: disable=unused-argument

    def test_write_calls_to_excel(
        self,
        tmp_path: Path,
        optional_module_stub: Callable[[dict[str, object]], None],
        make_records_frame: Callable[[list[dict[str, object]]], object],
        make_pandas_stub: Callable[[object], object],
    ) -> None:
        """
        Test that :func:`write` calls :meth:`to_excel` on the
        :class:`pandas.DataFrame`.
        """
        frame = make_records_frame([{'id': 1}])
        pandas = cast(Any, make_pandas_stub(frame))
        optional_module_stub({'pandas': pandas})
        path = tmp_path / 'data.xlsm'

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

        assert written == 1
        assert pandas.last_frame is not None
        assert pandas.last_frame.to_excel_calls == [
            {'path': path, 'index': False},
        ]

    def test_write_returns_zero_for_empty_payload(
        self,
        tmp_path: Path,
    ) -> None:
        """Test that writing an empty payload returns zero."""
        assert mod.write(tmp_path / 'data.xlsm', []) == 0

    def test_write_wraps_import_error(
        self,
        tmp_path: Path,
        monkeypatch: pytest.MonkeyPatch,
        make_import_error_writer: Callable[[], object],
    ) -> None:
        """
        Test that :func:`write` wraps :class:`ImportError` from :mod:`pandas`.
        """
        monkeypatch.setattr(
            mod,
            'get_pandas',
            lambda *_: make_import_error_writer(),
        )

        with pytest.raises(ImportError, match='openpyxl'):
            mod.write(tmp_path / 'data.xlsm', [{'id': 1}])
