import json

ARK_ERRORS = {
    "MissingParameter": "请求缺少必要参数，请查阅API文档。",
    "InvalidParameter": "请求包含非法参数，请查阅API文档。",
    "InvalidEndpoint.ClosedEndpoint": "推理接入点处于已关闭或暂时不可用，请稍后重试，或联系管理员。",
    "SensitiveContentDetected": "输入文本可能包含敏感信息，请使用其他prompt。",
    "SensitiveContentDetected.SevereViolation": "输入文本可能包含严重违规相关信息，请使用其他prompt。",
    "SensitiveContentDetected.Violence": "输入文本可能包含激进行为相关信息，请使用其他prompt。",
    "InputTextSensitiveContentDetected": "输入文本可能包含敏感信息，请更换后重试。",
    "InputImageSensitiveContentDetected": "输入图像可能包含敏感信息，请更换后重试。",
    "InputVideoSensitiveContentDetected": "输入视频可能包含敏感信息，请更换后重试。",
    "InputAudioSensitiveContentDetected": "输入音频可能包含敏感信息，请更换后重试。",
    "OutputTextSensitiveContentDetected": "生成的文字可能包含敏感信息，请更换输入内容后重试。",
    "OutputImageSensitiveContentDetected": "生成的图像可能包含敏感信息，请更换输入内容后重试。",
    "OutputVideoSensitiveContentDetected": "生成的视频可能包含敏感信息，请更换输入内容后重试。",
    "OutputVideoSensitiveContentDetected.PolicyViolation": "生成的视频可能涉及版权限制，请修改提示词。",
    "OutputAudioSensitiveContentDetected": "生成的音频可能包含敏感信息，请更换输入内容后重试。",
    "InputTextSensitiveContentDetected.PolicyViolation": "输入文本可能违反平台规定，请更换后重试。",
    "InputImageSensitiveContentDetected.PolicyViolation": "输入图片可能违反平台规定，请更换后重试。",
    "InputVideoSensitiveContentDetected.PolicyViolation": "输入视频可能违反平台规定，请更换后重试。",
    "InputAudioSensitiveContentDetected.PolicyViolation": "输入音频可能违反平台规定，请更换后重试。",
    "InputImageSensitiveContentDetected.PrivacyInformation": "输入图片可能包含真人，请更换后重试。",
    "InputVideoSensitiveContentDetected.PrivacyInformation": "输入视频可能包含真人，请更换后重试。",
    "InputTextRiskDetection": "风险识别产品检测到输入文本可能包含敏感信息，请更换后重试。",
    "InputImageRiskDetection": "风险识别产品检测到输入图片可能包含敏感信息，请更换后重试。",
    "OutputTextRiskDetection": "风险识别产品检测到输出文本可能包含敏感信息，请更换后重试。",
    "OutputImageRiskDetection": "风险识别产品检测到输出图片可能包含敏感信息，请更换后重试。",
    "ContentSecurityDetectionError": "风险识别产品请求失败。",
    "InvalidParameter.InvalidParameter": "请求参数值不合法，请检查参数值的正确性后重试。",
    "MissingParameter.MissingParameter": "缺少必要的请求参数，请确认请求参数后重试。",
    "Duplicate.Tags.Key": "对象的标签存在重复Key。",
    "InvalidArgumentError": "请求中的messages列表里，有消息体缺少role字段。",
    "InvalidArgumentError.UnknownRole": "消息体中的role值不被支持，如user_。",
    "InvalidArgumentError.InvalidImageDetail": "image_url中的detail参数值无效，只接受\"auto\", \"high\", \"low\"。",
    "InvalidArgumentError.InvalidPixelLimit": "用户自定义的图片像素限制无效（例如min_pixels > max_pixels，或超出了服务配置的范围）。",
    "InvalidImageURL.EmptyURL": "传入的图片URL为空。",
    "InvalidImageURL.InvalidFormat": "无法解析或处理图片，可能是Base64格式不正确、图片数据损坏或格式不支持。",
    "OutofContextError": "当请求中包含图片时，文本和图片编码后的总token数超过了模型上下文长度限制。",
    "InvalidParameter.UnsupportedParameter": "传入的参数在此推理接入点不可用。",
    "InvalidSubscription": "Coding Plan套餐未订阅或已过期。",
    "AuthenticationError": "请求携带的API Key或AK/SK校验未通过，请重新检查设置的鉴权凭证。",
    "InvalidAccountStatus": "当前使用的账号异常。",
    "OperationDenied.InvalidState": "请求所关联的Context ID处于非空闲状态，不可调用。",
    "OperationDenied.ConflictedValidationSet": "无法同时上传验证集和设置训练集取样为验证集百分比，不支持该操作。",
    "OperationDenied.PermissionDenied": "您没有权限访问基础模型的配置，不支持该操作。",
    "OperationDenied.UnsupportedCustomizationType": "模型不支持该训练方法，不支持该操作。",
    "OperationDenied.CustomizationNotSupported": "基础模型的版本不支持该训练方法，不支持该操作。",
    "OperationDenied.ServiceNotOpen": "模型服务不可用，请前往激活模型服务，或提交工单联系我们。",
    # "OperationDenied.ServiceOverdue": "您的账单已逾期，请前往费用中心充值。",
    "OperationDenied.ServiceOverdue": "系统使用高峰，请稍后重试",
    #"AccountOverdueError": "当前账号欠费（余额<0），如需继续调用，请前往费用中心进行充值。",
    "AccountOverdueError": "系统使用高峰，请稍后重试",
    "AccessDenied": "没有访问该资源的权限，请检查权限设置，或联系管理员添加白名单。",
    "OperationDenied.UnsupportedPhase": "操作失败，操作目标在特殊状态，请检查目标是否存在或者被锁定等特殊状态中。",
    "OperationDenied.FileQuotaExceeded": "当前账号已耗尽文件存储额度，如需继续使用，请删除历史文件。",
    "InvalidEndpointOrModel.NotFound": "模型或者推理接入点不存在或者您无权访问它。",
    "ModelNotOpen": "当前账号暂未开通模型服务，请前往开通管理页开通对应模型服务。",
    "NotFound.NotFound": "指定资源找不到，请确认参数后重试。",
    "InvalidEndpointOrModel.ModelIDAccessDisabled": "未能找到指定的模型ID，您的账号不允许使用模型ID来调用模型，请使用有权限的推理接入点ID来调用模型服务。",
    "UnsupportedModel": "当前模型不支持Coding Plan。",
    "RateLimitExceeded.EndpointRPMExceeded": "请求所关联的推理接入点已超过RPM(Requests Per Minute)限制，请稍后重试。",
    "RateLimitExceeded.EndpointTPMExceeded": "请求所关联的推理接入点已超过TPM(Tokens Per Minute)限制，请稍后重试。",
    "ModelAccountRpmRateLimitExceeded": "请求已超过帐户模型RPM(Requests Per Minute)限制，请稍后重试，或联系平台技术同学进行解决。",
    "ModelAccountTpmRateLimitExceeded": "请求已超过帐户模型TPM(Tokens Per Minute)限制，请稍后重试，或联系平台技术同学进行解决。",
    "APIAccountRpmRateLimitExceeded": "当前账号该接口的RPM(Requests Per Minute)限制已超出，请稍后重试。",
    "ModelAccountIpmRateLimitExceeded": "请求已超过账户模型IPM(Images Per Minute)限制，请稍后重试，或联系平台技术同学进行解决。",
    "QuotaExceeded": "当前账号对模型的免费试用额度已消耗完毕，如需继续调用，请前往开通管理页开通对应模型服务。",
    "ServerOverloaded": "服务资源紧张，请稍后重试。常出现在调用流量突增或刚开始调用长时间未使用的推理接入点。",
    "RequestBurstTooFast": "请求量激增触发系统保护，请放缓流量提升速度，逐步增加请求量后再尝试。",
    "SetLimitExceeded": "当前账号已达到模型推理限制，模型服务已暂停。如需继续使用该模型，请联系平台。",
    "InternalError": "服务内部错误，请稍后重试。",
    "ServiceUnavailable": "服务暂时不可用，请稍后重试。",
    "Timeout": "请求超时，请重试。",
    "TaskCancelled": "任务已被取消。",
    "UnsupportedOperation": "当前操作不支持，请联系管理员。",
    "PermissionDenied": "没有权限执行此操作。",
    "ResourceNotFound": "请求的资源不存在。",
    "InvalidApiKey": "API密钥无效，请联系管理员。",
    "InvalidSignature": "签名验证失败。",
    "InvalidToken": "Token无效或已过期。",
    "RequestTimeout": "请求超时，请重试。",
    "TooManyRequests": "请求过于频繁，请稍后重试。",
    "MethodNotAllowed": "不支持的HTTP方法。",
    "MediaTypeNotSupported": "不支持的媒体类型。",
    "RequestEntityTooLarge": "请求体过大。",
    "InvalidRequest": "请求格式无效。",
    "ResourceExists": "资源已存在。",
    "ResourceNotReady": "资源尚未就绪。",
    "GenerationFailed": "生成失败，请重试。",
    "AudioGenerationFailed": "音频生成失败。",
    "VideoGenerationFailed": "视频生成失败。",
    "ImageGenerationFailed": "图片生成失败。",
    "ModelNotFound": "模型不存在。",
    "ModelNotAvailable": "模型暂不可用。",
    "RegionNotSupported": "当前区域不支持。",
    "AccountNotActivated": "账户尚未激活。",
    "AccountSuspended": "账户已被暂停。",
    "InsufficientBalance": "余额不足，请充值。",
    "FeatureNotEnabled": "功能未启用。",
    "VersionNotSupported": "版本不支持。",
    "InvalidDuration": "无效的时长参数。",
    "InvalidSize": "无效的尺寸参数。",
    "InvalidFormat": "无效的格式参数。",
    "InvalidAspectRatio": "无效的画面比例。",
    "InvalidResolution": "无效的分辨率。",
    "FrameRateNotSupported": "不支持的帧率。",
    "BitrateNotSupported": "不支持的码率。",
    "AudioNotSupported": "音频格式不支持。",
    "VideoCodecNotSupported": "视频编码不支持。",
    "WatermarkFailed": "水印添加失败。",
    "TranscodeFailed": "转码失败。",
    "StorageFailed": "存储失败。",
    "DownloadFailed": "下载失败。",
    "UploadFailed": "上传失败。",
    "ProcessingFailed": "处理失败。",
    "ValidationFailed": "验证失败。",
    "AuthorizationFailed": "授权失败。",
    "AuthenticationFailed": "认证失败。",
    "SessionExpired": "会话已过期。",
    "SessionNotFound": "会话不存在。",
    "RateLimitReached": "达到速率限制。",
    "ConcurrentLimitReached": "达到并发限制。",
    "QueueFull": "队列已满。",
    "ServiceMaintenance": "服务维护中。",
    "ServiceUpgrade": "服务升级中。",
    "DataCorrupted": "数据损坏。",
    "DatabaseError": "数据库错误。",
    "CacheError": "缓存错误。",
    "NetworkError": "网络错误。",
    "DNSFailure": "DNS解析失败。",
    "ConnectionRefused": "连接被拒绝。",
    "SSLHandshakeFailed": "SSL握手失败。",
    "CertificateError": "证书错误。",
}


def extract_error_message(exc: Exception, service_type: str = "video") -> str:
    """Extract a clean, user-friendly error message from Ark SDK exceptions."""
    raw = str(exc)
    try:
        idx = raw.find("{")
        if idx >= 0:
            data = json.loads(raw[idx:].replace("'", '"'))
            err = data.get("error", {})
            code = err.get("code", "")
            msg = err.get("message", "")
            if code in ARK_ERRORS:
                return ARK_ERRORS[code]
            if msg:
                if "Request id:" in msg or "Request ID:" in msg:
                    idx_request = msg.find("Request id:")
                    if idx_request == -1:
                        idx_request = msg.find("Request ID:")
                    if idx_request >= 0:
                        msg = msg[:idx_request].strip().rstrip(".")
                return msg
            if code:
                return f"{service_type}生成失败: {code}"
    except (json.JSONDecodeError, TypeError, ValueError):
        pass
    return raw[:200] if len(raw) > 200 else raw