from pathlib import Path

import pytest

from config_inspector.file_content import FileContentService
from config_inspector.file_content import FileMetadataService
from config_inspector.file_content.domain.entities import FileMeta
from config_inspector.file_content.domain.rules import FileSizePolicy
from config_inspector.file_content.infrastructure.mappers import PayloadAssembler
from config_inspector.file_content.infrastructure.repository import FileReader


class DummySizeFormatter:
    def format(self, size):
        return f"{size} bytes"


class DummyDateTimeFormatter:
    def format(self, dt):
        return dt.strftime("%Y-%m-%d %H:%M:%S")


def test_file_metadata_service(tmp_path):
    file = tmp_path / "test.py"
    file.write_text("print('hello')")

    service = FileMetadataService(
        size_formatter=DummySizeFormatter(), datetime_formatter=DummyDateTimeFormatter()
    )

    meta = service.get_metadata(file, label="mylabel")
    assert meta.label == "mylabel"
    assert meta.filepath == file
    assert meta.name == "test.py"
    assert meta.ext == ".py"
    assert meta.size.endswith("bytes")
    assert meta.lang == "python"
    assert isinstance(meta.modified, str)


def test_file_content_service_valid(tmp_path):
    file = tmp_path / "test.txt"
    file.write_text("hello world")

    meta_service = FileMetadataService(
        size_formatter=DummySizeFormatter(), datetime_formatter=DummyDateTimeFormatter()
    )
    content_service = FileContentService(meta_service, max_size_bytes=100)

    payload = content_service.get_payload(file, key="myfile", label="lbl")
    assert "myfile" in payload
    assert payload["myfile"]["label"] == "lbl"
    assert payload["myfile"]["name"] == "test.txt"
    assert payload["myfile"]["ext"] == ".txt"
    assert payload["myfile"]["size"].endswith("bytes")
    assert payload["myfile"]["lang"] == "text"
    assert isinstance(payload["myfile"]["modified"], str)
    assert payload["myfile"]["content"] == "hello world"


def test_file_content_service_too_large(tmp_path):
    file = tmp_path / "big.txt"
    file.write_text("x" * 200)

    meta_service = FileMetadataService(
        size_formatter=DummySizeFormatter(), datetime_formatter=DummyDateTimeFormatter()
    )
    content_service = FileContentService(meta_service, max_size_bytes=100)

    with pytest.raises(ValueError, match=r"is too large"):
        content_service.get_payload(file, key="file")


def test_file_content_service_nonexistent(tmp_path):
    file = tmp_path / "nofile.txt"

    meta_service = FileMetadataService(
        size_formatter=DummySizeFormatter(), datetime_formatter=DummyDateTimeFormatter()
    )
    content_service = FileContentService(meta_service, max_size_bytes=100)

    payload = content_service.get_payload(file, key="file")
    assert payload == {}


def test_file_content_service_no_key(tmp_path):
    file = tmp_path / "test.txt"
    file.write_text("hello world")

    meta_service = FileMetadataService(
        size_formatter=DummySizeFormatter(), datetime_formatter=DummyDateTimeFormatter()
    )
    content_service = FileContentService(meta_service, max_size_bytes=100)

    payload = content_service.get_payload(file, key=None, label="lbl")
    assert payload["label"] == "lbl"
    assert payload["name"] == "test.txt"
    assert payload["ext"] == ".txt"
    assert payload["size"].endswith("bytes")
    assert payload["lang"] == "text"
    assert isinstance(payload["modified"], str)
    assert payload["content"] == "hello world"


def test_file_content_service_replaces_invalid_utf8(tmp_path):
    file = tmp_path / "bad_utf8.txt"
    file.write_bytes(b"ok-\xff-end")

    meta_service = FileMetadataService(
        size_formatter=DummySizeFormatter(), datetime_formatter=DummyDateTimeFormatter()
    )
    content_service = FileContentService(meta_service, max_size_bytes=100)

    payload = content_service.get_payload(file, key=None)
    assert payload["content"] == "ok-\ufffd-end"


def test_file_content_service_is_valid_size_delegates_to_policy(tmp_path):
    file = tmp_path / "small.txt"
    file.write_text("abc")
    meta_service = FileMetadataService(
        size_formatter=DummySizeFormatter(), datetime_formatter=DummyDateTimeFormatter()
    )

    assert FileContentService(meta_service, max_size_bytes=10).is_valid_size(file)
    assert not FileContentService(meta_service, max_size_bytes=2).is_valid_size(file)


def test_file_size_policy_raises_with_current_error_contract(tmp_path):
    file = tmp_path / "big.txt"
    file.write_text("x" * 10)
    policy = FileSizePolicy(max_size_bytes=5)

    with pytest.raises(
        ValueError,
        match=rf"^File {file} is too large \(10 bytes, max 5 bytes\)$",
    ):
        policy.ensure_within_limit(file)


def test_file_reader_decode_replacement(tmp_path):
    file = tmp_path / "binary.bin"
    file.write_bytes(b"a\xffb")

    assert FileReader().read(file) == "a\ufffdb"


def test_payload_assembler_returns_file_payload():
    assembler = PayloadAssembler()
    dummy_path = Path("dummy.txt")

    payload = assembler.assemble(
        FileMeta(
            label="l",
            filepath=dummy_path,
            name="dummy.txt",
            ext=".txt",
            size="1 bytes",
            lang="text",
            modified="2024-01-01 00:00:00",
        ),
        "content",
    )
    assert payload.meta.name == "dummy.txt"
    assert payload.content == "content"
