from __future__ import annotations

import re
import secrets
from typing import Any

from backend.bootstrap.config import TOKEN_PATTERN, validate_text


class SystemServiceMixin:
    def _load_system_credentials(self, environment: dict[str, str]) -> dict[str, Any]:
        encrypted = self.database.get_system_setting("credentials")
        current = self.vault.decrypt_json(encrypted) if encrypted else {}
        changed = False
        first_user_id = self.database.first_user_id()
        first_personal: dict[str, Any] = {}
        if first_user_id:
            first_encrypted = self.database.get_user_credentials(first_user_id)
            first_personal = self.vault.decrypt_json(first_encrypted) if first_encrypted else {}
        defaults = {
            "tushare_token": environment.get("tushare_token") or first_personal.get("tushare_token") or "",
            "ifind_refresh_token": environment.get("ifind_refresh_token") or "",
            "ifind_access_token": environment.get("ifind_access_token") or "",
            "platform_llm_primary_api_key": environment.get("platform_llm_primary_api_key") or first_personal.get("llm_primary_api_key") or "",
            "platform_llm_primary_base_url": environment.get("platform_llm_primary_base_url") or first_personal.get("llm_primary_base_url") or "https://api.openai.com/v1",
            "platform_llm_primary_model": environment.get("platform_llm_primary_model") or first_personal.get("llm_primary_model") or "",
            "platform_llm_fallback_api_key": environment.get("platform_llm_fallback_api_key") or first_personal.get("llm_fallback_api_key") or "",
            "platform_llm_fallback_base_url": environment.get("platform_llm_fallback_base_url") or first_personal.get("llm_fallback_base_url") or "",
            "platform_llm_fallback_model": environment.get("platform_llm_fallback_model") or first_personal.get("llm_fallback_model") or "",
            "member_daily_limit": 50,
            "background_refresh_enabled": True,
        }
        for key, value in defaults.items():
            if key not in current:
                current[key] = value
                changed = True
        if not isinstance(current.get("llm_models"), list):
            migrated_models: list[dict[str, str]] = []
            for role, label in (("primary", "原主模型"), ("fallback", "原辅助模型")):
                profile = {
                    "api_key": str(current.get(f"platform_llm_{role}_api_key") or ""),
                    "base_url": str(current.get(f"platform_llm_{role}_base_url") or ""),
                    "model": str(current.get(f"platform_llm_{role}_model") or ""),
                }
                if profile["api_key"] or profile["model"]:
                    model_id = f"migrated-{role}"
                    migrated_models.append(
                        {"id": model_id, "name": label, **profile}
                    )
                    current[f"{role}_model_id"] = model_id
            current["llm_models"] = migrated_models
            current.setdefault("primary_model_id", "")
            current.setdefault("fallback_model_id", "")
            changed = True
        if changed or not encrypted:
            self.database.save_system_setting("credentials", self.vault.encrypt_json(current))
        for row in self.database.list_user_credentials():
            personal = self.vault.decrypt_json(str(row.get("encrypted_payload") or ""))
            if "tushare_token" in personal:
                personal.pop("tushare_token", None)
                self.database.save_user_credentials(
                    int(row["user_id"]), self.vault.encrypt_json(personal)
                )
        return current

    def _save_system_credentials(self, credentials: dict[str, Any]) -> None:
        with self.system_lock:
            self.database.save_system_setting("credentials", self.vault.encrypt_json(credentials))
            self._system_credentials = dict(credentials)
            if hasattr(self, "ifind"):
                self.ifind.set_credentials(
                    str(credentials.get("ifind_refresh_token") or ""),
                    str(credentials.get("ifind_access_token") or ""),
                )

    @property
    def configured(self) -> bool:
        return bool(self.token)

    def _credentials(self) -> dict[str, str]:
        credentials = getattr(self._request_context, "credentials", {})
        return {
            "llm_primary_api_key": str(credentials.get("llm_primary_api_key") or ""),
            "llm_primary_base_url": str(
                credentials.get("llm_primary_base_url") or "https://api.openai.com/v1"
            ),
            "llm_primary_model": str(credentials.get("llm_primary_model") or ""),
            "llm_fallback_api_key": str(credentials.get("llm_fallback_api_key") or ""),
            "llm_fallback_base_url": str(credentials.get("llm_fallback_base_url") or ""),
            "llm_fallback_model": str(credentials.get("llm_fallback_model") or ""),
        }

    def _save_credentials(self, credentials: dict[str, str]) -> None:
        self.database.save_user_credentials(
            self.current_user_id,
            self.vault.encrypt_json(credentials),
        )
        self._request_context.credentials = dict(credentials)

    @property
    def token(self) -> str:
        return str(self._system_credentials.get("tushare_token") or "")

    def system_status(self) -> dict[str, Any]:
        platform = self._platform_llm_profile()
        model_pool = []
        for item in self._system_credentials.get("llm_models") or []:
            if not isinstance(item, dict):
                continue
            profile = {
                "api_key": str(item.get("api_key") or ""),
                "base_url": str(item.get("base_url") or ""),
                "model": str(item.get("model") or ""),
            }
            model_pool.append(
                {
                    "id": str(item.get("id") or ""),
                    "name": str(item.get("name") or ""),
                    "base_url": profile["base_url"],
                    "model": profile["model"],
                    "configured": self._profile_configured(profile),
                }
            )
        return {
            "data": {
                "configured": self.configured,
                "ifind": self.ifind.status(),
                "background_refresh_enabled": bool(
                    self._system_credentials.get("background_refresh_enabled", True)
                ),
                **self.database.status(),
                "jobs": self.jobs.repository.recent(12),
            },
            "llm": {
                "primary_configured": self._profile_configured(platform["primary"]),
                "fallback_configured": self._profile_configured(platform["fallback"]),
                "models": model_pool,
                "primary_model_id": str(self._system_credentials.get("primary_model_id") or ""),
                "fallback_model_id": str(self._system_credentials.get("fallback_model_id") or ""),
            },
            "membership": {
                "member_daily_limit": max(
                    1, int(self._system_credentials.get("member_daily_limit") or 50)
                )
            },
        }

    def save_system_settings(self, payload: dict[str, Any]) -> dict[str, Any]:
        current = dict(self._system_credentials)
        token = str(payload.get("tushare_token") or current.get("tushare_token") or "").strip()
        if token and not TOKEN_PATTERN.fullmatch(token):
            raise ValueError("Tushare Token 格式不正确。")
        ifind_refresh_token = str(
            payload.get("ifind_refresh_token")
            or current.get("ifind_refresh_token")
            or ""
        ).strip()
        if ifind_refresh_token and (
            len(ifind_refresh_token) > 2048
            or any(character.isspace() for character in ifind_refresh_token)
        ):
            raise ValueError("iFinD Refresh Token 格式不正确。")
        existing_models = {
            str(item.get("id") or ""): item
            for item in current.get("llm_models") or []
            if isinstance(item, dict) and item.get("id")
        }
        raw_models = payload.get("models")
        models: list[dict[str, str]] = []
        if raw_models is not None:
            if not isinstance(raw_models, list) or len(raw_models) > 20:
                raise ValueError("模型池格式不正确，最多可保存 20 个模型。")
            seen_ids: set[str] = set()
            seen_names: set[str] = set()
            for index, raw in enumerate(raw_models, start=1):
                if not isinstance(raw, dict):
                    raise ValueError("模型池条目格式不正确。")
                model_id = str(raw.get("id") or f"model-{secrets.token_hex(6)}").strip()
                if not re.fullmatch(r"[A-Za-z0-9_-]{3,80}", model_id) or model_id in seen_ids:
                    raise ValueError("模型 ID 不正确或重复。")
                name = validate_text(raw.get("name"), f"模型 {index} 名称", 50, required=True)
                normalized_name = name.casefold()
                if normalized_name in seen_names:
                    raise ValueError("模型名称不能重复。")
                profile = self._validate_llm_profile(
                    raw,
                    existing_models.get(model_id) or {},
                    required=True,
                    label=name,
                )
                models.append({"id": model_id, "name": name, **profile})
                seen_ids.add(model_id)
                seen_names.add(normalized_name)
        else:
            models = [dict(item) for item in existing_models.values()]
        model_ids = {item["id"] for item in models}
        primary_model_id = str(
            payload.get("primary_model_id", current.get("primary_model_id") or "") or ""
        ).strip()
        fallback_model_id = str(
            payload.get("fallback_model_id", current.get("fallback_model_id") or "") or ""
        ).strip()
        if models and primary_model_id not in model_ids:
            raise ValueError("请从模型池选择主模型。")
        if not models:
            primary_model_id = ""
            fallback_model_id = ""
        if fallback_model_id and fallback_model_id not in model_ids:
            raise ValueError("辅助模型不在模型池中。")
        if fallback_model_id and fallback_model_id == primary_model_id:
            raise ValueError("主模型与辅助模型不能相同。")
        try:
            daily_limit = max(
                1,
                min(
                    1000,
                    int(payload.get("member_daily_limit", current.get("member_daily_limit") or 50)),
                ),
            )
        except (TypeError, ValueError) as exc:
            raise ValueError("会员每日额度应为 1 至 1000。") from exc
        current.update(
            {
                "tushare_token": token,
                "ifind_refresh_token": ifind_refresh_token,
                "llm_models": models,
                "primary_model_id": primary_model_id,
                "fallback_model_id": fallback_model_id,
                "member_daily_limit": daily_limit,
                "background_refresh_enabled": bool(
                    payload.get(
                        "background_refresh_enabled",
                        current.get("background_refresh_enabled", True),
                    )
                ),
            }
        )
        self._save_system_credentials(current)
        return self.system_status()

    def status(self) -> dict[str, Any]:
        llm_access = self.llm_access_status()
        return {
            "configured": self.configured,
            "mode": "tushare" if self.configured else "unavailable",
            "llm_configured": self.llm_configured,
            "llm_model": self.llm_primary_model if self.llm_configured else "",
            "llm_fallback_configured": self.llm_fallback_configured,
            "llm_fallback_model": self.llm_fallback_model if self.llm_fallback_configured else "",
            "llm_access": llm_access,
            "birth_profile_configured": bool(self.stored_birth_profile()),
            "birth_profile": self.stored_birth_profile(),
            **self.database.status(),
        }
