"""Tests for PDF split functionality."""

import logging
import shutil
import sys
import tempfile
from pathlib import Path

import fitz
import pytest

sys.path.insert(0, str(Path(__file__).parent.parent))

from sfi.pdfsplit.pdfsplit import parse_page_ranges, split_by_number, split_by_range, split_by_size

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)


@pytest.fixture
def temp_dir():
    """Create a temporary directory for test files."""
    dir_path = Path(tempfile.mkdtemp())
    yield dir_path
    # Cleanup
    shutil.rmtree(dir_path, ignore_errors=True)


@pytest.fixture
def sample_pdf_10(temp_dir):
    """Create a 10-page PDF for testing."""
    pdf_path = temp_dir / "sample_10.pdf"
    doc = fitz.open()
    for i in range(10):
        page = doc.new_page()  # pyright: ignore[reportAttributeAccessIssue]
        text = fitz.Point(50, 72)
        page.insert_text(text, f"This is page {i + 1}", fontsize=12)
    doc.save(pdf_path)
    doc.close()
    logger.debug(f"Created test PDF: {pdf_path} with 10 pages")
    return pdf_path


@pytest.fixture
def sample_pdf_5(temp_dir):
    """Create a 5-page PDF for testing."""
    pdf_path = temp_dir / "sample_5.pdf"
    doc = fitz.open()
    for i in range(5):
        page = doc.new_page()  # pyright: ignore[reportAttributeAccessIssue]
        text = fitz.Point(50, 72)
        page.insert_text(text, f"This is page {i + 1}", fontsize=12)
    doc.save(pdf_path)
    doc.close()
    logger.debug(f"Created test PDF: {pdf_path} with 5 pages")
    return pdf_path


def get_page_count(pdf_path: Path) -> int:
    """Get the number of pages in a PDF."""
    doc = fitz.open(pdf_path)
    count = doc.page_count
    doc.close()
    return count


def test_parse_page_ranges():
    """Test page range parsing function."""
    # Single pages
    assert parse_page_ranges("1,2,3", 10) == [1, 2, 3]

    # Range
    assert parse_page_ranges("1-5", 10) == [1, 2, 3, 4, 5]

    # Mixed
    assert parse_page_ranges("1,3,5-7", 10) == [1, 3, 5, 6, 7]

    # Open end range
    assert parse_page_ranges("5-", 10) == [5, 6, 7, 8, 9, 10]

    # Open start range
    assert parse_page_ranges("-5", 10) == [1, 2, 3, 4, 5]

    # Out of range pages are handled
    assert parse_page_ranges("1,2,20", 10) == [1, 2, 20]


def test_split_by_number_3_parts(sample_pdf_10, temp_dir):
    """Test splitting 10-page PDF into 3 parts."""
    output_file = temp_dir / "output.pdf"
    split_by_number(sample_pdf_10, output_file, 3)

    # Check that 3 parts were created
    parts = sorted(temp_dir.glob("output_part*.pdf"))
    assert len(parts) == 3

    # Check page counts: 10 pages split into 3 parts should be 4, 3, 3 pages
    page_counts = [get_page_count(p) for p in parts]
    assert page_counts == [4, 3, 3]

    logger.info(f"Split into 3 parts: {page_counts}")


def test_split_by_number_equal(sample_pdf_10, temp_dir):
    """Test splitting 10-page PDF into 5 parts."""
    output_file = temp_dir / "output.pdf"
    split_by_number(sample_pdf_10, output_file, 5)

    parts = sorted(temp_dir.glob("output_part*.pdf"))
    assert len(parts) == 5

    # Each part should have 2 pages
    page_counts = [get_page_count(p) for p in parts]
    assert all(count == 2 for count in page_counts)

    logger.info(f"Split into 5 equal parts: {page_counts}")


def test_split_by_number_more_parts_than_pages(sample_pdf_5, temp_dir):
    """Test splitting 5-page PDF into 10 parts."""
    output_file = temp_dir / "output.pdf"
    split_by_number(sample_pdf_5, output_file, 10)

    parts = sorted(temp_dir.glob("output_part*.pdf"))
    assert len(parts) == 5

    # Each part should have 1 page
    page_counts = [get_page_count(p) for p in parts]
    assert all(count == 1 for count in page_counts)

    logger.info(f"Split 5 pages into 10 parts: {page_counts}")


def test_split_by_size_3_pages(sample_pdf_10, temp_dir):
    """Test splitting PDF with 3 pages per part."""
    output_file = temp_dir / "output.pdf"
    split_by_size(sample_pdf_10, output_file, 3)

    parts = sorted(temp_dir.glob("output_part*.pdf"))
    assert len(parts) == 4

    # Page counts should be 3, 3, 3, 1
    page_counts = [get_page_count(p) for p in parts]
    assert page_counts == [3, 3, 3, 1]

    logger.info(f"Split by size 3: {page_counts}")


def test_split_by_size_1_page(sample_pdf_10, temp_dir):
    """Test splitting PDF with 1 page per part."""
    output_file = temp_dir / "output.pdf"
    split_by_size(sample_pdf_10, output_file, 1)

    parts = sorted(temp_dir.glob("output_part*.pdf"))
    assert len(parts) == 10

    # Each part should have 1 page
    page_counts = [get_page_count(p) for p in parts]
    assert all(count == 1 for count in page_counts)

    logger.info(f"Split by size 1: {page_counts}")


def test_split_by_size_exact_division(sample_pdf_10, temp_dir):
    """Test splitting PDF with exact division."""
    output_file = temp_dir / "output.pdf"
    split_by_size(sample_pdf_10, output_file, 5)

    parts = sorted(temp_dir.glob("output_part*.pdf"))
    assert len(parts) == 2

    # Each part should have 5 pages
    page_counts = [get_page_count(p) for p in parts]
    assert page_counts == [5, 5]

    logger.info(f"Split by size 5 (exact): {page_counts}")


def test_split_by_range_single_pages(sample_pdf_10, temp_dir):
    """Test extracting single pages."""
    output_file = temp_dir / "extracted.pdf"
    split_by_range(sample_pdf_10, output_file, "1,3,5,7,9")

    assert output_file.exists()
    assert get_page_count(output_file) == 5

    # Verify the content (optional, just checking page count)
    logger.info("Extracted 5 pages: 1,3,5,7,9")


def test_split_by_range_with_dash(sample_pdf_10, temp_dir):
    """Test extracting page ranges."""
    output_file = temp_dir / "extracted.pdf"
    split_by_range(sample_pdf_10, output_file, "1-3,5-7,9-10")

    assert output_file.exists()
    assert get_page_count(output_file) == 8  # Pages 1,2,3,5,6,7,9,10

    logger.info("Extracted 8 pages with ranges: 1-3,5-7,9-10")


def test_split_by_range_open_end(sample_pdf_10, temp_dir):
    """Test extracting from a page to the end."""
    output_file = temp_dir / "extracted.pdf"
    split_by_range(sample_pdf_10, output_file, "5-")

    assert output_file.exists()
    assert get_page_count(output_file) == 6  # Pages 5,6,7,8,9,10

    logger.info("Extracted pages 5- (6 pages)")


def test_split_by_range_open_start(sample_pdf_10, temp_dir):
    """Test extracting from start to a page."""
    output_file = temp_dir / "extracted.pdf"
    split_by_range(sample_pdf_10, output_file, "-3")

    assert output_file.exists()
    assert get_page_count(output_file) == 3  # Pages 1,2,3

    logger.info("Extracted pages -3 (3 pages)")


def test_split_by_range_complex(sample_pdf_10, temp_dir):
    """Test complex page range."""
    output_file = temp_dir / "extracted.pdf"
    split_by_range(sample_pdf_10, output_file, "1,2,4-6,8-")

    assert output_file.exists()
    assert get_page_count(output_file) == 8  # Pages 1,2,4,5,6,8,9,10

    logger.info("Extracted complex range: 1,2,4-6,8-")


def test_split_by_range_out_of_range(sample_pdf_5, temp_dir):
    """Test extracting pages beyond the PDF length."""
    output_file = temp_dir / "extracted.pdf"
    split_by_range(sample_pdf_5, output_file, "1,3,10,20")

    assert output_file.exists()
    assert get_page_count(output_file) == 2  # Only pages 1 and 3 exist

    logger.info("Extracted pages with some out of range: 1,3,10,20")


def test_split_by_range_all_pages(sample_pdf_10, temp_dir):
    """Test extracting all pages."""
    output_file = temp_dir / "extracted.pdf"
    split_by_range(sample_pdf_10, output_file, "1-10")

    assert output_file.exists()
    assert get_page_count(output_file) == 10

    logger.info("Extracted all 10 pages")


def test_split_by_range_duplicate_pages(sample_pdf_10, temp_dir):
    """Test extracting with duplicate page specifications."""
    output_file = temp_dir / "extracted.pdf"
    split_by_range(sample_pdf_10, output_file, "1,2,1,2,3-5,3-5")

    assert output_file.exists()
    # Should deduplicate: 1,2,3,4,5
    assert get_page_count(output_file) == 5

    logger.info("Extracted with duplicates (should deduplicate): 1,2,1,2,3-5,3-5")


def test_output_file_naming(sample_pdf_10, temp_dir):
    """Test that output files are named correctly."""
    output_file = temp_dir / "test_output.pdf"
    split_by_number(sample_pdf_10, output_file, 3)

    # Check that parts are named correctly
    parts = sorted(temp_dir.glob("test_output_part*.pdf"))
    assert len(parts) == 3

    # Check file names
    filenames = [p.name for p in parts]
    assert filenames == ["test_output_part1.pdf", "test_output_part2.pdf", "test_output_part3.pdf"]

    logger.info(f"File naming test passed: {filenames}")


def test_large_pdf(temp_dir):
    """Test with a larger PDF (50 pages)."""
    # Create 50-page PDF
    pdf_path = temp_dir / "large_50.pdf"
    doc = fitz.open()
    for i in range(50):
        page = doc.new_page()  # pyright: ignore[reportAttributeAccessIssue]
        text = fitz.Point(50, 72)
        page.insert_text(text, f"This is page {i + 1}", fontsize=12)
    doc.save(pdf_path)
    doc.close()

    # Test split by size (10 pages each)
    output_file = temp_dir / "large_output.pdf"
    split_by_size(pdf_path, output_file, 10)

    parts = sorted(temp_dir.glob("large_output_part*.pdf"))
    assert len(parts) == 5

    # Each part should have 10 pages
    page_counts = [get_page_count(p) for p in parts]
    assert all(count == 10 for count in page_counts)

    logger.info("Large PDF (50 pages) split into 5 parts of 10 pages each")


def test_edge_case_single_page_pdf(temp_dir):
    """Test with a single-page PDF."""
    pdf_path = temp_dir / "single.pdf"
    doc = fitz.open()
    page = doc.new_page()  # pyright: ignore[reportAttributeAccessIssue]
    text = fitz.Point(50, 72)
    page.insert_text(text, "Single page", fontsize=12)
    doc.save(pdf_path)
    doc.close()

    # Split by number (should work)
    output_file = temp_dir / "single_output.pdf"
    split_by_number(pdf_path, output_file, 2)

    parts = sorted(temp_dir.glob("single_output_part*.pdf"))
    assert len(parts) == 1
    assert get_page_count(parts[0]) == 1

    logger.info("Single-page PDF handled correctly")


if __name__ == "__main__":
    pytest.main([__file__, "-v", "-s"])
