"""
:mod:`tests.integration.file.pytest_smoke_file_contracts` module.

Reusable smoke-test contracts for :mod:`etlplus.file` modules.
"""

from __future__ import annotations

from collections.abc import Mapping
from pathlib import Path
from types import ModuleType

import pytest

from etlplus.utils.types import JSONDict
from etlplus.utils.types import JSONList

from ...pytest_file_common import call_handler_operation
from ...pytest_file_common import resolve_module_handler
from ...pytest_file_common import skip_on_known_file_io_error

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

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

# SECTION: EXPORTS ========================================================== #


__all__ = [
    # Classes
    'SmokeRoundtripModuleContract',
    # Constants
    'SMOKE_ROUNDTRIP_EXCEPTION_MODULES',
    'SMOKE_ROUNDTRIP_EXCEPTION_OVERRIDES',
    'SMOKE_ROUNDTRIP_OVERRIDE_ATTRS',
    # Functions
    'run_file_smoke',
]


# SECTION: CONSTANTS ======================================================== #


SMOKE_ROUNDTRIP_OVERRIDE_ATTRS = frozenset(
    {
        'error_match',
        'expect_write_error',
        'file_name',
    },
)
SMOKE_ROUNDTRIP_EXCEPTION_OVERRIDES = {
    'test_i_file_gz.py': frozenset({'file_name'}),
    'test_i_file_xls.py': frozenset(
        {'expect_write_error', 'error_match'},
    ),
    'test_i_file_zip.py': frozenset({'file_name'}),
}
SMOKE_ROUNDTRIP_EXCEPTION_MODULES = frozenset(
    SMOKE_ROUNDTRIP_EXCEPTION_OVERRIDES,
)


class SmokeRoundtripModuleContract:
    """Reusable write/read smoke contract for file-format modules."""

    module: ModuleType
    file_name: str | None = None
    payload: object | None = None
    use_sample_record: bool = False
    write_kwargs: Mapping[str, object] | None = None
    expect_write_error: type[Exception] | None = None
    error_match: str | None = None

    def smoke_file_name(self) -> str:
        """Return the integration smoke-file name for this module."""
        if self.file_name is not None:
            return self.file_name
        suffix = resolve_module_handler(self.module).format.value
        return f'data.{suffix}'

    def test_roundtrip_smoke(
        self,
        tmp_path: Path,
        sample_record: JSONDict,
        sample_records: JSONList,
    ) -> None:
        """Test that read/write can be invoked with minimal payloads."""
        path = tmp_path / self.smoke_file_name()
        payload: object
        if self.payload is not None:
            payload = self.payload
        elif self.use_sample_record:
            payload = sample_record
        else:
            payload = sample_records
        run_file_smoke(
            self.module,
            path,
            payload,
            write_kwargs=self.write_kwargs,
            expect_write_error=self.expect_write_error,
            error_match=self.error_match,
        )


def run_file_smoke(
    module: ModuleType,
    path: Path,
    payload: object,
    *,
    write_kwargs: Mapping[str, object] | None = None,
    expect_write_error: type[Exception] | None = None,
    error_match: str | None = None,
) -> None:
    """
    Run a minimal read/write smoke cycle for file modules.

    Parameters
    ----------
    module : ModuleType
        File module exposing a singleton ``*_HANDLER`` instance.
    path : Path
        Target path for the test file.
    payload : object
        Payload passed to ``write``.
    write_kwargs : Mapping[str, object] | None, optional
        Keyword arguments forwarded to ``write``.
    expect_write_error : type[Exception] | None, optional
        Expected exception type for write failures.
    error_match : str | None, optional
        Regex message to assert when ``expect_write_error`` is provided.

    Raises
    ------
    OSError
        If the smoke test encounters unexpected file system errors.
    """
    try:
        if expect_write_error is not None:
            match = error_match or ''
            with pytest.raises(expect_write_error, match=match):
                call_handler_operation(
                    module,
                    operation='write',
                    path=path,
                    payload=payload,
                    write_kwargs=write_kwargs,
                )
            return
        written = call_handler_operation(
            module,
            operation='write',
            path=path,
            payload=payload,
            write_kwargs=write_kwargs,
        )
        assert isinstance(written, int)
        assert written >= 0
        result = call_handler_operation(
            module,
            operation='read',
            path=path,
        )
        assert result is not None
    except OSError as exc:
        skip_on_known_file_io_error(
            error=exc,
            module_name=module.__name__,
        )
        raise
    except ImportError as exc:
        pytest.skip(str(exc))
