MESSAGES = {
    "project_not_found": "Project '{name}' does not exist or is not initialized",
    "video_capabilities_unresolved": "Cannot resolve video model capabilities for project '{name}': {reason}",
    "scope_invalid": "scope must be full or current",
    "download_expired": "Download link expired, please re-export",
    "download_token_mismatch": "Download token does not match the target project",
    "download_token_invalid": "Download token is invalid",
    "jianying_path_invalid": "Please provide a valid Jianying draft directory path",
    "jianying_path_too_long": "Draft directory path is too long",
    "jianying_path_illegal": "Draft directory path contains illegal characters",
    "jianying_export_failed": "Jianying draft export failed, please try again later",
    "title_required": "Project title cannot be empty",
    "project_exists": "Project '{name}' already exists",
    "script_not_found": "Script '{name}' does not exist",
    "scene_not_found": "Scene '{id}' does not exist",
    "segment_not_found": "Segment '{id}' does not exist",
    "script_missing": "Script does not exist",
    "narration_mode_required": "This script is not in narration mode, please use the scene update interface",
    "content_or_file_required": "Please provide either content or file",
    "one_of_content_or_file": "Cannot provide both content and file, please choose one",
    "unsupported_file_type": "Only .txt / .md files are supported, received: {name}",
    "file_too_large": "File size exceeds limit (max approx {max_chars} characters)",
    "invalid_encoding": "File encoding error, please use UTF-8 encoded text file",
    "unauthorized": "Invalid username or password",
    "task_not_found": "Task '{id}' does not exist",
    "prompt_must_be_string_or_scene_object": "prompt must be a string or an object containing scene/composition",
    "prompt_scene_empty": "prompt.scene cannot be empty",
    "prompt_must_be_string_or_object": "prompt must be a string or an object",
    "prompt_text_empty": "prompt must not be empty",
    "storyboard_task_submitted": "Storyboard generation task for '{segment_id}' submitted",
    "generate_storyboard_first": "Please generate storyboard scene_{segment_id}.png first",
    "video_prompt_must_be_string_or_action_object": "prompt must be a string or an object containing action/camera_motion",
    "video_prompt_action_empty": "prompt.action cannot be empty",
    "video_prompt_dialogue_array": "prompt.dialogue must be an array",
    "video_task_submitted": "Video generation task for '{segment_id}' submitted",
    "character_not_found": "Character '{name}' does not exist",
    "character_task_submitted": "Character design generation task for '{name}' submitted",
    "scene_task_submitted": "Scene design generation task for '{name}' submitted",
    "prop_task_submitted": "Prop design generation task for '{name}' submitted",
    # Files
    "file_not_found": "File does not exist: {path}",
    "forbidden_access": "Access to files outside the project directory is forbidden",
    "invalid_upload_type": "Invalid upload type: {upload_type}",
    "missing_filename": "Uploaded file is missing a filename",
    "unsupported_image_type": "Unsupported file type {ext}. Allowed types: {allowed}",
    "invalid_image_file": "Invalid image file, could not be parsed",
    "invalid_asset_type": "asset type must be character / scene / prop",
    "invalid_asset_filename": "filename must not contain path separators or ..",
    "invalid_step_num": "Invalid step number: {step_num}",
    "draft_file_not_found": "Draft file does not exist",
    "draft_event_label": "Episode {episode} {label_prefix}",
    "normalized_script": "Normalized Script",
    "segment_splitting": "Segment Splitting",
    # Source loader
    "source_unsupported_format": "Unsupported source format: {ext} (supported: .txt / .md / .docx / .epub / .pdf)",
    "source_decode_failed": "Failed to decode source file '{filename}' (tried: {tried})",
    "source_corrupt_file": "Source file '{filename}' is not parseable: {reason}",
    "source_too_large": "Source file '{filename}' is too large ({size_mb} MB > {limit_mb} MB)",
    "source_conflict": "Source file '{existing}' already exists; suggested rename: '{suggested}'",
    # Providers
    "unknown_provider": "Unknown provider: {provider_id}",
    "credentials_not_found": "Credentials not found",
    "vertex_json_read_failed": "Failed to read the uploaded file",
    "vertex_json_too_large": "Credentials file is too large",
    "vertex_json_invalid": "Invalid JSON credentials file",
    "vertex_json_missing_project_id": "Credentials file is missing project_id",
    "connection_success": "Connection successful",
    "connection_timeout": "Connection timeout, please check your network or API configuration",
    "connection_failed": "Connection failed: {err_msg}",
    "unsupported_test": "Provider {provider_id} does not support connection testing yet",
    "missing_credentials": "Missing credential configuration, please add a key first",
    # Assistant
    "session_not_found": "Session '{session_id}' does not exist",
    "session_or_project_not_found": "Session or project does not exist",
    "sdk_session_timeout": "SDK session creation timed out",
    "agent_startup_failed": "Failed to start agent:\n{details}",
    "interface_offline": "This interface is offline, please use the recommended interface",
    "answers_required": "answers cannot be empty",
    # Custom Providers
    "price_input_required": "price_input must be set when setting price_output",
    "model_id_required": "model_id must be provided for enabled models",
    "duplicate_model_id": "Duplicate model_id: {model_id}",
    "default_model_conflict": "Each media_type can have at most one default model. Conflict: {conflict}",
    "provider_not_found": "Provider does not exist",
    "at_least_one_field_required": "At least one field must be provided for update",
    "discovery_failed": "Model discovery failed: {err_msg}",
    "anthropic_discovery_no_key": "API Key not configured, cannot discover models",
    "unknown_endpoint": "Unknown endpoint: {endpoint}",
    "unknown_discovery_format": "Unsupported discovery_format: {discovery_format}",
    "endpoint_required": "Enabled models must specify endpoint",
    "endpoint_media_type_mismatch": "Endpoint media_type mismatch: {detail}",
    "backend_creation_failed": "Backend creation failed: {err_msg}",
    "unsupported_discovery_format": "Connection test not supported for {discovery_format}",
    # Projects
    "unknown_style_template": "Unknown style template: {template_id}",
    "project_id_not_editable": "content_mode cannot be modified after project creation",
    "project_deleted": "Project '{name}' deleted",
    "scene_updated": "Scene '{scene_id}' updated",
    "segment_updated": "Segment '{segment_id}' updated",
    # Characters / Scenes / Props
    "character_already_exists": "Character '{name}' already exists",
    "character_deleted": "Character '{name}' deleted",
    "project_scene_already_exists": "Scene '{name}' already exists",
    "project_scene_not_found": "Scene '{name}' not found",
    "project_scene_deleted": "Scene '{name}' deleted",
    "prop_already_exists": "Prop '{name}' already exists",
    "prop_not_found": "Prop '{name}' not found",
    "prop_deleted": "Prop '{name}' deleted",
    # API Keys
    "jwt_auth_required": "API Key is not authorized to perform this action, please use JWT authentication",
    "api_key_name_exists": "Name '{name}' already exists",
    "api_key_not_found": "API Key {key_id} not found",
    # Agent Chat
    "session_project_mismatch": "Session '{session_id}' belongs to project '{session_project}', which does not match the requested project '{request_project}'",
    # Cost Estimation
    "cost_estimation_failed": "Cost estimation failed, please try again later",
    # Validators
    "invalid_backend_format": "{field_name} format should be provider/model",
    # Versions
    "unsupported_resource_type": "Unsupported resource type: {resource_type}",
    # Reference Video
    "ref_missing_asset": "Reference to {type} '{name}' is not in the project asset library, please generate it first",
    "ref_duration_exceeded": "Reference video unit duration {duration}s exceeds {model} limit of {max_duration}s, clamped",
    "ref_too_many_images": "Reference image count {count} exceeds {model} limit of {max_count}, kept the first {max_count}",
    "ref_payload_too_large": "Reference image payload exceeded provider limits, retried with extra compression",
    "ref_sora_single_ref": "Sora reference mode does not currently support multiple images, downgraded to single image",
    "ref_shot_parse_fallback": "No Shot N (Xs) header detected, treated as a single shot",
    "ref_episode_not_found": "Episode {episode} not found",
    "ref_not_reference_video_mode": "Episode script is not in reference-video mode",
    "ref_not_registered": "Referenced assets are not registered: {missing}",
    "ref_unit_not_found": "Video unit '{unit_id}' not found",
    "ref_unit_ids_length_mismatch": "unit_ids count does not match existing units",
    "ref_duplicate_unit_ids": "unit_ids contains duplicates",
    "ref_unit_ids_mismatch": "unit_ids do not match existing units",
    "ref_script_missing": "The script file bound to this episode does not exist",
    "ref_script_rebound": "This episode's script binding changed, please retry",
    "about_update_check_failed": "Failed to check for updates, please try again later",
    "about_version_read_failed": "Failed to read app version",
    # Image Capability
    "image_endpoint_mismatch_no_i2i": "Model {model} only supports text-to-image (no /v1/images/edits); remove reference images or pick a model that supports image edits",
    "image_endpoint_mismatch_no_t2i": "Model {model} only supports image-to-image (reference images required); supply reference images or pick a model that supports text-to-image",
    "image_capability_missing_i2i": "{provider}/{model} does not support image-to-image; configure a default model that supports image edits",
    "image_capability_missing_t2i": "{provider}/{model} does not support text-to-image; configure a default model that supports text-to-image",
    # Agent credentials
    "agent_preset_unknown": "Unknown preset provider: {preset_id}",
    "agent_base_url_required_custom": "base_url is required for custom configuration",
    "agent_no_fields_to_update": "No fields to update",
    "agent_credential_not_found": "Credential not found",
    "agent_cannot_delete_active": "Cannot delete the active credential; activate another first",
    "agent_test_validation_error": "Test connection failed: {error}",
}
