from __future__ import annotations

from types import SimpleNamespace

import pytest
from fastapi import HTTPException
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
from sqlalchemy.pool import StaticPool

from app.models import Base
from app.models.ai_config import SystemConfig
from app.services.voice_input_service import VoiceInputService, load_voice_config


def database() -> Session:
    engine = create_engine("sqlite:///:memory:", connect_args={"check_same_thread": False}, poolclass=StaticPool)
    Base.metadata.create_all(engine)
    return Session(engine)


def settings(**overrides):
    values = {
        "voice_input_enabled": False,
        "voice_max_duration_seconds": 60,
        "aliyun_nls_app_key": "app-key",
        "aliyun_nls_access_key_id": "voice-access-id",
        "aliyun_nls_access_key_secret": "voice-access-secret",
        "aliyun_nls_endpoint": "https://nls.example/asr",
    }
    values.update(overrides)
    return SimpleNamespace(**values)


def test_public_config_is_hidden_by_default_and_duration_is_clamped(monkeypatch):
    monkeypatch.setattr("app.services.voice_input_service.get_settings", lambda: settings())
    with database() as db:
        db.add_all([
            SystemConfig(config_key="voice_input_enabled", config_value="true"),
            SystemConfig(config_key="voice_max_duration_seconds", config_value="999"),
        ])
        db.commit()
        assert VoiceInputService.public_config(db) == {"enabled": True, "maxDurationSeconds": 60}


def test_transcription_endpoint_is_rejected_when_switch_is_off(monkeypatch):
    monkeypatch.setattr("app.services.voice_input_service.get_settings", lambda: settings())
    with database() as db, pytest.raises(HTTPException) as exc:
        VoiceInputService.transcribe(db, 1, b"audio", "audio/webm")
    assert exc.value.status_code == 404


def test_voice_credentials_are_loaded_independently_from_sms(monkeypatch):
    monkeypatch.setattr("app.services.voice_input_service.get_settings", lambda: settings())
    with database() as db:
        db.add_all([
            SystemConfig(config_key="aliyun_sms_access_key_id", config_value="sms-id"),
            SystemConfig(config_key="aliyun_nls_app_key", config_value="db-app-key"),
            SystemConfig(config_key="aliyun_nls_access_key_id", config_value="db-voice-id"),
            SystemConfig(config_key="aliyun_nls_access_key_secret", config_value="db-voice-secret"),
        ])
        db.commit()
        config = load_voice_config(db)
        assert config.app_key == "db-app-key"
        assert config.access_key_id == "db-voice-id"
        assert config.access_key_secret == "db-voice-secret"


def test_transcription_returns_provider_text(monkeypatch):
    monkeypatch.setattr("app.services.voice_input_service.get_settings", lambda: settings(voice_input_enabled=True))
    monkeypatch.setattr("app.services.voice_input_service._normalize_audio", lambda *_: b"wav")
    monkeypatch.setattr("app.services.voice_input_service._wav_duration", lambda *_: 8.4)
    monkeypatch.setattr("app.services.voice_input_service._create_aliyun_token", lambda *_: "token")
    monkeypatch.setattr("app.services.voice_input_service.SecurityStateService.enforce_limit", lambda *_, **__: None)

    class Response:
        status_code = 200

        @staticmethod
        def json():
            return {"status": 20000000, "result": "这是转写结果。"}

    monkeypatch.setattr("app.services.voice_input_service.httpx.post", lambda *_, **__: Response())
    with database() as db:
        result = VoiceInputService.transcribe(db, 1, b"audio", "audio/webm")
    assert result == {"text": "这是转写结果。", "durationSeconds": 8.4}
