"""
Tests for wittrans

SPDX-FileCopyrightText: 2024 Jiri Grönroos <jiri.gronroos@iki.fi>

SPDX-License-Identifier: LGPL-3.0-or-later
"""

from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from wittrans.search import search_directory


@pytest.fixture
def mock_progress():
    """Create a mock Progress object with the required methods."""

    class MockProgress:
        def __init__(self):
            self.tasks = {}
            self.advance_calls = 0

        def add_task(self, description, total=None):
            task_id = "task_id"
            self.tasks[task_id] = {
                "description": description,
                "total": total,
                "completed": 0,
            }
            return task_id

        def advance(self, task_id, advance=1):
            self.advance_calls += 1
            self.tasks[task_id]["completed"] += advance

    return MockProgress()


@pytest.fixture
def mock_pool():
    """Create a mock Pool for multiprocessing."""
    pool = MagicMock()

    def mock_imap(func, args_list):
        """Mock imap that processes items sequentially."""
        return [func(args) for args in args_list]

    pool.imap = mock_imap
    pool.__enter__ = MagicMock(return_value=pool)
    pool.__exit__ = MagicMock(return_value=None)
    return pool


def test_search_directory_empty(monkeypatch, mock_progress):
    """Test search with empty directory."""
    # Mock find_mo_files to return an empty list
    monkeypatch.setattr("wittrans.search.find_mo_files", lambda path: [])

    results, mo_count = search_directory(Path("/test"), "search", mock_progress)

    assert results == []
    assert mo_count == 0
    assert mock_progress.advance_calls == 0
    assert len(mock_progress.tasks) == 0  # No task created for empty directories


def test_search_directory_no_matches(monkeypatch, mock_progress, mock_pool):
    """Test search with files but no matches."""
    files = [Path("/test/file1.mo"), Path("/test/file2.mo")]

    monkeypatch.setattr("wittrans.search.find_mo_files", lambda path: files)

    def mock_search_mo_file(
        file_path, term, search_source=True, search_translation=True, whole_word=False
    ):
        return []

    with patch("wittrans.search.Pool", return_value=mock_pool):
        with patch("wittrans.search.search_mo_file", side_effect=mock_search_mo_file):
            results, mo_count = search_directory(Path("/test"), "search", mock_progress)

    assert results == []
    assert mo_count == 2
    assert mock_progress.advance_calls == 2
    assert len(mock_progress.tasks) == 1
    assert list(mock_progress.tasks.values())[0]["total"] == 2


def test_search_directory_with_matches(monkeypatch, mock_progress, mock_pool):
    """Test search with matches found."""
    file1 = Path("/test/file1.mo")
    file2 = Path("/test/file2.mo")
    files = [file1, file2]

    matches1 = [("original1", "translated1", None)]
    matches2 = [("original2", "translated2", "context")]

    monkeypatch.setattr("wittrans.search.find_mo_files", lambda path: files)

    def mock_search_mo_file(
        file_path, term, search_source=True, search_translation=True, whole_word=False
    ):
        if file_path == file1:
            return matches1
        elif file_path == file2:
            return matches2
        return []

    with patch("wittrans.search.Pool", return_value=mock_pool):
        with patch("wittrans.search.search_mo_file", side_effect=mock_search_mo_file):
            results, mo_count = search_directory(Path("/test"), "search", mock_progress)

    assert len(results) == 2
    assert results[0] == (file1, matches1)
    assert results[1] == (file2, matches2)
    assert mo_count == 2
    assert mock_progress.advance_calls == 2
    assert len(mock_progress.tasks) == 1
    assert list(mock_progress.tasks.values())[0]["total"] == 2


def test_search_directory_mixed_matches(monkeypatch, mock_progress, mock_pool):
    """Test search with some matches and some non-matches."""
    file1 = Path("/test/file1.mo")
    file2 = Path("/test/file2.mo")
    file3 = Path("/test/file3.mo")
    files = [file1, file2, file3]

    matches1 = [("original1", "translated1", None)]
    matches3 = [("original3", "translated3", "context")]

    monkeypatch.setattr("wittrans.search.find_mo_files", lambda path: files)

    def mock_search_mo_file(
        file_path, term, search_source=True, search_translation=True, whole_word=False
    ):
        if file_path == file1:
            return matches1
        elif file_path == file3:
            return matches3
        return []

    with patch("wittrans.search.Pool", return_value=mock_pool):
        with patch("wittrans.search.search_mo_file", side_effect=mock_search_mo_file):
            results, mo_count = search_directory(Path("/test"), "search", mock_progress)

    assert len(results) == 2
    assert results[0] == (file1, matches1)
    assert results[1] == (file3, matches3)
    assert mo_count == 3
    assert mock_progress.advance_calls == 3
    assert len(mock_progress.tasks) == 1
    assert list(mock_progress.tasks.values())[0]["total"] == 3


def test_search_directory_source_only(monkeypatch, mock_progress, mock_pool):
    """Test search with source-only flag."""
    file1 = Path("/test/file1.mo")
    files = [file1]
    matches1 = [("original1", "translated1", None)]

    monkeypatch.setattr("wittrans.search.find_mo_files", lambda path: files)

    def mock_search_mo_file(
        file_path, term, search_source=True, search_translation=True, whole_word=False
    ):
        # Verify that search_translation is False when searching source only
        assert search_source is True
        assert search_translation is False
        return matches1

    with patch("wittrans.search.Pool", return_value=mock_pool):
        with patch("wittrans.search.search_mo_file", side_effect=mock_search_mo_file):
            results, mo_count = search_directory(
                Path("/test"),
                "search",
                mock_progress,
                search_source=True,
                search_translation=False,
            )

    assert len(results) == 1
    assert results[0] == (file1, matches1)
    assert mo_count == 1


def test_search_directory_translation_only(monkeypatch, mock_progress, mock_pool):
    """Test search with translation-only flag."""
    file1 = Path("/test/file1.mo")
    files = [file1]
    matches1 = [("original1", "translated1", None)]

    monkeypatch.setattr("wittrans.search.find_mo_files", lambda path: files)

    def mock_search_mo_file(
        file_path, term, search_source=True, search_translation=True, whole_word=False
    ):
        # Verify that search_source is False when searching translation only
        assert search_source is False
        assert search_translation is True
        return matches1

    with patch("wittrans.search.Pool", return_value=mock_pool):
        with patch("wittrans.search.search_mo_file", side_effect=mock_search_mo_file):
            results, mo_count = search_directory(
                Path("/test"),
                "search",
                mock_progress,
                search_source=False,
                search_translation=True,
            )

    assert len(results) == 1
    assert results[0] == (file1, matches1)
    assert mo_count == 1


def test_search_directory_whole_word(monkeypatch, mock_progress, mock_pool):
    """Test search with whole-word flag."""
    file1 = Path("/test/file1.mo")
    files = [file1]
    matches1 = [("original1", "translated1", None)]

    monkeypatch.setattr("wittrans.search.find_mo_files", lambda path: files)

    def mock_search_mo_file(
        file_path, term, search_source=True, search_translation=True, whole_word=False
    ):
        # Verify that whole_word is True
        assert whole_word is True
        return matches1

    with patch("wittrans.search.Pool", return_value=mock_pool):
        with patch("wittrans.search.search_mo_file", side_effect=mock_search_mo_file):
            results, mo_count = search_directory(
                Path("/test"),
                "search",
                mock_progress,
                search_source=True,
                search_translation=True,
                whole_word=True,
            )

    assert len(results) == 1
    assert results[0] == (file1, matches1)
    assert mo_count == 1
