"""#1661 查重 API enqueue 及仓储 commit 覆盖测试。

覆盖：
- upload 接口在成功后调用 celery_app.send_task
- retry 接口在成功后调用 celery_app.send_task
- duplication_repository.update() 正确调用 session.commit()
"""

from __future__ import annotations

import os
import sys
from unittest.mock import MagicMock, patch

import pytest

os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing")
os.environ.setdefault("DATABASE_URL", "sqlite:///test.db")

ROOT = os.path.join(os.path.dirname(__file__), "..", "..")
sys.path.insert(0, os.path.join(ROOT, "apps", "api"))
sys.path.insert(0, os.path.join(ROOT, "packages"))

from app.api.routes.duplication import router
from app.auth import AuthenticatedUser, get_current_user
from app.core.storage import get_storage_service
from app.dependencies import get_duplication_repository
from fastapi import FastAPI
from fastapi.testclient import TestClient

from packages.domain.duplication import DuplicationRecord
from packages.domain.entities import User

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _make_test_user():
    return User(id="user-1", username="testuser", email="test@example.com", display_name="Test User")


def _make_auth_user():
    return AuthenticatedUser(user=_make_test_user(), session_id="test-session", token_type="bearer")


def _make_record(status="pending"):
    record = DuplicationRecord.create(
        user_id="user-1",
        filename="test.mp4",
        file_size=1024,
        storage_key="duplication/abc/test.mp4",
    )
    if status != "pending":
        record.status = status
    return record


def _build_client(auth_user, repo, storage=None):
    """构建带 dependency_overrides 的 TestClient。"""
    app = FastAPI()
    app.include_router(router, prefix="/duplication")
    app.dependency_overrides[get_current_user] = lambda: auth_user
    app.dependency_overrides[get_duplication_repository] = lambda: repo
    if storage is not None:
        app.dependency_overrides[get_storage_service] = lambda: storage
    return TestClient(app)


# ---------------------------------------------------------------------------
# 1. Upload endpoint enqueues celery task
# ---------------------------------------------------------------------------


def test_upload_enqueue_calls_celery_task():
    """POST /duplication/upload 成功创建记录后必须调用 send_task。"""
    record = _make_record()

    fake_repo = MagicMock()
    fake_repo.create.return_value = record

    fake_storage = MagicMock()
    fake_auth = _make_auth_user()

    client = _build_client(fake_auth, fake_repo, fake_storage)

    with patch("app.api.routes.duplication.celery_app") as mock_celery:
        response = client.post(
            "/duplication/upload",
            files={"file": ("test.mp4", b"fake-video-content", "video/mp4")},
        )

    assert response.status_code == 200, response.text
    mock_celery.send_task.assert_called_once_with(
        "worker.process_duplication_check",
        args=[record.id],
    )


# ---------------------------------------------------------------------------
# 2. Retry endpoint enqueues celery task
# ---------------------------------------------------------------------------


def test_retry_enqueue_calls_celery_task():
    """POST /duplication/records/{id}/retry 成功后必须调用 send_task。"""
    record = _make_record(status="failed")

    fake_repo = MagicMock()
    fake_repo.get.return_value = record

    # RetryDuplicationUseCase.execute 内部调用 repo.get → record.reset_for_retry → repo.update
    updated = _make_record()
    updated.id = record.id
    updated.status = "pending"
    fake_repo.update.return_value = updated

    fake_auth = _make_auth_user()

    client = _build_client(fake_auth, fake_repo)

    with patch("app.api.routes.duplication.celery_app") as mock_celery:
        response = client.post(f"/duplication/records/{record.id}/retry")

    assert response.status_code == 200, response.text
    mock_celery.send_task.assert_called_once_with(
        "worker.process_duplication_check",
        args=[record.id],
    )


# ---------------------------------------------------------------------------
# 3. Repository update calls session.commit()
# ---------------------------------------------------------------------------


def test_repository_update_calls_session_commit():
    """duplication_repository 的 update 方法必须调用 session.commit()。"""
    from packages.adapters.sqlalchemy_impl.duplication_repository import (
        SQLAlchemyDuplicationRecordRepository,
    )
    from packages.adapters.sqlalchemy_impl.models import DuplicationRecordModel

    mock_session = MagicMock()
    mock_model = MagicMock(spec=DuplicationRecordModel)
    mock_model.id = "rec-1"

    mock_session.query.return_value.filter.return_value.first.return_value = mock_model

    repo = SQLAlchemyDuplicationRecordRepository(mock_session)

    record = DuplicationRecord.create(
        user_id="user-1",
        filename="test.mp4",
        file_size=1024,
        storage_key="duplication/abc/test.mp4",
    )
    record.status = "completed"
    record.duplicate_rate = 42.0
    record.duplicate_count = 1
    record.visual_similarity = 0.85
    record.match_count = 2

    result = repo.update(record)

    mock_session.commit.assert_called()
    assert result.visual_similarity == 0.85
    assert result.match_count == 2
