from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime
from typing import Any, Mapping

from rights_filter.analysis.llm_assistance import InternalLlmAssistant, OllamaGenerateLlmClient
from rights_filter.integrations.cloud_vision_web_detection import (
    CloudVisionWebDetectionAdapter,
    GoogleVisionRestClient,
)
from rights_filter.integrations.external_policy import ExternalApiPolicy
from rights_filter.integrations.google_custom_search import (
    GoogleCustomSearchAdapter,
    GoogleCustomSearchClient,
)
from rights_filter.integrations.naver_search import NaverOpenApiSearchClient, NaverSearchAdapter
from rights_filter.integrations.search_policy import SearchApiPolicy


@dataclass
class ProviderRuntime:
    naver_adapter: NaverSearchAdapter | None
    search_policy: SearchApiPolicy
    google_adapter: CloudVisionWebDetectionAdapter | None
    external_policy: ExternalApiPolicy
    google_custom_search_adapter: GoogleCustomSearchAdapter | None
    google_custom_search_policy: SearchApiPolicy
    face_crop_web_detection_enabled: bool
    auto_naver_query_limit: int
    auto_naver_blog_query_limit: int
    auto_naver_web_query_limit: int
    auto_google_custom_query_limit: int
    search_result_compare_limit: int
    search_result_page_image_limit: int
    search_result_similarity_threshold: float
    llm_assistant: InternalLlmAssistant | None
    provider_payloads: dict[str, dict[str, Any]]


def build_provider_runtime(
    env: Mapping[str, str],
    transport: Any | None = None,
) -> ProviderRuntime:
    naver_adapter = _naver_adapter(env, transport)
    google_adapter = _google_adapter(env, transport)
    google_custom_search_adapter = _google_custom_search_adapter(env, transport)
    llm_assistant = _llm_assistant(env, transport)
    search_limit = _optional_int(env.get("COPYRIGHTER_NAVER_DAILY_LIMIT"))
    google_limit = _optional_int(env.get("COPYRIGHTER_GOOGLE_DAILY_LIMIT"))
    google_custom_search_limit = _optional_int(env.get("COPYRIGHTER_GOOGLE_CUSTOM_SEARCH_DAILY_LIMIT"))
    llm_limit = _optional_int(env.get("COPYRIGHTER_LLM_DAILY_LIMIT"))
    face_crop_search_enabled = _truthy(env.get("COPYRIGHTER_GOOGLE_FACE_CROP_SEARCH"))
    auto_naver_query_limit = _bounded_int(
        env.get("COPYRIGHTER_AUTO_NAVER_QUERY_LIMIT"),
        default=3,
        minimum=0,
        maximum=10,
    )
    auto_naver_blog_query_limit = _bounded_int(
        env.get("COPYRIGHTER_AUTO_NAVER_BLOG_QUERY_LIMIT"),
        default=1,
        minimum=0,
        maximum=10,
    )
    auto_naver_web_query_limit = _bounded_int(
        env.get("COPYRIGHTER_AUTO_NAVER_WEB_QUERY_LIMIT"),
        default=1,
        minimum=0,
        maximum=10,
    )
    auto_google_custom_query_limit = _bounded_int(
        env.get("COPYRIGHTER_AUTO_GOOGLE_CUSTOM_QUERY_LIMIT"),
        default=2,
        minimum=0,
        maximum=10,
    )
    search_result_compare_limit = _bounded_int(
        env.get("COPYRIGHTER_SEARCH_RESULT_COMPARE_LIMIT"),
        default=3,
        minimum=0,
        maximum=20,
    )
    search_result_page_image_limit = _bounded_int(
        env.get("COPYRIGHTER_SEARCH_RESULT_PAGE_IMAGE_LIMIT"),
        default=3,
        minimum=0,
        maximum=10,
    )
    search_result_similarity_threshold = _bounded_float(
        env.get("COPYRIGHTER_SEARCH_RESULT_SIMILARITY_THRESHOLD"),
        default=0.9,
        minimum=0.0,
        maximum=1.0,
    )

    return ProviderRuntime(
        naver_adapter=naver_adapter,
        search_policy=SearchApiPolicy(
            disabled=naver_adapter is None,
            compliance_approved=naver_adapter is not None,
            daily_limit=search_limit,
        ),
        google_adapter=google_adapter,
        external_policy=ExternalApiPolicy(
            disabled=google_adapter is None,
            compliance_approved=google_adapter is not None,
            metadata_logging_accepted=google_adapter is not None,
            allow_online_sync=google_adapter is not None,
            daily_limit=google_limit,
        ),
        google_custom_search_adapter=google_custom_search_adapter,
        google_custom_search_policy=SearchApiPolicy(
            disabled=google_custom_search_adapter is None,
            compliance_approved=google_custom_search_adapter is not None,
            allowed_providers={"google_custom_search"},
            daily_limit=google_custom_search_limit,
        ),
        face_crop_web_detection_enabled=face_crop_search_enabled,
        auto_naver_query_limit=auto_naver_query_limit,
        auto_naver_blog_query_limit=auto_naver_blog_query_limit,
        auto_naver_web_query_limit=auto_naver_web_query_limit,
        auto_google_custom_query_limit=auto_google_custom_query_limit,
        search_result_compare_limit=search_result_compare_limit,
        search_result_page_image_limit=search_result_page_image_limit,
        search_result_similarity_threshold=search_result_similarity_threshold,
        llm_assistant=llm_assistant,
        provider_payloads={
            "internal": _provider_payload(
                "internal",
                "Internal analysis",
                True,
                "local only",
                1000,
                "없음",
                "로컬 지문, 지식 DB, 얼굴/사람 존재 신호만 사용",
                env=env,
            ),
            "naver": _provider_payload(
                "naver",
                "Naver search",
                naver_adapter is not None,
                "text-query API configured" if naver_adapter else "text-query client not configured",
                search_limit,
                "없음" if naver_adapter else "missing NAVER_CLIENT_ID or NAVER_CLIENT_SECRET",
                "텍스트 쿼리만 허용; 이미지 전송 금지",
                required_env=["NAVER_CLIENT_ID", "NAVER_CLIENT_SECRET"],
                env=env,
            ),
            "google": _provider_payload(
                "google",
                "Google Web Detection",
                google_adapter is not None,
                "REST images:annotate configured" if google_adapter else "client not configured",
                google_limit,
                "없음" if google_adapter else "missing GOOGLE_CLOUD_VISION_API_KEY",
                "승인된 파생 이미지만 허용",
                required_env=["GOOGLE_CLOUD_VISION_API_KEY"],
                env=env,
            ),
            "google_search": _provider_payload(
                "google_search",
                "Google Custom Search",
                google_custom_search_adapter is not None,
                "Programmable Search configured" if google_custom_search_adapter else "client not configured",
                google_custom_search_limit,
                "없음" if google_custom_search_adapter else "missing GOOGLE_CUSTOM_SEARCH_API_KEY or GOOGLE_CUSTOM_SEARCH_CX",
                "text-query web/image search; no submitted image upload",
                required_env=["GOOGLE_CUSTOM_SEARCH_API_KEY", "GOOGLE_CUSTOM_SEARCH_CX"],
                env=env,
            ),
            "llm": _provider_payload(
                "llm",
                "Ollama local LLM",
                llm_assistant is not None,
                f"Ollama local API configured ({env.get('OLLAMA_MODEL', 'qwen2.5:0.5b-instruct')})",
                llm_limit,
                "없음",
                "source-linked summary only",
                required_env=["OLLAMA_BASE_URL", "OLLAMA_MODEL"],
                env=env,
            ),
        },
    )


def _naver_adapter(env: Mapping[str, str], transport: Any | None) -> NaverSearchAdapter | None:
    client_id = env.get("NAVER_CLIENT_ID")
    client_secret = env.get("NAVER_CLIENT_SECRET")
    if not client_id or not client_secret:
        return None
    client = NaverOpenApiSearchClient(
        client_id=client_id,
        client_secret=client_secret,
        transport=transport,
        display=_optional_int(env.get("NAVER_SEARCH_DISPLAY")) or 10,
        sort=env.get("NAVER_SEARCH_SORT", "sim"),
        image_pages=_bounded_int(env.get("NAVER_SEARCH_PAGES"), default=1, minimum=1, maximum=10),
        blog_display=_optional_int(env.get("NAVER_BLOG_SEARCH_DISPLAY")) or 3,
        blog_sort=env.get("NAVER_BLOG_SEARCH_SORT", "sim"),
        blog_pages=_bounded_int(env.get("NAVER_BLOG_SEARCH_PAGES"), default=1, minimum=1, maximum=10),
        web_display=_optional_int(env.get("NAVER_WEB_SEARCH_DISPLAY")) or 3,
        web_pages=_bounded_int(env.get("NAVER_WEB_SEARCH_PAGES"), default=1, minimum=1, maximum=10),
    )
    return NaverSearchAdapter(client)


def _google_adapter(env: Mapping[str, str], transport: Any | None) -> CloudVisionWebDetectionAdapter | None:
    api_key = env.get("GOOGLE_CLOUD_VISION_API_KEY")
    if not api_key:
        return None
    client = GoogleVisionRestClient(
        api_key=api_key,
        transport=transport,
        parent=env.get("GOOGLE_CLOUD_VISION_PARENT"),
    )
    return CloudVisionWebDetectionAdapter(client)


def _google_custom_search_adapter(
    env: Mapping[str, str], transport: Any | None
) -> GoogleCustomSearchAdapter | None:
    api_key = env.get("GOOGLE_CUSTOM_SEARCH_API_KEY")
    cx = env.get("GOOGLE_CUSTOM_SEARCH_CX")
    if not api_key or not cx:
        return None
    client = GoogleCustomSearchClient(
        api_key=api_key,
        cx=cx,
        transport=transport,
        image_num=_bounded_int(env.get("GOOGLE_CUSTOM_SEARCH_IMAGE_RESULTS"), default=3, minimum=1, maximum=10),
        web_num=_bounded_int(env.get("GOOGLE_CUSTOM_SEARCH_WEB_RESULTS"), default=3, minimum=1, maximum=10),
        image_pages=_bounded_int(env.get("GOOGLE_CUSTOM_SEARCH_IMAGE_PAGES"), default=1, minimum=1, maximum=10),
        web_pages=_bounded_int(env.get("GOOGLE_CUSTOM_SEARCH_WEB_PAGES"), default=1, minimum=1, maximum=10),
    )
    return GoogleCustomSearchAdapter(client)


def _llm_assistant(env: Mapping[str, str], transport: Any | None) -> InternalLlmAssistant | None:
    client = OllamaGenerateLlmClient(
        base_url=env.get("OLLAMA_BASE_URL", "http://127.0.0.1:11434"),
        model=env.get("OLLAMA_MODEL", "qwen2.5:0.5b-instruct"),
        transport=transport,
    )
    return InternalLlmAssistant(client)


def _provider_payload(
    provider_id: str,
    name: str,
    enabled: bool,
    compliance: str,
    quota: int | None,
    last_failure: str,
    boundary: str,
    required_env: list[str] | None = None,
    env: Mapping[str, str] | None = None,
) -> dict[str, Any]:
    required_env = required_env or []
    env = env or {}
    return {
        "id": provider_id,
        "name": name,
        "enabled": enabled,
        "compliance": compliance,
        "usage": 0,
        "quota": quota,
        "lastSuccess": "없음",
        "lastFailure": last_failure,
        "boundary": boundary,
        "adminOnly": True,
        "requiredEnv": required_env,
        "configuredEnv": {key: bool(env.get(key)) for key in required_env},
    }


def _optional_int(value: str | None) -> int | None:
    if not value:
        return None
    try:
        return int(value)
    except (ValueError, TypeError):
        return None


def _bounded_int(value: str | None, default: int, minimum: int, maximum: int) -> int:
    if not value:
        return default
    try:
        parsed = int(value)
    except (ValueError, TypeError):
        return default
    return max(minimum, min(maximum, parsed))


def _bounded_float(value: str | None, default: float, minimum: float, maximum: float) -> float:
    if not value:
        return default
    try:
        parsed = float(value)
    except (ValueError, TypeError):
        return default
    return max(minimum, min(maximum, parsed))


def _truthy(value: str | None) -> bool:
    return str(value or "").strip().lower() in {"1", "true", "yes", "y", "on"}


def _now_label() -> str:
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
