"""Tests for BaseExporter, ExportResult, ExportOptions, and BaseFileVerifier."""

import hashlib
import tempfile
from pathlib import Path

import pytest

from local_deep_research.exporters.base import (
    BaseExporter,
    ExportOptions,
    ExportResult,
)
from local_deep_research.security.file_integrity.base_verifier import (
    BaseFileVerifier,
    FileType,
)


# --- Concrete subclasses for testing abstract bases ---


class StubExporter(BaseExporter):
    @property
    def format_name(self) -> str:
        return "stub"

    @property
    def file_extension(self) -> str:
        return ".stub"

    @property
    def mimetype(self) -> str:
        return "application/x-stub"

    def export(self, markdown_content, options=None):
        return ExportResult(
            content=markdown_content.encode(),
            filename="out.stub",
            mimetype=self.mimetype,
        )


class StubVerifier(BaseFileVerifier):
    def should_verify(self, file_path: Path) -> bool:
        return file_path.suffix == ".stub"

    def get_file_type(self) -> FileType:
        return FileType.EXPORT

    def allows_modifications(self) -> bool:
        return False


# --- ExportResult tests ---


class TestExportResult:
    def test_fields(self):
        result = ExportResult(
            content=b"hello", filename="f.txt", mimetype="text/plain"
        )
        assert result.content == b"hello"
        assert result.filename == "f.txt"
        assert result.mimetype == "text/plain"


# --- ExportOptions tests ---


class TestExportOptions:
    def test_defaults(self):
        opts = ExportOptions()
        assert opts.title is None
        assert opts.metadata is None
        assert opts.custom_options == {}

    def test_with_values(self):
        opts = ExportOptions(
            title="My Report",
            metadata={"author": "Alice"},
            custom_options={"css": "body{}"},
        )
        assert opts.title == "My Report"
        assert opts.metadata == {"author": "Alice"}
        assert opts.custom_options == {"css": "body{}"}


# --- BaseExporter._validate_content_size tests ---


class TestValidateContentSize:
    def test_under_limit_no_error(self):
        exporter = StubExporter()
        exporter._validate_content_size("short content")

    def test_over_limit_raises(self):
        exporter = StubExporter()
        oversized = "x" * (exporter.MAX_CONTENT_SIZE + 1)
        with pytest.raises(ValueError, match="exceeds maximum size"):
            exporter._validate_content_size(oversized)

    def test_exactly_at_limit_no_error(self):
        exporter = StubExporter()
        exact = "x" * exporter.MAX_CONTENT_SIZE
        exporter._validate_content_size(exact)


# --- BaseExporter._generate_safe_filename tests ---


class TestGenerateSafeFilename:
    def test_with_title(self):
        exporter = StubExporter()
        filename = exporter._generate_safe_filename("My Report")
        assert filename == "My_Report.stub"

    def test_without_title(self):
        exporter = StubExporter()
        assert exporter._generate_safe_filename(None) == "research_report.stub"
        assert exporter._generate_safe_filename("") == "research_report.stub"

    def test_special_chars_stripped(self):
        exporter = StubExporter()
        filename = exporter._generate_safe_filename("Hello! @World# (2024)")
        # Only word chars, spaces, hyphens survive; spaces become underscores
        assert "!" not in filename
        assert "@" not in filename
        assert "#" not in filename
        assert filename.endswith(".stub")

    def test_long_title_truncated(self):
        exporter = StubExporter()
        long_title = "A" * 100
        filename = exporter._generate_safe_filename(long_title)
        # safe_title is truncated to 50 chars, plus extension
        assert filename == "A" * 50 + ".stub"


# --- BaseExporter._prepend_title_if_needed tests ---


class TestPrependTitleIfNeeded:
    def test_no_title_returns_content(self):
        exporter = StubExporter()
        content = "Some body text"
        assert exporter._prepend_title_if_needed(content, None) == content
        assert exporter._prepend_title_if_needed(content, "") == content

    def test_content_starts_with_same_title(self):
        exporter = StubExporter()
        content = "# My Title\n\nBody text"
        result = exporter._prepend_title_if_needed(content, "My Title")
        assert result == content  # unchanged

    def test_content_starts_with_different_heading(self):
        exporter = StubExporter()
        content = "## Section One\n\nBody text"
        result = exporter._prepend_title_if_needed(content, "My Title")
        # Has a heading already, so title is NOT prepended
        assert result == content

    def test_content_without_heading_gets_title(self):
        exporter = StubExporter()
        content = "Just some plain text"
        result = exporter._prepend_title_if_needed(content, "My Title")
        assert result == "# My Title\n\nJust some plain text"

    def test_content_with_leading_whitespace_and_heading(self):
        exporter = StubExporter()
        content = "  # Existing Heading\n\nBody"
        result = exporter._prepend_title_if_needed(content, "New Title")
        # lstrip() finds '#', so title is NOT prepended
        assert result == content


# --- BaseFileVerifier tests ---


class TestBaseFileVerifier:
    def test_calculate_checksum(self):
        verifier = StubVerifier()
        with tempfile.NamedTemporaryFile(delete=False, suffix=".stub") as f:
            f.write(b"test data for checksum")
            f.flush()
            tmp_path = Path(f.name)

        try:
            result = verifier.calculate_checksum(tmp_path)
            expected = hashlib.sha256(b"test data for checksum").hexdigest()
            assert result == expected
        finally:
            tmp_path.unlink()

    def test_get_algorithm(self):
        verifier = StubVerifier()
        assert verifier.get_algorithm() == "sha256"

    def test_checksum_file_not_found(self):
        verifier = StubVerifier()
        with pytest.raises(FileNotFoundError):
            verifier.calculate_checksum(Path("/nonexistent/file.stub"))
