# #region Test.InlineCorrectionService [C:3] [TYPE Module] [SEMANTICS test,inline,correction,dictionary]
# @BRIEF Tests for service_inline_correction.py — InlineCorrectionService.
# @RELATION BINDS_TO -> [InlineCorrectionService]
# @TEST_EDGE: lang_entry_not_found -> ValueError
# @TEST_EDGE: record_not_found -> ValueError
# @TEST_EDGE: no_source_text -> ValueError

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, patch

from src.models.translate import (
    DictionaryEntry,
    TerminologyDictionary,
    TranslationBatch,
    TranslationLanguage,
    TranslationRecord,
    TranslationRun,
)
from src.plugins.translate.service_inline_correction import InlineCorrectionService
from .conftest import JOB_ID


def _setup_run_and_batch(db_session, run_id="run-ie", batch_id="batch-1"):
    """Create minimal run + batch for FK constraints."""
    run = TranslationRun(id=run_id, job_id=JOB_ID, status="RUNNING")
    db_session.add(run)
    db_session.flush()
    batch = TranslationBatch(id=batch_id, run_id=run_id, batch_index=0, status="PENDING")
    db_session.add(batch)
    db_session.flush()
    return run_id, batch_id


class TestApplyInlineEdit:
    """InlineCorrectionService.apply_inline_edit."""

    def _setup(self, db_session, run_id="run-ie"):
        _setup_run_and_batch(db_session, run_id)
        record = TranslationRecord(
            id="rec-ie-1", run_id=run_id, batch_id="batch-1",
            source_sql="hello", target_sql="bonjour", status="SUCCESS",
        )
        db_session.add(record)
        db_session.flush()

        lang = TranslationLanguage(
            record_id="rec-ie-1", language_code="fr",
            translated_value="bonjour", final_value="bonjour",
            status="translated",
        )
        db_session.add(lang)
        db_session.commit()
        return record, lang

    def test_apply_basic(self, db_session):
        """Basic inline edit updates final_value and user_edit."""
        record, lang = self._setup(db_session)
        result = InlineCorrectionService.apply_inline_edit(
            db_session, "run-ie", "rec-ie-1", "fr", "salut"
        )
        assert result["final_value"] == "salut"
        assert result["user_edit"] == "salut"
        db_session.refresh(lang)
        assert lang.final_value == "salut"

    def test_lang_entry_not_found(self, db_session):
        """Non-existent language entry raises ValueError."""
        with pytest.raises(ValueError, match="not found"):
            InlineCorrectionService.apply_inline_edit(
                db_session, "run-ie", "nonexistent", "fr", "test"
            )

    def test_record_not_found(self, db_session):
        """Translation record not in run raises ValueError."""
        _setup_run_and_batch(db_session, "other-run")
        record = TranslationRecord(
            id="rec-ie-other", run_id="other-run", batch_id="batch-1",
            source_sql="hello", target_sql="bonjour", status="SUCCESS",
        )
        db_session.add(record)
        db_session.flush()

        lang = TranslationLanguage(
            record_id="rec-ie-other", language_code="fr",
            translated_value="bonjour", final_value="bonjour",
            status="translated",
        )
        db_session.add(lang)
        db_session.commit()

        with pytest.raises(ValueError, match="not found in run"):
            InlineCorrectionService.apply_inline_edit(
                db_session, "run-ie", "rec-ie-other", "fr", "test"
            )

    def test_pending_status_edited(self, db_session):
        """Pending status becomes 'edited'."""
        _setup_run_and_batch(db_session, "run-ie-status")
        record = TranslationRecord(
            id="rec-ie-status", run_id="run-ie-status", batch_id="batch-1",
            source_sql="hello", target_sql="bonjour", status="SUCCESS",
        )
        db_session.add(record)
        db_session.flush()

        lang = TranslationLanguage(
            record_id="rec-ie-status", language_code="fr",
            translated_value="bonjour", final_value="bonjour",
            status="pending",
        )
        db_session.add(lang)
        db_session.commit()

        InlineCorrectionService.apply_inline_edit(
            db_session, "run-ie-status", "rec-ie-status", "fr", "salut"
        )
        db_session.refresh(lang)
        assert lang.status == "edited"

    def test_submit_to_dictionary(self, db_session):
        """Submit to dictionary on inline edit."""
        dict_entry = TerminologyDictionary(id="dict-ie-1", name="Test", is_active=True)
        db_session.add(dict_entry)
        db_session.commit()

        record, lang = self._setup(db_session)

        result = InlineCorrectionService.apply_inline_edit(
            db_session, "run-ie", "rec-ie-1", "fr", "salut",
            submit_to_dictionary=True, dictionary_id="dict-ie-1",
        )
        assert result.get("dictionary_result") is not None
        assert result["dictionary_result"]["action"] in ("created",)

    def test_submit_to_dictionary_context_override(self, db_session):
        """Dictionary submission with context override."""
        dict_entry = TerminologyDictionary(id="dict-ie-ctx", name="Test", is_active=True)
        db_session.add(dict_entry)
        db_session.commit()

        _setup_run_and_batch(db_session, "run-ie-ctx")
        record = TranslationRecord(
            id="rec-ie-ctx", run_id="run-ie-ctx", batch_id="batch-1",
            source_sql="hello", target_sql="bonjour", status="SUCCESS",
        )
        db_session.add(record)
        db_session.flush()

        lang = TranslationLanguage(
            record_id="rec-ie-ctx", language_code="fr",
            translated_value="bonjour", final_value="bonjour",
            status="translated",
        )
        db_session.add(lang)
        db_session.commit()

        result = InlineCorrectionService.apply_inline_edit(
            db_session, "run-ie-ctx", "rec-ie-ctx", "fr", "salut",
            submit_to_dictionary=True, dictionary_id="dict-ie-ctx",
            context_data_override={"custom": "data"},
        )
        assert result.get("dictionary_result") is not None

    def test_submit_to_dictionary_keep_context_false(self, db_session):
        """keep_context=False clears context."""
        dict_entry = TerminologyDictionary(id="dict-ie-nc", name="Test", is_active=True)
        db_session.add(dict_entry)
        db_session.commit()

        _setup_run_and_batch(db_session, "run-ie-nc")
        record = TranslationRecord(
            id="rec-ie-nc", run_id="run-ie-nc", batch_id="batch-1",
            source_sql="hello", target_sql="bonjour", status="SUCCESS",
            source_data={"key": "val"},
        )
        db_session.add(record)
        db_session.flush()

        lang = TranslationLanguage(
            record_id="rec-ie-nc", language_code="fr",
            translated_value="bonjour", final_value="bonjour",
            status="translated",
        )
        db_session.add(lang)
        db_session.commit()

        result = InlineCorrectionService.apply_inline_edit(
            db_session, "run-ie-nc", "rec-ie-nc", "fr", "salut",
            submit_to_dictionary=True, dictionary_id="dict-ie-nc",
            keep_context=False,
        )
        assert result.get("dictionary_result") is not None


class TestSubmitCorrectionToDict:
    """InlineCorrectionService.submit_correction_to_dict."""

    def _setup(self, db_session, run_id="run-ie-dict"):
        _setup_run_and_batch(db_session, run_id)
        record = TranslationRecord(
            id="rec-ie-dict", run_id=run_id, batch_id="batch-1",
            source_sql="hello world", target_sql="bonjour", status="SUCCESS",
        )
        db_session.add(record)
        db_session.flush()

        lang = TranslationLanguage(
            record_id="rec-ie-dict", language_code="fr",
            translated_value="bonjour le monde", final_value="bonjour",
            source_language_detected="en",
            status="translated",
        )
        db_session.add(lang)
        db_session.commit()
        return record, lang

    def test_creates_entry(self, db_session):
        """Creates a new dictionary entry."""
        from src.models.translate import TerminologyDictionary
        dict_entry = TerminologyDictionary(id="dict-sub-1", name="Test", is_active=True)
        db_session.add(dict_entry)
        db_session.commit()

        record, lang = self._setup(db_session)

        result = InlineCorrectionService.submit_correction_to_dict(
            db_session, "rec-ie-dict", "fr", "dict-sub-1", "salut"
        )
        assert result["action"] == "created"
        assert result["entry_id"] is not None

    def test_detects_conflict(self, db_session):
        """Existing entry triggers conflict."""
        from src.models.translate import TerminologyDictionary, DictionaryEntry
        dict_entry = TerminologyDictionary(id="dict-sub-cf", name="Test", is_active=True)
        db_session.add(dict_entry)
        db_session.commit()

        record, lang = self._setup(db_session)

        # First submission creates
        InlineCorrectionService.submit_correction_to_dict(
            db_session, "rec-ie-dict", "fr", "dict-sub-cf", "salut"
        )

        # Second submission with same normalized term detects conflict
        result = InlineCorrectionService.submit_correction_to_dict(
            db_session, "rec-ie-dict", "fr", "dict-sub-cf", "salut2"
        )
        assert result["action"] == "conflict_detected"
        assert result["conflict"] is not None

    def test_lang_entry_not_found(self, db_session):
        """Non-existent lang entry raises ValueError."""
        with pytest.raises(ValueError, match="not found"):
            InlineCorrectionService.submit_correction_to_dict(
                db_session, "nonexistent", "fr", "dict-id", "salut"
            )

    def test_no_source_text(self, db_session):
        """Record without source text raises ValueError."""
        _setup_run_and_batch(db_session, "run-ie-no-src")
        record = TranslationRecord(
            id="rec-ie-no-src", run_id="run-ie-no-src", batch_id="batch-1",
            source_sql=None, target_sql="bonjour", status="SUCCESS",
        )
        db_session.add(record)
        db_session.flush()

        lang = TranslationLanguage(
            record_id="rec-ie-no-src", language_code="fr",
            translated_value="bonjour", final_value="bonjour",
            status="translated",
        )
        db_session.add(lang)
        db_session.commit()

        with pytest.raises(ValueError, match="No source text"):
            InlineCorrectionService.submit_correction_to_dict(
                db_session, "rec-ie-no-src", "fr", "dict-id", "salut"
            )

    def test_context_data_override(self, db_session):
        """Context data override is respected."""
        from src.models.translate import TerminologyDictionary
        dict_entry = TerminologyDictionary(id="dict-sub-co", name="Test", is_active=True)
        db_session.add(dict_entry)
        db_session.commit()

        record, lang = self._setup(db_session)

        result = InlineCorrectionService.submit_correction_to_dict(
            db_session, "rec-ie-dict", "fr", "dict-sub-co", "salut",
            context_data_override={"my": "data"},
        )
        assert result["action"] == "created"
        from src.models.translate import DictionaryEntry
        entry = db_session.query(DictionaryEntry).filter(DictionaryEntry.id == result["entry_id"]).first()
        assert entry.context_data == {"my": "data"}
        assert entry.context_source == "manual"

    def test_keep_context_false(self, db_session):
        """keep_context=False clears context."""
        from src.models.translate import TerminologyDictionary
        dict_entry = TerminologyDictionary(id="dict-sub-kcf", name="Test", is_active=True)
        db_session.add(dict_entry)
        db_session.commit()

        record, lang = self._setup(db_session)

        result = InlineCorrectionService.submit_correction_to_dict(
            db_session, "rec-ie-dict", "fr", "dict-sub-kcf", "salut",
            keep_context=False,
        )
        entry = db_session.query(DictionaryEntry).filter(DictionaryEntry.id == result["entry_id"]).first()
        assert entry.has_context is False
        assert entry.context_source == "manual"
# #endregion Test.InlineCorrectionService
