# #region Test.ReportNormalizer [C:3] [TYPE Module] [SEMANTICS test,report,normalizer,task]
# @BRIEF Tests for services/reports/normalizer.py — status_to_report_status, build_summary, extract_error_context, normalize_task_report.
# @RELATION BINDS_TO -> [normalizer]

from pathlib import Path
import sys

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

from datetime import UTC, datetime, timedelta
from unittest.mock import MagicMock, patch
import pytest


# #region helper factories [C:1] [TYPE Function]
# @BRIEF Helper factory for creating mock tasks.
def _make_task(**overrides):
    from src.core.task_manager.models import Task, TaskStatus, LogEntry
    defaults = {
        "id": "task-1",
        "plugin_id": "superset-migration",
        "status": TaskStatus.SUCCESS,
        "params": {},
        "result": None,
        "started_at": datetime(2024, 1, 1, 12, 0, 0, tzinfo=UTC),
        "finished_at": datetime(2024, 1, 1, 12, 30, 0, tzinfo=UTC),
        "logs": [],
    }
    defaults.update(overrides)
    return Task(**defaults)
# #endregion helper factories


# #region test_status_to_report_status [C:2] [TYPE Function]
# @BRIEF Test status_to_report_status mapping.
class TestStatusToReportStatus:
    def test_success_maps_to_success(self):
        from src.services.reports.normalizer import status_to_report_status
        from src.core.task_manager.models import TaskStatus
        from src.models.report import ReportStatus
        assert status_to_report_status(TaskStatus.SUCCESS) == ReportStatus.SUCCESS

    def test_failed_maps_to_failed(self):
        from src.services.reports.normalizer import status_to_report_status
        from src.core.task_manager.models import TaskStatus
        from src.models.report import ReportStatus
        assert status_to_report_status(TaskStatus.FAILED) == ReportStatus.FAILED

    def test_pending_maps_to_in_progress(self):
        from src.services.reports.normalizer import status_to_report_status
        from src.core.task_manager.models import TaskStatus
        from src.models.report import ReportStatus
        for status in [TaskStatus.PENDING, TaskStatus.RUNNING, TaskStatus.AWAITING_INPUT, TaskStatus.AWAITING_MAPPING]:
            assert status_to_report_status(status) == ReportStatus.IN_PROGRESS

    def test_unknown_string_maps_to_partial(self):
        from src.services.reports.normalizer import status_to_report_status
        from src.models.report import ReportStatus
        assert status_to_report_status("UNKNOWN_STATUS") == ReportStatus.PARTIAL
# #endregion test_status_to_report_status


# #region test_build_summary [C:2] [TYPE Function]
# @BRIEF Test build_summary extracts summary from task result.
class TestBuildSummary:
    def test_uses_result_summary(self):
        from src.services.reports.normalizer import build_summary
        from src.models.report import ReportStatus
        from src.core.task_manager.models import Task, TaskStatus
        task = _make_task(result={"summary": "Migration of 5 dashboards completed"})
        summary = build_summary(task, ReportStatus.SUCCESS)
        assert summary == "Migration of 5 dashboards completed"

    def test_uses_result_message(self):
        from src.services.reports.normalizer import build_summary
        from src.models.report import ReportStatus
        task = _make_task(result={"message": "All dashboards migrated"})
        summary = build_summary(task, ReportStatus.SUCCESS)
        assert summary == "All dashboards migrated"

    def test_fallback_to_status_based_summary(self):
        from src.services.reports.normalizer import build_summary
        from src.models.report import ReportStatus
        task = _make_task(result=None)
        assert build_summary(task, ReportStatus.SUCCESS) == "Task completed successfully"
        assert build_summary(task, ReportStatus.FAILED) == "Task failed"
        assert build_summary(task, ReportStatus.IN_PROGRESS) == "Task is in progress"
        assert build_summary(task, ReportStatus.PARTIAL) == "Task completed with partial data"

    def test_prefers_summary_over_message(self):
        from src.services.reports.normalizer import build_summary
        from src.models.report import ReportStatus
        task = _make_task(result={"summary": "Summary text", "message": "Message text"})
        assert build_summary(task, ReportStatus.SUCCESS) == "Summary text"

    def test_empty_summary_skips(self):
        from src.services.reports.normalizer import build_summary
        from src.models.report import ReportStatus
        task = _make_task(result={"summary": "", "message": "Actual message"})
        assert build_summary(task, ReportStatus.SUCCESS) == "Actual message"
# #endregion test_build_summary


# #region test_extract_error_context [C:2] [TYPE Function]
# @BRIEF Test extract_error_context for failed/partial reports.
class TestExtractErrorContext:
    def test_returns_none_for_success(self):
        from src.services.reports.normalizer import extract_error_context
        from src.models.report import ReportStatus
        task = _make_task()
        assert extract_error_context(task, ReportStatus.SUCCESS) is None

    def test_returns_none_for_in_progress(self):
        from src.services.reports.normalizer import extract_error_context
        from src.models.report import ReportStatus
        task = _make_task()
        assert extract_error_context(task, ReportStatus.IN_PROGRESS) is None

    def test_extracts_from_error_dict(self):
        from src.services.reports.normalizer import extract_error_context
        from src.models.report import ReportStatus
        task = _make_task(result={"error": {"message": "Connection failed", "code": "ERR_CONNECT", "next_actions": ["Check network", "Retry"]}})
        ctx = extract_error_context(task, ReportStatus.FAILED)
        assert ctx is not None
        assert ctx.message == "Connection failed"
        assert ctx.code == "ERR_CONNECT"
        assert ctx.next_actions == ["Check network", "Retry"]

    def test_falls_back_to_error_message_string(self):
        from src.services.reports.normalizer import extract_error_context
        from src.models.report import ReportStatus
        task = _make_task(result={"error_message": "Something went wrong"})
        ctx = extract_error_context(task, ReportStatus.FAILED)
        assert ctx is not None
        assert ctx.message == "Something went wrong"

    def test_falls_back_to_logs(self):
        from src.services.reports.normalizer import extract_error_context
        from src.models.report import ReportStatus
        from src.core.task_manager.models import LogEntry
        task = _make_task(
            result={"other": "data"},
            logs=[LogEntry(level="ERROR", message="Critical failure in module X")],
        )
        ctx = extract_error_context(task, ReportStatus.FAILED)
        assert ctx is not None
        assert ctx.message == "Critical failure in module X"

    def test_default_message_not_provided(self):
        from src.services.reports.normalizer import extract_error_context
        from src.models.report import ReportStatus
        task = _make_task(result={"unrelated": "data"})
        ctx = extract_error_context(task, ReportStatus.FAILED)
        assert ctx is not None
        assert ctx.message == "Not provided"

    def test_default_next_actions(self):
        from src.services.reports.normalizer import extract_error_context
        from src.models.report import ReportStatus
        task = _make_task(result={"error": {"message": "Error without actions"}})
        ctx = extract_error_context(task, ReportStatus.FAILED)
        assert ctx is not None
        assert ctx.next_actions == ["Review task diagnostics", "Retry the operation"]

    def test_works_for_partial_status(self):
        from src.services.reports.normalizer import extract_error_context
        from src.models.report import ReportStatus
        task = _make_task(result={"error": {"message": "Partial failure"}})
        ctx = extract_error_context(task, ReportStatus.PARTIAL)
        assert ctx is not None
        assert ctx.message == "Partial failure"
# #endregion test_extract_error_context


# #region test_normalize_task_report [C:2] [TYPE Function]
# @BRIEF Test normalize_task_report produces correct TaskReport.
class TestNormalizeTaskReport:
    def test_returns_task_report(self):
        from src.services.reports.normalizer import normalize_task_report
        from src.models.report import TaskReport, ReportStatus, TaskType
        task = _make_task()
        report = normalize_task_report(task)
        assert isinstance(report, TaskReport)
        assert report.report_id == task.id
        assert report.task_type == TaskType.MIGRATION
        assert report.status == ReportStatus.SUCCESS
        assert report.summary == "Task completed successfully"

    def test_includes_profile_details(self):
        from src.services.reports.normalizer import normalize_task_report
        task = _make_task()
        report = normalize_task_report(task)
        assert report.details is not None
        assert "profile" in report.details
        assert report.details["profile"]["display_label"] == "Migration"

    def test_includes_source_ref_from_params(self):
        from src.services.reports.normalizer import normalize_task_report
        task = _make_task(params={"environment_id": "env-1", "dashboard_id": "dash-42"})
        report = normalize_task_report(task)
        assert report.source_ref is not None
        assert report.source_ref["environment_id"] == "env-1"
        assert report.source_ref["dashboard_id"] == "dash-42"

    def test_includes_error_context_for_failed(self):
        from src.services.reports.normalizer import normalize_task_report
        from src.core.task_manager.models import TaskStatus
        task = _make_task(
            status=TaskStatus.FAILED,
            result={"error": {"message": "Deployment failed", "code": "ERR_DEPLOY"}},
        )
        report = normalize_task_report(task)
        assert report.error_context is not None
        assert report.error_context.message == "Deployment failed"

    def test_unknown_plugin_type(self):
        from src.services.reports.normalizer import normalize_task_report
        from src.models.report import TaskType
        task = _make_task(plugin_id="unknown-plugin")
        report = normalize_task_report(task)
        assert report.task_type == TaskType.UNKNOWN

    def test_no_updated_at_uses_started(self):
        from src.services.reports.normalizer import normalize_task_report
        from datetime import UTC, datetime
        task = _make_task(finished_at=None)
        report = normalize_task_report(task)
        assert report.updated_at == task.started_at

    def test_no_dates_uses_current_time(self):
        from src.services.reports.normalizer import normalize_task_report
        task = _make_task(started_at=None, finished_at=None)
        report = normalize_task_report(task)
        assert report.updated_at is not None

    def test_source_ref_none_when_no_params(self):
        from src.services.reports.normalizer import normalize_task_report
        task = _make_task(params={})
        report = normalize_task_report(task)
        assert report.source_ref is None

    def test_none_result_becomes_note(self):
        from src.services.reports.normalizer import normalize_task_report
        task = _make_task(result=None)
        report = normalize_task_report(task)
        assert report.details is not None
        assert report.details["result"] == {"note": "Not provided"}

    def test_dict_result_included(self):
        from src.services.reports.normalizer import normalize_task_report
        task = _make_task(result={"migrated": 5, "errors": 0})
        report = normalize_task_report(task)
        assert report.details is not None
        assert report.details["result"] == {"migrated": 5, "errors": 0}
# #endregion test_normalize_task_report
# #endregion Test.ReportNormalizer
