# #region TranslateCorrectionTests [C:2] [TYPE Module]
# @SEMANTICS: tests, translate, corrections, dictionary
# @PURPOSE: Tests for term correction API endpoints and DictionaryManager correction methods.
# @LAYER Tests
# @RELATION  BINDS_TO -> [DictionaryManagerModule]
# @RELATION  BINDS_TO -> [TranslateRoutes]
#
# @TEST_CONTRACT: CorrectionFlow ->
# {
#   required_fields: {source_term, incorrect_target_term, corrected_target_term, dictionary_id},
#   invariants: [
#       "POST /api/translate/corrections creates or updates entry",
#       "Conflict detection works for existing entries",
#       "POST /api/translate/corrections/bulk atomic all-or-nothing",
#       "Origin tracking fields are populated"
#   ]
# }
# @TEST_FIXTURE: valid_dictionary -> created via DictionaryManager
# @TEST_EDGE: missing_dictionary_id -> 422
# @TEST_EDGE: conflict_detected -> conflict response returned
# @TEST_EDGE: bulk_correction_all_succeed -> atomic commit
# @TEST_EDGE: bulk_correction_with_conflicts -> conflicts listed
# @TEST_INVARIANT: correction_flow -> VERIFIED_BY: [valid_dictionary, conflict_detected]

from pathlib import Path
import sys

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

import pytest
from unittest.mock import MagicMock

from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from src.app import app
from src.core.database import Base
from src.dependencies import get_config_manager, get_current_user, get_db
from src.plugins.translate.dictionary import DictionaryManager

# In-memory SQLite
SQLALCHEMY_DATABASE_URL = "sqlite:///:memory:"
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base.metadata.create_all(bind=engine)

mock_user = MagicMock()
mock_user.username = "testuser"
mock_user.roles = []
admin_role = MagicMock()
admin_role.name = "Admin"
mock_user.roles.append(admin_role)


@pytest.fixture
def db_session():
    connection = engine.connect()
    transaction = connection.begin()
    session = TestingSessionLocal(bind=connection)
    yield session
    session.close()
    transaction.rollback()
    connection.close()


@pytest.fixture
def mock_api_deps():
    config_manager = MagicMock()
    config_manager.get_environments.return_value = []

    connection = engine.connect()
    transaction = connection.begin()
    session = TestingSessionLocal(bind=connection)

    def override_get_db():
        try:
            yield session
        finally:
            pass

    overrides = {
        get_current_user: lambda: mock_user,
        get_config_manager: lambda: config_manager,
        get_db: override_get_db,
    }
    for dep, override in overrides.items():
        app.dependency_overrides[dep] = override

    yield {"config_manager": config_manager, "session": session}

    app.dependency_overrides.clear()
    session.close()
    transaction.rollback()
    connection.close()


@pytest.fixture
def client(mock_api_deps):
    return TestClient(app)


# #region test_submit_correction_creates_entry [C:2] [TYPE Function]
# @PURPOSE: Verify that a correction creates a new dictionary entry.
def test_submit_correction_creates_entry(db_session):
    """Test that submitting a correction creates a new entry."""
    dict_obj = DictionaryManager.create_dictionary(
        db_session, name="Test Dict", source_dialect="en", target_dialect="ru",
        created_by="testuser",
    )

    result = DictionaryManager.submit_correction(
        db_session,
        dict_id=dict_obj.id,
        source_term="hello",
        incorrect_target_term="privet",
        corrected_target_term="zdravstvuyte",
        origin_run_id="run-123",
        origin_row_key="row-1",
        origin_user_id="testuser",
    )

    assert result["action"] == "created"
    assert result["entry_id"] is not None

    entries, total = DictionaryManager.list_entries(db_session, dict_obj.id)
    assert total == 1
    assert entries[0].source_term == "hello"
    assert entries[0].target_term == "zdravstvuyte"
    assert entries[0].origin_run_id == "run-123"
    assert entries[0].origin_row_key == "row-1"
    assert entries[0].origin_user_id == "testuser"
# #endregion test_submit_correction_creates_entry


# #region test_submit_correction_conflict_detected [C:2] [TYPE Function]
# @PURPOSE: Verify conflict detection when entry already exists.
def test_submit_correction_conflict_detected(db_session):
    """Test that conflict is detected when entry already exists."""
    dict_obj = DictionaryManager.create_dictionary(
        db_session, name="Dict", source_dialect="en", target_dialect="ru",
    )

    # Create initial entry
    DictionaryManager.add_entry(db_session, dict_obj.id, "hello", "privet")

    # Submit correction with conflict
    result = DictionaryManager.submit_correction(
        db_session,
        dict_id=dict_obj.id,
        source_term="hello",
        incorrect_target_term="privet",
        corrected_target_term="zdravstvuyte",
        on_conflict="keep_existing",
    )

    assert result["action"] == "conflict_detected"
    assert result["conflict"] is not None
    assert result["conflict"]["existing_target_term"] == "privet"
# #endregion test_submit_correction_conflict_detected


# #region test_submit_correction_overwrite [C:2] [TYPE Function]
# @PURPOSE: Verify that correction overwrites existing entry.
def test_submit_correction_overwrite(db_session):
    """Test that correction overwrites existing entry."""
    dict_obj = DictionaryManager.create_dictionary(
        db_session, name="Dict", source_dialect="en", target_dialect="ru",
    )

    DictionaryManager.add_entry(db_session, dict_obj.id, "hello", "privet")

    result = DictionaryManager.submit_correction(
        db_session,
        dict_id=dict_obj.id,
        source_term="hello",
        incorrect_target_term="privet",
        corrected_target_term="hi_there",
        on_conflict="overwrite",
    )

    assert result["action"] == "updated"
    entries, _ = DictionaryManager.list_entries(db_session, dict_obj.id)
    assert entries[0].target_term == "hi_there"
# #endregion test_submit_correction_overwrite


# #region test_bulk_corrections_atomic [C:2] [TYPE Function]
# @PURPOSE: Verify bulk corrections are applied atomically.
def test_bulk_corrections_atomic(db_session):
    """Test that bulk corrections are applied atomically."""
    dict_obj = DictionaryManager.create_dictionary(
        db_session, name="Bulk Dict", source_dialect="en", target_dialect="ru",
    )

    corrections = [
        {"source_term": "cat", "incorrect_target_term": "kot", "corrected_target_term": "koshka"},
        {"source_term": "dog", "incorrect_target_term": "sobaka", "corrected_target_term": "pyos"},
    ]

    result = DictionaryManager.submit_bulk_corrections(
        db_session,
        dict_id=dict_obj.id,
        corrections=corrections,
        origin_user_id="testuser",
    )

    assert result["status"] == "completed"
    assert result["applied"] == 2
    assert len(result["conflicts"]) == 0

    entries, total = DictionaryManager.list_entries(db_session, dict_obj.id)
    assert total == 2
# #endregion test_bulk_corrections_atomic


# #region test_api_correction_missing_dict [C:2] [TYPE Function]
# @PURPOSE: Verify POST corrections without dictionary_id returns 422.
def test_api_correction_missing_dict(client):
    """Test correction without dict_id returns 422."""
    response = client.post("/api/translate/corrections", json={
        "source_term": "hello",
        "incorrect_target_term": "privet",
        "corrected_target_term": "zdravstvuyte",
    })
    assert response.status_code == 422
# #endregion test_api_correction_missing_dict


# #region test_api_bulk_corrections [C:2] [TYPE Function]
# @PURPOSE: Verify POST /corrections/bulk works.
def test_api_bulk_corrections(client, mock_api_deps):
    """Test bulk corrections endpoint."""
    session = mock_api_deps["session"]
    dict_obj = DictionaryManager.create_dictionary(
        session, name="API Dict", source_dialect="en", target_dialect="ru",
    )

    response = client.post("/api/translate/corrections/bulk", json={
        "dictionary_id": dict_obj.id,
        "corrections": [
            {"source_term": "hello", "incorrect_target_term": "privet", "corrected_target_term": "hi"},
        ],
    })
    assert response.status_code == 200
    data = response.json()
    assert data["status"] == "completed"
# #endregion test_api_bulk_corrections
# #endregion TranslateCorrectionTests
