from __future__ import annotations

from datetime import datetime
from typing import Any, Dict, Optional

from pydantic import BaseModel, Field, HttpUrl, model_validator

from turbo_agent_core.schema.enums import AuthType, KeyLocation, RequestMethod
from turbo_agent_core.schema.external import (
    Platform as CorePlatform,
    AuthMethod as CoreAuthMethod,
    Secret as CoreSecret,
)
from turbo_agent_core.schema.basic import Endpoint, WebProtocol


# Platform
class PlatformCreate(BaseModel):
    id: str = Field(description="平台ID（外部提供）")
    orgId: str = Field(description="组织ID")
    nameId: str = Field(description="平台名称标识（与orgId联合唯一）")
    name: str
    code: str = Field(description="平台编码/slug（非唯一）")
    baseUrl: Optional[HttpUrl] = None
    description: Optional[str] = None
    isActive: Optional[bool] = True


class PlatformUpdate(BaseModel):
    name: Optional[str] = None
    baseUrl: Optional[HttpUrl] = None
    description: Optional[str] = None
    isActive: Optional[bool] = None


class PlatformOut(CorePlatform):
    id: str
    orgId: str
    nameId: str
    name: str
    code: str
    baseUrl: Optional[HttpUrl]
    description: Optional[str]
    isActive: bool
    createdAt: datetime
    updatedAt: datetime

    # Core fields defaults
    image_url: str = ""
    name_en: str = ""
    officialUrl: str = ""
    endpoint: Endpoint = Field(default_factory=lambda: Endpoint(id="placeholder"))

    @model_validator(mode='before')
    @classmethod
    def _fill_core_fields(cls, data: Any) -> Any:
        # Helper to get attribute or item
        def get(obj, key, default=None):
            if isinstance(obj, dict):
                return obj.get(key, default)
            return getattr(obj, key, default)

        # If data is an object (Prisma model), convert to dict to inject fields
        # This is a bit expensive but ensures compatibility
        if not isinstance(data, dict):
            try:
                # Attempt to convert to dict if possible, or build one
                d = {}
                for field in ['id', 'orgId', 'nameId', 'name', 'code', 'baseUrl', 'description', 'isActive', 'createdAt', 'updatedAt']:
                    val = get(data, field)
                    if val is not None:
                        d[field] = val
                data = d
            except Exception:
                pass

        if isinstance(data, dict):
            if 'name_en' not in data:
                data['name_en'] = data.get('code', '')
            if 'officialUrl' not in data:
                data['officialUrl'] = str(data.get('baseUrl') or '')
            if 'endpoint' not in data:
                base_url = str(data.get('baseUrl') or '')
                data['endpoint'] = Endpoint(
                    id=f"ep_{data.get('id', 'unknown')}",
                    baseURL=base_url,
                    protocol=WebProtocol.HTTPS if base_url.startswith("https") else WebProtocol.HTTP
                )
        return data


# AuthMethod
class AuthMethodCreate(BaseModel):
    id: str = Field(description="授权方式ID（外部提供）")
    platformId: str
    name: str
    authType: AuthType
    description: Optional[str] = Field(default=None, description="指导用户如何配置和获取凭证的说明文字")
    loginFlow: Optional[Dict[str, Any]] = Field(default=None, description="描述首次登录请求")
    loginFieldsSchema: Optional[Dict[str, Any]] = Field(default=None, description="首次登录所需用户提供字段的 JSON Schema")
    refreshFlow: Optional[Dict[str, Any]] = Field(default=None, description="描述刷新请求")
    refreshFieldsSchema: Optional[Dict[str, Any]] = Field(default=None, description="刷新流程所需 Secret 内部字段的 JSON Schema")
    authFieldsSchema: Optional[Dict[str, Any]] = Field(default=None, description="用户直接提供授权数据的 JSON Schema（完整的 JSON Schema 对象）")
    authFieldPlacements: Optional[Dict[str, Any]] = Field(default=None, description="授权字段放置策略，如 header/query/cookie 等信息")
    responseMapping: Optional[Dict[str, Any]] = None
    defaultValiditySeconds: Optional[int] = Field(default=None, ge=0)
    refreshBeforeSeconds: Optional[int] = Field(default=None, ge=0)


class AuthMethodUpdate(BaseModel):
    name: Optional[str] = None
    description: Optional[str] = None
    loginFlow: Optional[Dict[str, Any]] = None
    loginFieldsSchema: Optional[Dict[str, Any]] = None
    refreshFlow: Optional[Dict[str, Any]] = None
    refreshFieldsSchema: Optional[Dict[str, Any]] = None
    authFieldsSchema: Optional[Dict[str, Any]] = None
    authFieldPlacements: Optional[Dict[str, Any]] = None
    responseMapping: Optional[Dict[str, Any]] = None
    defaultValiditySeconds: Optional[int] = Field(default=None, ge=0)
    refreshBeforeSeconds: Optional[int] = Field(default=None, ge=0)


class AuthMethodOut(CoreAuthMethod):
    platformId: str
    type: AuthType
    createdAt: datetime
    updatedAt: datetime

    @model_validator(mode='before')
    @classmethod
    def _fill_core_auth_fields(cls, data: Any) -> Any:
        def get(obj, key, default=None):
            if isinstance(obj, dict):
                return obj.get(key, default)
            return getattr(obj, key, default)

        if not isinstance(data, dict):
             try:
                d = {}
                for field in ['id', 'platformId', 'name', 'type', 'description', 'loginFlow', 'loginFieldsSchema', 
                              'refreshFlow', 'refreshFieldsSchema', 'authFieldsSchema', 'authFieldPlacements', 
                              'responseMapping', 'defaultValiditySeconds', 'refreshBeforeSeconds', 'createdAt', 'updatedAt']:
                    val = get(data, field)
                    if val is not None:
                        d[field] = val
                data = d
             except Exception:
                 pass

        if isinstance(data, dict):
            if 'authType' not in data and 'type' in data:
                data['authType'] = data['type']
        
        return data


# Secret
class SecretUpsert(BaseModel):
    id: Optional[str] = Field(default=None, description="秘钥ID（创建时必填，更新时可不填）")
    name: str = Field(description="秘钥名称（在同一授权方式下唯一）")
    tags: list[str] = Field(default_factory=list, description="标签列表")
    credential: Dict[str, Any]
    loginPayload: Optional[Dict[str, Any]] = Field(default=None, description="第一阶段登录所需字段")
    autoLoginEnabled: Optional[bool] = Field(default=None, description="是否启用自动登录并保存Token（若为False则不校验也不保存loginPayload）")
    expiresAt: Optional[datetime] = None
    validFrom: Optional[datetime] = None
    status: Optional[str] = "active"
    # 用户绑定（不再持久化用户名/密码）
    userEmail: Optional[str] = Field(default=None, description="用户邮箱（用于提醒）")


class SecretOut(CoreSecret):
    platformId: str
    authMethodId: str
    type: Optional[AuthType] = None
    name: str
    tags: list[str]
    data: Dict[str, Any]
    authDataSources: Optional[Dict[str, Any]]
    loginPayload: Optional[Dict[str, Any]]
    autoLoginEnabled: bool
    expiresAt: Optional[datetime]
    validFrom: Optional[datetime]
    lastRefreshed: Optional[datetime]
    status: str
    isValid: bool
    lastLoginAt: Optional[datetime]
    lastLoginStatus: Optional[str]
    lastLoginError: Optional[str]
    userEmail: Optional[str]
    lastReminderAt: Optional[datetime]
    invalidNotified: bool | None = Field(default=None, description="该秘钥失效后是否已通知用户")
    usageMapping: Optional[Dict[str, Any]] = Field(default=None, description="如何在最终请求中应用该秘钥（等同于 AuthMethod.authFieldPlacements）")
    createdAt: datetime
    updatedAt: datetime

    @model_validator(mode='before')
    @classmethod
    def _fill_core_secret_fields(cls, data: Any) -> Any:
        def get(obj, key, default=None):
            if isinstance(obj, dict):
                return obj.get(key, default)
            return getattr(obj, key, default)

        if not isinstance(data, dict):
             try:
                d = {}
                for field in ['id', 'platformId', 'authMethodId', 'name', 'tags', 'data', 'authDataSources', 
                              'loginPayload', 'autoLoginEnabled', 'expiresAt', 'validFrom', 'lastRefreshed', 
                              'status', 'isValid', 'lastLoginAt', 'lastLoginStatus', 'lastLoginError', 
                              'userEmail', 'lastReminderAt', 'invalidNotified', 'createdAt', 'updatedAt']:
                    val = get(data, field)
                    if val is not None:
                        d[field] = val
                
                val = get(data, 'usageMapping')
                if val is not None:
                    d['usageMapping'] = val
                
                val = get(data, 'type')
                if val is not None:
                    d['type'] = val
                
                data = d
             except Exception:
                 pass

        if isinstance(data, dict):
            if 'credential' not in data and 'data' in data:
                data['credential'] = data['data']
            if 'identifier' not in data:
                data['identifier'] = data.get('name', '')
        
        return data


class SecretUpdate(BaseModel):
    name: Optional[str] = None
    tags: Optional[list[str]] = None
    credential: Optional[Dict[str, Any]] = None
    loginPayload: Optional[Dict[str, Any]] = None
    autoLoginEnabled: Optional[bool] = None
    expiresAt: Optional[datetime] = None
    validFrom: Optional[datetime] = None
    status: Optional[str] = None
    isValid: Optional[bool] = None
    userEmail: Optional[str] = None
    invalidNotified: Optional[bool] = None


class SecretLoginRequest(BaseModel):
    loginPayload: Optional[Dict[str, Any]] = Field(default=None, description="本次登录使用的字段覆盖")
    storeLoginPayload: bool = Field(default=True, description="是否将本次登录字段存储至秘钥")
    proxy: Optional[str] = Field(default=None, description="可选：通过该代理执行登录（HTTP/HTTPS/SOCKS），仅本次生效，不会保存")


class SecretRemindRequest(BaseModel):
    email: Optional[str] = Field(default=None, description="覆盖保存/使用的提醒邮箱")


class SecretInvalidateRequest(BaseModel):
    reason: Optional[str] = Field(default=None, description="失效原因备注（可选）")


class SecretInvalidNotifiedRequest(BaseModel):
    notified: bool = Field(default=True, description="是否已向用户发出失效通知")


class PreAuthRequest(BaseModel):
    loginPayload: Dict[str, Any] = Field(..., description="登录所需的字段数据")
    proxy: Optional[str] = Field(default=None, description="可选：通过该代理执行登录（HTTP/HTTPS/SOCKS）")


class ParseResponseRequest(BaseModel):
    authMethodId: str
    body: Optional[Dict[str, Any]] = None
    headers: Optional[Dict[str, Any]] = None
    cookies: Optional[Dict[str, Any]] = None
    rawBody: Optional[str] = Field(default=None, description="原始响应文本（解析 HTML、非 JSON 场景使用）")


class ParseResponseResult(BaseModel):
    extracted: Dict[str, Any] = Field(default_factory=dict)
    fieldSources: Dict[str, str] = Field(default_factory=dict)
    expiresAt: Optional[datetime] = None


# 执行跟踪（登录流程）
class LoginStepRequestSnapshot(BaseModel):
    method: str
    url: str
    headers: Dict[str, Any] | None = None
    params: Dict[str, Any] | None = None
    cookies: Dict[str, Any] | None = None
    json_body: Dict[str, Any] | None = Field(default=None, alias="json")
    data: Any | None = None

    class Config:
        populate_by_name = True


class LoginStepResult(BaseModel):
    index: int
    name: Optional[str] = None
    request: LoginStepRequestSnapshot
    statusCode: Optional[int] = None
    ok: bool = False
    durationMs: Optional[int] = None
    extracted: Dict[str, Any] = Field(default_factory=dict)
    fieldSources: Dict[str, str] = Field(default_factory=dict)
    error: Optional[str] = None


class LoginExecutionResult(BaseModel):
    steps: list[LoginStepResult] = Field(default_factory=list)
    finalExtracted: Dict[str, Any] = Field(default_factory=dict)
    finalFieldSources: Dict[str, str] = Field(default_factory=dict)
    finalExpiresAt: Optional[datetime] = None
    success: bool = False
    error: Optional[str] = None


class SecretLoginWithExecution(BaseModel):
    secret: "SecretOut"
    execution: LoginExecutionResult


class DefaultSchemaOut(BaseModel):
    authType: AuthType
    description: Optional[str] = None
    loginFlow: Optional[Dict[str, Any]] = None
    loginFieldsSchema: Optional[Dict[str, Any]] = None
    refreshFlow: Optional[Dict[str, Any]] = None
    refreshFieldsSchema: Optional[Dict[str, Any]] = None
    authFieldsSchema: Optional[Dict[str, Any]] = None
    authFieldPlacements: Optional[Dict[str, Any]] = None
    responseMapping: Optional[Dict[str, Any]] = None


class CallbackInfo(BaseModel):
    callbackUrl: Optional[str] = Field(default=None, description="从环境变量解析的回调地址")
    configured: bool = Field(default=False, description="是否已配置回调地址")


class IpAllowListInfo(BaseModel):
    ipAllowList: list[str] = Field(default_factory=list, description="出口 IP 白名单列表")
    configured: bool = Field(default=False, description="是否配置了出口 IP 白名单")
