"""Tests for PDF to image conversion functionality."""

import logging
import shutil
import tempfile
from pathlib import Path
from typing import Generator

import fitz
import pytest

from pytola_office.pdf2img.cli import PdfToImageRunner, parse_page_ranges

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


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
    """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: Path) -> Path:
    """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: Path) -> Path:
    """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_image_count(directory: Path, prefix: str) -> int:
    """Get the number of image files in a directory with given prefix."""
    images = list(directory.glob(f"{prefix}_page*.png"))
    images.extend(list(directory.glob(f"{prefix}_page*.jpg")))
    images.extend(list(directory.glob(f"{prefix}_page*.jpeg")))
    images.extend(list(directory.glob(f"{prefix}_page*.webp")))
    return len(images)


def test_parse_page_ranges() -> None:
    """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 filtered later
    result = parse_page_ranges("1,2,20", 10)
    assert 20 in result  # Parsing includes it, filtering happens later


def test_convert_all_pages_png(sample_pdf_10, temp_dir) -> None:
    """Test converting all pages to PNG format."""
    output_dir = temp_dir / "output_png"
    runner = PdfToImageRunner(
        input_pdf=sample_pdf_10,
        output_dir=output_dir,
        dpi=150,
        img_format="png",
    )
    runner.run()

    # Check that 10 images were created
    image_count = get_image_count(output_dir, "sample_10")
    assert image_count == 10

    # Check that files exist and have content
    images = sorted(output_dir.glob("sample_10_page*.png"))
    assert len(images) == 10
    assert all(img.stat().st_size > 0 for img in images)

    logger.info(f"Converted 10 pages to PNG: {image_count} images")


def test_convert_with_dpi(sample_pdf_5, temp_dir) -> None:
    """Test conversion with different DPI settings."""
    # Low DPI
    output_low = temp_dir / "output_low_dpi"
    runner_low = PdfToImageRunner(
        input_pdf=sample_pdf_5,
        output_dir=output_low,
        dpi=72,
        img_format="png",
    )
    runner_low.run()

    # High DPI
    output_high = temp_dir / "output_high_dpi"
    runner_high = PdfToImageRunner(
        input_pdf=sample_pdf_5,
        output_dir=output_high,
        dpi=300,
        img_format="png",
    )
    runner_high.run()

    # Both should create 5 images
    assert get_image_count(output_low, "sample_5") == 5
    assert get_image_count(output_high, "sample_5") == 5

    # High DPI images should be larger
    low_images = sorted(output_low.glob("sample_5_page*.png"))
    high_images = sorted(output_high.glob("sample_5_page*.png"))

    for low_img, high_img in zip(low_images, high_images):
        assert high_img.stat().st_size > low_img.stat().st_size

    logger.info("DPI test passed: high DPI produces larger files")


def test_convert_jpg_format(sample_pdf_5, temp_dir) -> None:
    """Test converting to JPG format."""
    output_dir = temp_dir / "output_jpg"
    runner = PdfToImageRunner(
        input_pdf=sample_pdf_5,
        output_dir=output_dir,
        dpi=150,
        img_format="jpg",
        quality=85,
    )
    runner.run()

    # Check that 5 JPG images were created
    image_count = get_image_count(output_dir, "sample_5")
    assert image_count == 5

    # Verify file extension
    jpg_images = sorted(output_dir.glob("sample_5_page*.jpg"))
    assert len(jpg_images) == 5

    logger.info("JPG conversion test passed")


def test_convert_page_range_single(sample_pdf_10, temp_dir) -> None:
    """Test converting specific single pages."""
    output_dir = temp_dir / "output_range_single"
    runner = PdfToImageRunner(
        input_pdf=sample_pdf_10,
        output_dir=output_dir,
        dpi=150,
        img_format="png",
        page_range="1,3,5,7,9",
    )
    runner.run()

    # Should create 5 images
    image_count = get_image_count(output_dir, "sample_10")
    assert image_count == 5

    # Verify specific pages exist
    expected_pages = [
        "sample_10_page001.png",
        "sample_10_page003.png",
        "sample_10_page005.png",
        "sample_10_page007.png",
        "sample_10_page009.png",
    ]
    for page_file in expected_pages:
        assert (output_dir / page_file).exists()

    logger.info("Single page range test passed")


def test_convert_page_range_continuous(sample_pdf_10, temp_dir) -> None:
    """Test converting a continuous range of pages."""
    output_dir = temp_dir / "output_range_continuous"
    runner = PdfToImageRunner(
        input_pdf=sample_pdf_10,
        output_dir=output_dir,
        dpi=150,
        img_format="png",
        page_range="3-7",
    )
    runner.run()

    # Should create 5 images (pages 3,4,5,6,7)
    image_count = get_image_count(output_dir, "sample_10")
    assert image_count == 5

    logger.info("Continuous page range test passed")


def test_convert_page_range_open_end(sample_pdf_10, temp_dir) -> None:
    """Test converting from a page to the end."""
    output_dir = temp_dir / "output_range_open_end"
    runner = PdfToImageRunner(
        input_pdf=sample_pdf_10,
        output_dir=output_dir,
        dpi=150,
        img_format="png",
        page_range="5-",
    )
    runner.run()

    # Should create 6 images (pages 5,6,7,8,9,10)
    image_count = get_image_count(output_dir, "sample_10")
    assert image_count == 6

    logger.info("Open-end page range test passed")


def test_convert_page_range_open_start(sample_pdf_10, temp_dir) -> None:
    """Test converting from start to a page."""
    output_dir = temp_dir / "output_range_open_start"
    runner = PdfToImageRunner(
        input_pdf=sample_pdf_10,
        output_dir=output_dir,
        dpi=150,
        img_format="png",
        page_range="-4",
    )
    runner.run()

    # Should create 4 images (pages 1,2,3,4)
    image_count = get_image_count(output_dir, "sample_10")
    assert image_count == 4

    logger.info("Open-start page range test passed")


def test_convert_page_range_complex(sample_pdf_10, temp_dir) -> None:
    """Test converting with complex page range."""
    output_dir = temp_dir / "output_range_complex"
    runner = PdfToImageRunner(
        input_pdf=sample_pdf_10,
        output_dir=output_dir,
        dpi=150,
        img_format="png",
        page_range="1,2,4-6,8-",
    )
    runner.run()

    # Should create 8 images (pages 1,2,4,5,6,8,9,10)
    image_count = get_image_count(output_dir, "sample_10")
    assert image_count == 8

    logger.info("Complex page range test passed")


def test_convert_page_range_out_of_range(sample_pdf_5, temp_dir) -> None:
    """Test converting with out-of-range page numbers."""
    output_dir = temp_dir / "output_range_invalid"
    runner = PdfToImageRunner(
        input_pdf=sample_pdf_5,
        output_dir=output_dir,
        dpi=150,
        img_format="png",
        page_range="1,3,10,20",
    )
    runner.run()

    # Should only convert existing pages (1 and 3)
    image_count = get_image_count(output_dir, "sample_5")
    assert image_count == 2

    logger.info("Out-of-range page test passed")


def test_convert_page_range_duplicate(sample_pdf_10, temp_dir) -> None:
    """Test converting with duplicate page specifications."""
    output_dir = temp_dir / "output_range_duplicate"
    runner = PdfToImageRunner(
        input_pdf=sample_pdf_10,
        output_dir=output_dir,
        dpi=150,
        img_format="png",
        page_range="1,2,1,2,3-5,3-5",
    )
    runner.run()

    # Should deduplicate: pages 1,2,3,4,5
    image_count = get_image_count(output_dir, "sample_10")
    assert image_count == 5

    logger.info("Duplicate page range test passed")


def test_convert_quality_setting(sample_pdf_5, temp_dir) -> None:
    """Test different quality settings for JPG."""
    # Low quality
    output_low = temp_dir / "output_low_quality"
    runner_low = PdfToImageRunner(
        input_pdf=sample_pdf_5,
        output_dir=output_low,
        dpi=150,
        img_format="jpg",
        quality=30,
    )
    runner_low.run()

    # High quality
    output_high = temp_dir / "output_high_quality"
    runner_high = PdfToImageRunner(
        input_pdf=sample_pdf_5,
        output_dir=output_high,
        dpi=150,
        img_format="jpg",
        quality=95,
    )
    runner_high.run()

    # High quality images should be larger
    low_images = sorted(output_low.glob("sample_5_page*.jpg"))
    high_images = sorted(output_high.glob("sample_5_page*.jpg"))

    for low_img, high_img in zip(low_images, high_images):
        assert high_img.stat().st_size > low_img.stat().st_size

    logger.info("Quality setting test passed")


def test_edge_case_single_page_pdf(temp_dir) -> None:
    """Test converting 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()

    output_dir = temp_dir / "single_output"
    runner = PdfToImageRunner(
        input_pdf=pdf_path,
        output_dir=output_dir,
        dpi=150,
        img_format="png",
    )
    runner.run()

    # Should create 1 image
    image_count = get_image_count(output_dir, "single")
    assert image_count == 1

    logger.info("Single-page PDF test passed")


def test_large_pdf(temp_dir) -> None:
    """Test converting 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()

    output_dir = temp_dir / "large_output"
    runner = PdfToImageRunner(
        input_pdf=pdf_path,
        output_dir=output_dir,
        dpi=150,
        img_format="png",
    )
    runner.run()

    # Should create 50 images
    image_count = get_image_count(output_dir, "large_50")
    assert image_count == 50

    logger.info("Large PDF (50 pages) conversion test passed")


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