# #region Test.DictionaryCorrectionService [C:3] [TYPE Module] [SEMANTICS test,dictionary,correction,bulk]
# @BRIEF Tests for dictionary_correction.py — DictionaryCorrectionService.
# @RELATION BINDS_TO -> [DictionaryCorrectionService]
# @TEST_EDGE: dict_not_found -> ValueError
# @TEST_EDGE: conflict_keep_existing -> conflict_detected
# @TEST_EDGE: conflict_cancel -> skipped

import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))

import pytest
from unittest.mock import MagicMock

from src.models.translate import DictionaryEntry, TerminologyDictionary
from src.plugins.translate.dictionary_correction import DictionaryCorrectionService
from .conftest import JOB_ID


class TestSubmitCorrection:
    """DictionaryCorrectionService.submit_correction."""

    def _setup_dict(self, db_session):
        d = TerminologyDictionary(id="dict-corr-1", name="Test Dict", is_active=True)
        db_session.add(d)
        db_session.commit()
        return d.id

    def test_create_new(self, db_session):
        """Creates a new entry."""
        dict_id = self._setup_dict(db_session)
        result = DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut",
        )
        assert result["action"] == "created"
        assert result["entry_id"] is not None

    def test_dict_not_found(self, db_session):
        """Non-existent dictionary raises ValueError."""
        with pytest.raises(ValueError, match="not found"):
            DictionaryCorrectionService.submit_correction(
                db_session, "nonexistent", "hello", "bonjour", "salut",
            )

    def test_conflict_overwrite(self, db_session):
        """Overwrite existing entry."""
        dict_id = self._setup_dict(db_session)
        DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut",
        )
        result = DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut2",
            on_conflict="overwrite",
        )
        assert result["action"] == "updated"
        entry = db_session.query(DictionaryEntry).filter(DictionaryEntry.id == result["entry_id"]).first()
        assert entry.target_term == "salut2"

    def test_conflict_keep_existing(self, db_session):
        """Keep existing entry."""
        dict_id = self._setup_dict(db_session)
        DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut",
        )
        result = DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut2",
            on_conflict="keep_existing",
        )
        assert result["action"] == "conflict_detected"
        assert result["conflict"]["action"] == "keep_existing"

    def test_conflict_cancel(self, db_session):
        """Cancel on conflict."""
        dict_id = self._setup_dict(db_session)
        DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut",
        )
        result = DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut2",
            on_conflict="cancel",
        )
        assert result["action"] == "skipped"

    def test_origin_fields(self, db_session):
        """Origin fields are stored."""
        dict_id = self._setup_dict(db_session)
        result = DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut",
            origin_run_id="run-1", origin_row_key="row-1", origin_user_id="user-1",
        )
        entry = db_session.query(DictionaryEntry).filter(DictionaryEntry.id == result["entry_id"]).first()
        assert entry.origin_run_id == "run-1"
        assert entry.origin_row_key == "row-1"
        assert entry.origin_user_id == "user-1"

    def test_context_data(self, db_session):
        """Context data is stored."""
        dict_id = self._setup_dict(db_session)
        result = DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut",
            context_data={"table": "test"},
        )
        entry = db_session.query(DictionaryEntry).filter(DictionaryEntry.id == result["entry_id"]).first()
        assert entry.context_data == {"table": "test"}
        assert entry.has_context is True

    def test_keep_context_false(self, db_session):
        """keep_context=False clears context."""
        dict_id = self._setup_dict(db_session)
        result = DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut",
            context_data={"table": "test"}, keep_context=False,
        )
        entry = db_session.query(DictionaryEntry).filter(DictionaryEntry.id == result["entry_id"]).first()
        assert entry.has_context is False
        # Production code sets context_source to None when effective_context is None
        assert entry.context_source is None

    def test_usage_notes(self, db_session):
        """Usage notes are stored."""
        dict_id = self._setup_dict(db_session)
        result = DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut",
            usage_notes="formal greeting",
        )
        entry = db_session.query(DictionaryEntry).filter(DictionaryEntry.id == result["entry_id"]).first()
        assert entry.usage_notes == "formal greeting"

    def test_overwrite_preserves_existing_fields(self, db_session):
        """Overwrite updates fields but keeps entry_id."""
        dict_id = self._setup_dict(db_session)
        result1 = DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut",
            origin_run_id="run-1",
        )
        result2 = DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut2",
            on_conflict="overwrite", origin_run_id="run-2",
        )
        assert result2["entry_id"] == result1["entry_id"]


class TestSubmitBulkCorrections:
    """DictionaryCorrectionService.submit_bulk_corrections."""

    def _setup_dict(self, db_session):
        d = TerminologyDictionary(id="dict-bulk-1", name="Test Dict", is_active=True)
        db_session.add(d)
        db_session.commit()
        return d.id

    def test_all_created(self, db_session):
        """All corrections created successfully."""
        dict_id = self._setup_dict(db_session)
        corrections = [
            {"source_term": "hello", "corrected_target_term": "bonjour"},
            {"source_term": "world", "corrected_target_term": "monde"},
        ]
        result = DictionaryCorrectionService.submit_bulk_corrections(
            db_session, dict_id, corrections,
        )
        assert result["status"] == "completed"
        assert result["applied"] == 2

    def test_empty_fields_skipped(self, db_session):
        """Empty source_term or corrected_target_term are skipped."""
        dict_id = self._setup_dict(db_session)
        corrections = [
            {"source_term": "", "corrected_target_term": "bonjour"},
            {"source_term": "hello", "corrected_target_term": ""},
            {"source_term": "valid", "corrected_target_term": "valide"},
        ]
        result = DictionaryCorrectionService.submit_bulk_corrections(
            db_session, dict_id, corrections,
        )
        assert result["status"] == "completed"
        assert result["applied"] == 1

    def test_dict_not_found(self, db_session):
        """Non-existent dictionary raises ValueError."""
        with pytest.raises(ValueError, match="not found"):
            DictionaryCorrectionService.submit_bulk_corrections(db_session, "nonexistent", [])

    def test_bulk_overwrite(self, db_session):
        """Existing entries are overwritten."""
        dict_id = self._setup_dict(db_session)
        DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut",
        )
        corrections = [
            {"source_term": "hello", "corrected_target_term": "salut2"},
        ]
        result = DictionaryCorrectionService.submit_bulk_corrections(
            db_session, dict_id, corrections,
        )
        assert result["applied"] == 1
        assert result["results"][0]["action"] == "updated"

    def test_bulk_keep_existing(self, db_session):
        """Keep existing entries on conflict."""
        dict_id = self._setup_dict(db_session)
        DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut",
        )
        corrections = [
            {"source_term": "hello", "corrected_target_term": "salut2", "on_conflict": "keep_existing"},
        ]
        result = DictionaryCorrectionService.submit_bulk_corrections(
            db_session, dict_id, corrections,
        )
        assert result["results"][0]["action"] == "conflict_detected"

    def test_bulk_cancel(self, db_session):
        """Cancel on conflict in bulk."""
        dict_id = self._setup_dict(db_session)
        DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut",
        )
        corrections = [
            {"source_term": "hello", "corrected_target_term": "salut2", "on_conflict": "cancel"},
        ]
        result = DictionaryCorrectionService.submit_bulk_corrections(
            db_session, dict_id, corrections,
        )
        assert result["results"][0]["action"] == "skipped"

    def test_rollback_on_keep_existing(self, db_session):
        """Rollback when keep_existing and not all_ok."""
        dict_id = self._setup_dict(db_session)
        DictionaryCorrectionService.submit_correction(
            db_session, dict_id, "hello", "bonjour", "salut",
        )
        # Mix valid and empty to trigger rollback path
        corrections = [
            {"source_term": "hello", "corrected_target_term": "salut2",
             "on_conflict": "keep_existing"},
            {"source_term": "", "corrected_target_term": "empty"},
        ]
        result = DictionaryCorrectionService.submit_bulk_corrections(
            db_session, dict_id, corrections,
        )
        assert result["status"] == "conflicts"
# #endregion Test.DictionaryCorrectionService
