from fastapi.testclient import TestClient
from sqlmodel import Session, select

from app.core.config import get_settings
from app.db.session import init_db, reset_db_for_tests
from app.main import app
from app.models.core import ActivityEvent, ApiCallLog, PostDraft, Provider, TikTokUploadAttempt, VideoAsset


def setup_test_db(tmp_path):
    settings = get_settings()
    settings.database_url = "sqlite://"
    settings.storage_root = tmp_path / "storage"
    settings.tiktok_client_id = None
    settings.tiktok_client_secret = None
    engine = init_db(settings)
    reset_db_for_tests(engine)
    return engine, TestClient(app)


def create_tiktok_fixture(session: Session, tmp_path):
    video_file = tmp_path / "video.mp4"
    video_file.write_bytes(b"fake video bytes")
    video = VideoAsset(
        candidate_id="sprint83-demo",
        package_id="package_sprint83_demo",
        source="prepared_package",
        file_path=str(video_file),
        file_size=video_file.stat().st_size,
        checksum="sprint83checksum",
        working_title="Sprint 8.3 TikTok Verification Demo",
        is_aigc=True,
        status="ready_for_review",
    )
    session.add(video); session.commit(); session.refresh(video)
    draft = PostDraft(
        video_asset_id=video.id,
        provider=Provider.tiktok,
        caption="TEST ONLY: TikTok verification demo caption",
        hashtags_json=["#onlinesafety", "#cybersecurity", "#testpreview"],
        status="draft",
    )
    session.add(draft); session.commit(); session.refresh(draft)
    return video, draft


def test_tiktok_mock_upload_draft_requires_consent(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        _, draft = create_tiktok_fixture(session, tmp_path)
    response = client.post(f"/api/publish/tiktok/upload-draft/{draft.id}", json={"mode": "mock"})
    assert response.status_code == 400
    assert "consent" in response.text.lower()


def test_tiktok_mock_upload_draft_creates_attempt_log_and_no_secret(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        video, draft = create_tiktok_fixture(session, tmp_path)
        video_id = video.id
        draft_id = draft.id
    result = client.post(f"/api/publish/tiktok/upload-draft/{draft_id}", json={"mode": "mock", "source": "FILE_UPLOAD", "consent_confirmed": True}).json()
    assert result["mode"] == "mock"
    assert result["scope"] == "video.upload"
    assert result["source"] == "FILE_UPLOAD"
    assert result["upload_status"] == "mock_uploaded_to_draft"
    assert result["publish_id"].startswith("mock_publish_")

    with Session(engine) as session:
        attempt = session.exec(select(TikTokUploadAttempt)).one()
        api_log = session.exec(select(ApiCallLog).where(ApiCallLog.provider == "tiktok")).one()
        events = session.exec(select(ActivityEvent).where(ActivityEvent.event_type.in_(["tiktok_demo_started", "tiktok_upload_init_mocked", "tiktok_file_upload_succeeded"]))).all()
        video = session.get(VideoAsset, video_id)
        assert attempt.publish_id == result["publish_id"]
        assert attempt.response_redacted_json["mock"] is True
        text = str(attempt.request_redacted_json) + str(attempt.response_redacted_json) + str(api_log.request_redacted_json) + str(api_log.response_redacted_json)
        lowered = text.lower()
        assert "client_secret" not in lowered
        assert "access_token': '" not in lowered
        assert "mock-secret-token" not in lowered
        assert "[redacted]" in lowered
        assert len(events) == 3
        assert video.external_tiktok_id == result["publish_id"]


def test_tiktok_verification_status_honest_without_sandbox_credentials(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        _, draft = create_tiktok_fixture(session, tmp_path)
    before = client.get("/api/verification/tiktok").json()
    assert before["sandbox_configured"] is False
    assert before["mode"] == "Mock"
    assert before["readiness"]["ready_for_tiktok_submission_video"] is False
    assert any("Sandbox not configured" in issue for issue in before["blocking_issues"])

    client.post(f"/api/publish/tiktok/upload-draft/{draft.id}", json={"mode": "mock", "source": "FILE_UPLOAD", "consent_confirmed": True})
    after = client.get("/api/verification/tiktok").json()
    assert after["readiness"]["ready_for_internal_rehearsal"] is True
    assert after["readiness"]["ready_for_tiktok_submission_video"] is False
    assert after["last_attempt"]["upload_status"] == "mock_uploaded_to_draft"


def test_tiktok_logs_show_verification_entry_redacted(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        _, draft = create_tiktok_fixture(session, tmp_path)
    client.post(f"/api/publish/tiktok/upload-draft/{draft.id}", json={"mode": "mock", "source": "FILE_UPLOAD", "consent_confirmed": True})
    logs = client.get("/api/logs?kind=tiktok_verification").json()["items"]
    assert len(logs) == 1
    payload = logs[0]["payload"]
    assert logs[0]["kind"] == "tiktok_verification"
    assert payload["endpoint"] == "/v2/post/publish/inbox/video/init/"
    assert payload["scope"] == "video.upload"
    assert payload["mode"] == "mock"
    serialized = str(payload).lower()
    assert "client_secret" not in serialized
    assert "access_token': '" not in serialized
    assert "mock-secret-token" not in serialized


def test_tiktok_real_disabled_mode_blocks_direct_post(tmp_path):
    engine, client = setup_test_db(tmp_path)
    with Session(engine) as session:
        _, draft = create_tiktok_fixture(session, tmp_path)
    response = client.post(f"/api/publish/tiktok/upload-draft/{draft.id}", json={"mode": "real_disabled", "consent_confirmed": True})
    assert response.status_code == 400
    assert "direct post" in response.text.lower()
