# #region TestDictionaryFilter [C:3] [TYPE Module] [SEMANTICS test, dictionary, filter, batch]
# @BRIEF Validate DictionaryBatchFilter operations.
# @RELATION BINDS_TO -> [DictionaryBatchFilter]

import pytest

from sqlalchemy import create_engine, event
from sqlalchemy.orm import Session, sessionmaker

from src.models.translate import (
    Base,
    DictionaryEntry,
    TerminologyDictionary,
    TranslationJob,
    TranslationJobDictionary,
)
from src.plugins.translate.dictionary import DictionaryManager


# region db_session [TYPE Fixture]
@pytest.fixture
def db_session():
    engine = create_engine("sqlite:///:memory:", echo=False)
    @event.listens_for(engine, "connect")
    def _set_sqlite_pragma(dbapi_connection, connection_record):
        cursor = dbapi_connection.cursor()
        cursor.execute("PRAGMA foreign_keys=ON")
        cursor.close()
    Base.metadata.create_all(bind=engine)
    session = sessionmaker(bind=engine)()
    try:
        yield session
    finally:
        session.close()
        Base.metadata.drop_all(bind=engine)
# endregion db_session


class TestFilterBasic:
    """Verify basic filter_for_batch behavior."""

    # region test_filter_for_batch_no_dictionaries [C:2] [TYPE Function]
    def test_filter_for_batch_no_dictionaries(self, db_session: Session):
        job = TranslationJob(name="No Dict Job", source_dialect="a", target_dialect="b", status="DRAFT")
        db_session.add(job)
        db_session.commit()
        result = DictionaryManager.filter_for_batch(db_session, ["hello world"], job.id)
        assert result == []
    # endregion test_filter_for_batch_no_dictionaries

    # region test_filter_for_batch_matches [C:2] [TYPE Function]
    def test_filter_for_batch_matches(self, db_session: Session):
        d = DictionaryManager.create_dictionary(db_session, name="Test Dict", source_dialect="a", target_dialect="b")
        DictionaryManager.add_entry(db_session, d.id, "hello", "hola", source_language="en", target_language="es")
        DictionaryManager.add_entry(db_session, d.id, "world", "mundo", source_language="en", target_language="es")
        DictionaryManager.add_entry(db_session, d.id, "foo", "bar", source_language="en", target_language="es")

        job = TranslationJob(name="Test Job", source_dialect="a", target_dialect="b", status="DRAFT")
        db_session.add(job)
        db_session.flush()
        link = TranslationJobDictionary(job_id=job.id, dictionary_id=d.id)
        db_session.add(link)
        db_session.commit()

        result = DictionaryManager.filter_for_batch(db_session, ["hello beautiful world", "nothing here", "foo bar"], job.id)
        assert len(result) >= 3

        hello_matches = [m for m in result if m["source_term"] == "hello"]
        assert len(hello_matches) == 1
        assert hello_matches[0]["text_index"] == 0

        foo_matches = [m for m in result if m["source_term"] == "foo"]
        assert len(foo_matches) == 1
        assert foo_matches[0]["text_index"] == 2
    # endregion test_filter_for_batch_matches

    # region test_filter_for_batch_case_insensitive [C:2] [TYPE Function]
    def test_filter_for_batch_case_insensitive(self, db_session: Session):
        d = DictionaryManager.create_dictionary(db_session, name="Test Dict", source_dialect="a", target_dialect="b")
        DictionaryManager.add_entry(db_session, d.id, "Hello World", "Hola Mundo", source_language="en", target_language="es")

        job = TranslationJob(name="Test Job", source_dialect="a", target_dialect="b", status="DRAFT")
        db_session.add(job)
        db_session.flush()
        link = TranslationJobDictionary(job_id=job.id, dictionary_id=d.id)
        db_session.add(link)
        db_session.commit()

        result = DictionaryManager.filter_for_batch(db_session, ["hello world is great"], job.id)
        assert len(result) == 1
        assert result[0]["source_term"] == "Hello World"
        assert result[0]["target_term"] == "Hola Mundo"
    # endregion test_filter_for_batch_case_insensitive

    # region test_filter_for_batch_word_boundary [C:2] [TYPE Function]
    def test_filter_for_batch_word_boundary(self, db_session: Session):
        d = DictionaryManager.create_dictionary(db_session, name="Test Dict", source_dialect="a", target_dialect="b")
        DictionaryManager.add_entry(db_session, d.id, "cat", "gato", source_language="en", target_language="es")

        job = TranslationJob(name="Test Job", source_dialect="a", target_dialect="b", status="DRAFT")
        db_session.add(job)
        db_session.flush()
        link = TranslationJobDictionary(job_id=job.id, dictionary_id=d.id)
        db_session.add(link)
        db_session.commit()

        result = DictionaryManager.filter_for_batch(db_session, ["catalog"], job.id)
        assert len(result) == 0

        result = DictionaryManager.filter_for_batch(db_session, ["the cat sat"], job.id)
        assert len(result) == 1
    # endregion test_filter_for_batch_word_boundary


class TestFilterPriority:
    """Verify dictionary link order priority in filtering."""

    # region test_filter_for_batch_multi_dictionary_priority [C:2] [TYPE Function]
    def test_filter_for_batch_multi_dictionary_priority(self, db_session: Session):
        d1 = DictionaryManager.create_dictionary(db_session, name="Priority1", source_dialect="a", target_dialect="b")
        d2 = DictionaryManager.create_dictionary(db_session, name="Priority2", source_dialect="a", target_dialect="b")
        DictionaryManager.add_entry(db_session, d1.id, "hello", "hola", source_language="en", target_language="es")
        DictionaryManager.add_entry(db_session, d2.id, "hello", "bonjour", source_language="en", target_language="es")

        job = TranslationJob(name="Test Job", source_dialect="a", target_dialect="b", status="DRAFT")
        db_session.add(job)
        db_session.flush()
        link1 = TranslationJobDictionary(job_id=job.id, dictionary_id=d1.id)
        link2 = TranslationJobDictionary(job_id=job.id, dictionary_id=d2.id)
        db_session.add(link1)
        db_session.add(link2)
        db_session.commit()

        result = DictionaryManager.filter_for_batch(db_session, ["hello"], job.id)
        assert len(result) == 2
        assert result[0]["dictionary_id"] == d1.id
        assert result[0]["target_term"] == "hola"
        assert result[1]["dictionary_id"] == d2.id
        assert result[1]["target_term"] == "bonjour"
    # endregion test_filter_for_batch_multi_dictionary_priority


class TestFilterLanguagePair:
    """Verify language-pair filtering in filter_for_batch."""

    # region test_filter_for_batch_with_language_pair [C:2] [TYPE Function]
    def test_filter_for_batch_with_language_pair(self, db_session: Session):
        d = DictionaryManager.create_dictionary(db_session, name="Lang Filter", source_dialect="a", target_dialect="b")
        DictionaryManager.add_entry(db_session, d.id, "hello", "привет", source_language="en", target_language="ru")
        DictionaryManager.add_entry(db_session, d.id, "world", "мир", source_language="en", target_language="ru")
        DictionaryManager.add_entry(db_session, d.id, "hello", "hallo", source_language="en", target_language="de")

        job = TranslationJob(name="Lang Job", source_dialect="a", target_dialect="b", status="DRAFT")
        db_session.add(job)
        db_session.flush()
        link = TranslationJobDictionary(job_id=job.id, dictionary_id=d.id)
        db_session.add(link)
        db_session.commit()

        result = DictionaryManager.filter_for_batch(db_session, ["hello world"], job.id, source_language="en", target_language="ru")
        assert len(result) == 2
        assert all(m["target_language"] == "ru" for m in result)

        result2 = DictionaryManager.filter_for_batch(db_session, ["hello"], job.id, source_language="en", target_language="de")
        assert len(result2) == 1
        assert result2[0]["target_language"] == "de"
        assert result2[0]["target_term"] == "hallo"
    # endregion test_filter_for_batch_with_language_pair

    # region test_filter_for_batch_target_language_only [C:2] [TYPE Function]
    def test_filter_for_batch_target_language_only(self, db_session: Session):
        d = DictionaryManager.create_dictionary(db_session, name="Target Only", source_dialect="a", target_dialect="b")
        DictionaryManager.add_entry(db_session, d.id, "hello", "привет", source_language="en", target_language="ru")
        DictionaryManager.add_entry(db_session, d.id, "hello", "hallo", source_language="en", target_language="de")
        DictionaryManager.add_entry(db_session, d.id, "bonjour", "hallo", source_language="fr", target_language="de")

        job = TranslationJob(name="Tgt Job", source_dialect="a", target_dialect="b", status="DRAFT")
        db_session.add(job)
        db_session.flush()
        link = TranslationJobDictionary(job_id=job.id, dictionary_id=d.id)
        db_session.add(link)
        db_session.commit()

        result = DictionaryManager.filter_for_batch(db_session, ["hello bonjour"], job.id, target_language="de")
        assert len(result) == 2
        assert all(m["target_language"] == "de" for m in result)

        result2 = DictionaryManager.filter_for_batch(db_session, ["hello"], job.id, target_language="ru")
        assert len(result2) == 1
        assert result2[0]["target_language"] == "ru"
    # endregion test_filter_for_batch_target_language_only


class TestMigration:
    """Verify migration of old-style entries."""

    # region test_migrate_old_entries [C:2] [TYPE Function]
    def test_migrate_old_entries(self, db_session: Session):
        d = TerminologyDictionary(name="Old Dict", source_dialect="a", target_dialect="b")
        db_session.add(d)
        db_session.flush()

        entry1 = DictionaryEntry(
            dictionary_id=d.id, source_term="hello", source_term_normalized="hello",
            target_term="привет", source_language="und", target_language="und",
        )
        entry2 = DictionaryEntry(
            dictionary_id=d.id, source_term="world", source_term_normalized="world",
            target_term="мир", source_language="und", target_language="und",
            origin_source_language="en",
        )
        db_session.add(entry1)
        db_session.add(entry2)
        db_session.commit()

        result = DictionaryManager.migrate_old_entries(db_session)
        assert result["total_processed"] >= 2
        assert result["migrated_source"] >= 1
        assert result["migrated_target"] == 0

        db_session.refresh(entry1)
        assert entry1.target_language == "und"
        assert entry1.source_language == "und"

        db_session.refresh(entry2)
        assert entry2.source_language == "en"
        assert entry2.target_language == "und"
    # endregion test_migrate_old_entries
# #endregion TestDictionaryFilter
