# #region Test.OrchestratorQuery [C:3] [TYPE Module] [SEMANTICS test, translate, query, records, history]
# @BRIEF Tests for orchestrator_query.py — get_run_records, get_run_history.
# @RELATION BINDS_TO -> [orchestrator_query]
# @TEST_CONTRACT: get_run_records -> dict | paginated records with optional deduplicate
# @TEST_CONTRACT: get_run_history -> tuple[int, list[dict]] | paginated run history
# @TEST_EDGE: deduplicate_empty_source_hash
# @TEST_EDGE: status_filter

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

import os
os.environ.setdefault("SECRET_KEY", "test-secret-key-for-testing")
os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:")
os.environ.setdefault("AUTH_DATABASE_URL", "sqlite:///:memory:")

from datetime import datetime, timezone
from unittest.mock import MagicMock, patch, ANY

import pytest

from src.plugins.translate.orchestrator_query import get_run_records, get_run_history


class TestGetRunRecords:
    """get_run_records — paginated run records."""

    def _make_record(self, rec_id: str, **kwargs):
        rec = MagicMock()
        rec.id = rec_id
        rec.batch_id = "batch-1"
        rec.source_sql = "SELECT * FROM t"
        rec.target_sql = "SELECT * FROM t_translated"
        rec.source_object_type = "table"
        rec.source_object_id = "123"
        rec.source_object_name = "my_table"
        rec.source_data = {"col": "val"}
        rec.source_hash = f"hash_{rec_id}"
        rec.status = "translated"
        rec.error_message = None
        rec.created_at = datetime(2026, 1, 15, 12, 0, 0, tzinfo=timezone.utc)
        rec.languages = []
        for k, v in kwargs.items():
            setattr(rec, k, v)
        return rec

    def test_basic_query(self):
        """Returns paginated records with metadata."""
        db = MagicMock()
        # Mock chain: db.query().options().filter()
        options_mock = MagicMock()
        filter_mock = MagicMock()
        options_filter_mock = MagicMock()

        db.query.return_value.options.return_value = options_mock
        options_mock.filter.return_value = options_filter_mock
        options_filter_mock.count.return_value = 2
        options_filter_mock.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [
            self._make_record("r1"), self._make_record("r2"),
        ]

        result = get_run_records(db, "run-1")

        assert result["total"] == 2
        assert len(result["items"]) == 2
        assert result["page"] == 1
        assert result["page_size"] == 50
        assert result["status_filter"] is None
        assert result["deduplicate"] is False

    def test_with_status_filter(self):
        """Status filter applied to query."""
        db = MagicMock()
        options_mock = MagicMock()
        filter_mock1 = MagicMock()
        filter_mock2 = MagicMock()

        db.query.return_value.options.return_value = options_mock
        options_mock.filter.return_value = filter_mock1
        filter_mock1.filter.return_value = filter_mock2
        filter_mock2.count.return_value = 1
        filter_mock2.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [
            self._make_record("r1", status="failed"),
        ]

        result = get_run_records(db, "run-1", status_filter="failed")

        assert result["total"] == 1
        assert result["status_filter"] == "failed"

    def test_deduplicate_applied(self):
        """Deduplicate flag applies exclusion logic."""
        db = MagicMock()
        options_mock = MagicMock()
        filter_mock1 = MagicMock()
        filter_mock2 = MagicMock()

        db.query.return_value.options.return_value = options_mock
        options_mock.filter.return_value = filter_mock1
        filter_mock1.filter.return_value = filter_mock2
        filter_mock2.count.return_value = 1
        filter_mock2.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [
            self._make_record("r1", source_hash="abc123"),
        ]

        result = get_run_records(db, "run-1", deduplicate=True)

        assert result["deduplicate"] is True
        assert len(result["items"]) == 1

    def test_empty_records(self):
        """No records returns empty list."""
        db = MagicMock()
        options_mock = MagicMock()

        db.query.return_value.options.return_value = options_mock
        options_mock.filter.return_value.count.return_value = 0
        options_mock.filter.return_value.order_by.return_value.offset.return_value.limit.return_value.all.return_value = []

        result = get_run_records(db, "run-1")

        assert result["total"] == 0
        assert result["items"] == []

    def test_record_languages_included(self):
        """Language entries included in record dict."""
        db = MagicMock()
        lang = MagicMock()
        lang.language_code = "ru"
        lang.translated_value = "привет"
        lang.final_value = None
        lang.source_language_detected = "en"
        lang.status = "translated"
        lang.needs_review = False

        rec = self._make_record("r1", languages=[lang])
        options_mock = MagicMock()
        db.query.return_value.options.return_value = options_mock
        options_mock.filter.return_value.count.return_value = 1
        options_mock.filter.return_value.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [rec]

        result = get_run_records(db, "run-1")

        assert len(result["items"][0]["languages"]) == 1
        assert result["items"][0]["languages"][0]["language_code"] == "ru"
        assert result["items"][0]["languages"][0]["final_value"] == "привет"

    def test_record_without_created_at(self):
        """Record without created_at returns None for isoformat."""
        db = MagicMock()
        rec = self._make_record("r1", created_at=None)
        options_mock = MagicMock()
        db.query.return_value.options.return_value = options_mock
        options_mock.filter.return_value.count.return_value = 1
        options_mock.filter.return_value.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [rec]

        result = get_run_records(db, "run-1")

        assert result["items"][0]["created_at"] is None

    def test_pagination_params(self):
        """Pagination params affect offset and limit."""
        db = MagicMock()
        rec = self._make_record("r1")
        options_mock = MagicMock()
        db.query.return_value.options.return_value = options_mock
        options_mock.filter.return_value.count.return_value = 10
        options_mock.filter.return_value.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [rec]

        result = get_run_records(db, "run-1", page=2, page_size=10)

        assert result["page"] == 2
        assert result["page_size"] == 10


class TestGetRunHistory:
    """get_run_history — paginated run history for a job."""

    def _make_run(self, run_id: str, **kwargs):
        run = MagicMock()
        run.id = run_id
        run.job_id = "job-1"
        run.status = "COMPLETED"
        run.trigger_type = "manual"
        run.started_at = datetime(2026, 1, 15, 12, 0, 0, tzinfo=timezone.utc)
        run.completed_at = datetime(2026, 1, 15, 13, 0, 0, tzinfo=timezone.utc)
        run.error_message = None
        run.total_records = 100
        run.successful_records = 95
        run.failed_records = 3
        run.skipped_records = 2
        run.cache_hits = 5
        run.insert_status = "success"
        run.insert_method = "cte_insert"
        run.connection_snapshot = {"db": "test"}
        run.superset_execution_id = "q-1"
        run.created_by = "user"
        run.created_at = datetime(2026, 1, 15, 12, 0, 0, tzinfo=timezone.utc)
        for k, v in kwargs.items():
            setattr(run, k, v)
        return run

    def test_basic_history(self):
        """Returns total count and run list."""
        db = MagicMock()
        runs = [self._make_run("r1"), self._make_run("r2")]
        filter_mock = MagicMock()
        db.query.return_value.filter.return_value = filter_mock
        filter_mock.count.return_value = 2
        filter_mock.order_by.return_value.offset.return_value.limit.return_value.all.return_value = runs

        total, items = get_run_history(db, "job-1")

        assert total == 2
        assert len(items) == 2
        assert items[0]["id"] == "r1"
        assert items[0]["status"] == "COMPLETED"

    def test_empty_history(self):
        """No runs returns total=0 and empty list."""
        db = MagicMock()
        filter_mock = MagicMock()
        db.query.return_value.filter.return_value = filter_mock
        filter_mock.count.return_value = 0
        filter_mock.order_by.return_value.offset.return_value.limit.return_value.all.return_value = []

        total, items = get_run_history(db, "job-1")

        assert total == 0
        assert items == []

    def test_pagination_params(self):
        """Pagination affects offset and limit."""
        db = MagicMock()
        runs = [self._make_run("r1")]
        filter_mock = MagicMock()
        db.query.return_value.filter.return_value = filter_mock
        filter_mock.count.return_value = 10
        filter_mock.order_by.return_value.offset.return_value.limit.return_value.all.return_value = runs

        total, items = get_run_history(db, "job-1", page=3, page_size=5)

        assert total == 10
        assert len(items) == 1

    def test_run_without_dates(self):
        """Run without started_at/completed_at returns None."""
        db = MagicMock()
        run = self._make_run("r1", started_at=None, completed_at=None)
        filter_mock = MagicMock()
        db.query.return_value.filter.return_value = filter_mock
        filter_mock.count.return_value = 1
        filter_mock.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [run]

        total, items = get_run_history(db, "job-1")

        assert items[0]["started_at"] is None
        assert items[0]["completed_at"] is None

    def test_all_numeric_fields_default_to_zero(self):
        """Numeric fields default to 0 when None."""
        db = MagicMock()
        run = self._make_run(
            "r1", total_records=None, successful_records=None,
            failed_records=None, skipped_records=None, cache_hits=None,
        )
        filter_mock = MagicMock()
        db.query.return_value.filter.return_value = filter_mock
        filter_mock.count.return_value = 1
        filter_mock.order_by.return_value.offset.return_value.limit.return_value.all.return_value = [run]

        total, items = get_run_history(db, "job-1")

        assert items[0]["total_records"] == 0
        assert items[0]["successful_records"] == 0
        assert items[0]["failed_records"] == 0
        assert items[0]["skipped_records"] == 0
        assert items[0]["cache_hits"] == 0
# #endregion Test.OrchestratorQuery
