# #region Test.DictionaryCRUD [C:3] [TYPE Module] [SEMANTICS test,dictionary,crud]
# @BRIEF Tests for dictionary_crud.py — DictionaryCRUD.
# @RELATION BINDS_TO -> [DictionaryCRUD]
# @TEST_EDGE: not_found -> ValueError
# @TEST_EDGE: delete_blocked_by_active_jobs -> 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

from src.models.translate import (
    DictionaryEntry,
    TerminologyDictionary,
    TranslationJob,
    TranslationJobDictionary,
)
from src.plugins.translate.dictionary_crud import DictionaryCRUD
from .conftest import JOB_ID


class TestCreateDictionary:
    """DictionaryCRUD.create_dictionary."""

    def test_create_basic(self, db_session):
        """Creates a new dictionary."""
        d = DictionaryCRUD.create_dictionary(db_session, "My Dict", created_by="test")
        assert d.name == "My Dict"
        assert d.created_by == "test"
        assert d.is_active is True
        assert d.id is not None

    def test_create_with_description(self, db_session):
        """Creates with description."""
        d = DictionaryCRUD.create_dictionary(
            db_session, "My Dict", description="Test dictionary",
        )
        assert d.description == "Test dictionary"

    def test_create_inactive(self, db_session):
        """Creates inactive dictionary."""
        d = DictionaryCRUD.create_dictionary(
            db_session, "My Dict", is_active=False,
        )
        assert d.is_active is False


class TestUpdateDictionary:
    """DictionaryCRUD.update_dictionary."""

    def test_update_name(self, db_session):
        """Update name."""
        d = DictionaryCRUD.create_dictionary(db_session, "Original")
        updated = DictionaryCRUD.update_dictionary(db_session, d.id, name="Updated")
        assert updated.name == "Updated"

    def test_update_description(self, db_session):
        """Update description."""
        d = DictionaryCRUD.create_dictionary(db_session, "Test")
        updated = DictionaryCRUD.update_dictionary(db_session, d.id, description="New desc")
        assert updated.description == "New desc"

    def test_update_is_active(self, db_session):
        """Update is_active."""
        d = DictionaryCRUD.create_dictionary(db_session, "Test", is_active=True)
        updated = DictionaryCRUD.update_dictionary(db_session, d.id, is_active=False)
        assert updated.is_active is False

    def test_update_not_found(self, db_session):
        """Update non-existent raises ValueError."""
        with pytest.raises(ValueError, match="not found"):
            DictionaryCRUD.update_dictionary(db_session, "nonexistent", name="x")

    def test_update_partial(self, db_session):
        """Partial update doesn't change other fields."""
        d = DictionaryCRUD.create_dictionary(
            db_session, "Original", description="Desc", is_active=True,
        )
        updated = DictionaryCRUD.update_dictionary(db_session, d.id, name="New")
        assert updated.description == "Desc"
        assert updated.is_active is True


class TestDeleteDictionary:
    """DictionaryCRUD.delete_dictionary."""

    def test_delete(self, db_session):
        """Delete a dictionary without attached jobs."""
        d = DictionaryCRUD.create_dictionary(db_session, "To Delete")
        dict_id = d.id

        # Add some entries
        from src.plugins.translate.dictionary_entries import DictionaryEntryCRUD
        DictionaryEntryCRUD.add_entry(db_session, dict_id, "hello", "bonjour")

        DictionaryCRUD.delete_dictionary(db_session, dict_id)
        assert db_session.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first() is None
        # Entries should also be deleted
        entries = db_session.query(DictionaryEntry).filter(DictionaryEntry.dictionary_id == dict_id).all()
        assert len(entries) == 0

    def test_not_found(self, db_session):
        """Delete non-existent raises ValueError."""
        with pytest.raises(ValueError, match="not found"):
            DictionaryCRUD.delete_dictionary(db_session, "nonexistent")

    def test_delete_blocked_by_active_job(self, db_session):
        """Delete blocked when attached to active job."""
        d = DictionaryCRUD.create_dictionary(db_session, "Blocked Dict")
        dict_id = d.id

        # Attach to the existing job
        from src.models.translate import TranslationJobDictionary
        job = db_session.query(TranslationJob).filter(TranslationJob.id == JOB_ID).first()
        job.status = "ACTIVE"
        db_session.commit()

        tj_dict = TranslationJobDictionary(job_id=JOB_ID, dictionary_id=dict_id)
        db_session.add(tj_dict)
        db_session.commit()

        with pytest.raises(ValueError, match="Cannot delete dictionary attached"):
            DictionaryCRUD.delete_dictionary(db_session, dict_id)

    def test_delete_blocked_by_scheduled_job(self, db_session):
        """Delete blocked when attached to SCHEDULED job."""
        d = DictionaryCRUD.create_dictionary(db_session, "Scheduled Dict")
        dict_id = d.id

        job = db_session.query(TranslationJob).filter(TranslationJob.id == JOB_ID).first()
        job.status = "SCHEDULED"
        db_session.commit()

        tj_dict = TranslationJobDictionary(job_id=JOB_ID, dictionary_id=dict_id)
        db_session.add(tj_dict)
        db_session.commit()

        with pytest.raises(ValueError, match="Cannot delete dictionary attached"):
            DictionaryCRUD.delete_dictionary(db_session, dict_id)

    def test_delete_with_completed_job_ok(self, db_session):
        """Delete OK when attached to COMPLETED job."""
        d = DictionaryCRUD.create_dictionary(db_session, "Completed Dict")
        dict_id = d.id

        job = db_session.query(TranslationJob).filter(TranslationJob.id == JOB_ID).first()
        job.status = "COMPLETED"
        db_session.commit()

        tj_dict = TranslationJobDictionary(job_id=JOB_ID, dictionary_id=dict_id)
        db_session.add(tj_dict)
        db_session.commit()

        DictionaryCRUD.delete_dictionary(db_session, dict_id)
        assert db_session.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first() is None


class TestGetDictionary:
    """DictionaryCRUD.get_dictionary."""

    def test_get(self, db_session):
        """Get existing dictionary."""
        d = DictionaryCRUD.create_dictionary(db_session, "Get Test")
        fetched = DictionaryCRUD.get_dictionary(db_session, d.id)
        assert fetched.id == d.id
        assert fetched.name == "Get Test"

    def test_not_found(self, db_session):
        """Get non-existent raises ValueError."""
        with pytest.raises(ValueError, match="not found"):
            DictionaryCRUD.get_dictionary(db_session, "nonexistent")


class TestListDictionaries:
    """DictionaryCRUD.list_dictionaries."""

    def test_list(self, db_session):
        """List with pagination."""
        for i in range(5):
            DictionaryCRUD.create_dictionary(db_session, f"Dict {i}")

        dicts, total = DictionaryCRUD.list_dictionaries(db_session, page=1, page_size=2)
        assert len(dicts) == 2
        assert total == 5

    def test_empty(self, db_session):
        """Empty list returns [] and 0."""
        dicts, total = DictionaryCRUD.list_dictionaries(db_session)
        assert dicts == []
        assert total == 0
# #endregion Test.DictionaryCRUD
