use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AssetType {
    Image,
    Ui,
    Icon,
    #[serde(rename = "sprite_2d")]
    Sprite2d,
    Tile,
    Background,
    VfxTexture,
    Video,
    Audio,
    Music,
    Tts,
    Model3d,
    Character,
    Prop,
    Sprite,
    World,
    Animation,
    Material,
    Hdri,
    Vfx,
    Lut,
    Shader,
    FontTypeface,
    Skybox,
    Decal,
    Heightmap,
    Text,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ImageEditMode {
    Edit,
    Inpaint,
    Restyle,
    Expand,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenerateRequest {
    pub asset_type: AssetType,
    pub prompt: Option<String>,
    pub model: Option<String>,
    pub input_file: Option<String>,
    pub provider: Option<String>,
    pub size: Option<String>,
    pub transparent: Option<bool>,
    #[serde(default)]
    pub reference_images: Vec<String>,
    pub edit_mode: Option<ImageEditMode>,
    pub session_id: Option<String>,
    #[serde(default)]
    pub params: Value,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutputFile {
    pub name: String,
    pub url: String,
    pub kind: String,
    pub size_bytes: Option<u64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenerateResponse {
    pub provider_id: String,
    pub output_path: Option<String>,
    pub output_url: Option<String>,
    pub output_data: Option<String>,
    #[serde(default)]
    pub output_files: Vec<OutputFile>,
    pub url: Option<String>,
    #[serde(default)]
    pub metadata: Value,
    pub cost_usd: Option<f64>,
    pub elapsed_ms: Option<u64>,
    pub job_id: Option<String>,
    pub user_id: Option<String>,
    pub session_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PackOnlyResponse {
    pub ok: bool,
    pub pack_only: bool,
    pub asset_type: String,
    pub message: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubmitAck {
    pub job_id: String,
    pub status: String,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AsyncSubmitOptions {
    pub idempotency_key: Option<String>,
    pub async_mode: bool,
}

impl AsyncSubmitOptions {
    pub fn enabled() -> Self {
        Self {
            idempotency_key: None,
            async_mode: true,
        }
    }

    pub fn ensure_async(mut self) -> Self {
        self.async_mode = true;
        self
    }
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ImageOptions {
    pub model: Option<String>,
    pub provider: Option<String>,
    pub size: Option<String>,
    pub quality: Option<String>,
    pub background: Option<String>,
    pub output_format: Option<String>,
    pub n: Option<u8>,
    pub preset: Option<String>,
    pub mask: Option<String>,
    pub mask_url: Option<String>,
    pub transparent: Option<bool>,
    pub input: Option<String>,
    #[serde(default)]
    pub reference_images: Vec<String>,
    pub edit_mode: Option<ImageEditMode>,
    pub session_id: Option<String>,
    #[serde(default)]
    pub params: Value,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct VideoOptions {
    pub model: Option<String>,
    pub provider: Option<String>,
    pub size: Option<String>,
    pub ratio: Option<String>,
    #[serde(default)]
    pub reference_images: Vec<String>,
    #[serde(default)]
    pub params: Value,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AudioOptions {
    pub model: Option<String>,
    pub provider: Option<String>,
    pub audio_type: Option<String>,
    pub duration: Option<u32>,
    #[serde(default)]
    pub params: Value,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TtsOptions {
    pub model: Option<String>,
    pub provider: Option<String>,
    pub voice: Option<String>,
    pub voice_id: Option<String>,
    pub context: Option<String>,
    pub style: Option<String>,
    pub language: Option<String>,
    pub instructions: Option<String>,
    #[serde(default)]
    pub params: Value,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct MusicOptions {
    pub model: Option<String>,
    pub provider: Option<String>,
    pub duration: Option<u32>,
    pub force_instrumental: Option<bool>,
    pub output_format: Option<String>,
    #[serde(default)]
    pub params: Value,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Model3dOptions {
    pub model: Option<String>,
    pub provider: Option<String>,
    pub input: Option<String>,
    #[serde(default)]
    pub images: Vec<String>,
    pub ai_model: Option<String>,
    pub polycount: Option<u32>,
    pub pbr: Option<bool>,
    pub hd_texture: Option<bool>,
    pub auto_size: Option<bool>,
    pub pose_mode: Option<String>,
    pub texture_prompt: Option<String>,
    pub format: Option<String>,
    #[serde(default)]
    pub params: Value,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CharacterOptions {
    pub model: Option<String>,
    pub provider: Option<String>,
    pub input: Option<String>,
    #[serde(default)]
    pub images: Vec<String>,
    pub format: Option<String>,
    pub polycount: Option<u32>,
    pub pbr: Option<bool>,
    pub hd_texture: Option<bool>,
    pub pose_mode: Option<String>,
    pub ai_model: Option<String>,
    pub auto_size: Option<bool>,
    pub texture_prompt: Option<String>,
    #[serde(default)]
    pub params: Value,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PropOptions {
    pub model: Option<String>,
    pub provider: Option<String>,
    pub input: Option<String>,
    #[serde(default)]
    pub images: Vec<String>,
    pub format: Option<String>,
    pub polycount: Option<u32>,
    pub pbr: Option<bool>,
    pub hd_texture: Option<bool>,
    pub ai_model: Option<String>,
    pub auto_size: Option<bool>,
    pub texture_prompt: Option<String>,
    pub symmetry_mode: Option<String>,
    #[serde(default)]
    pub params: Value,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SpriteOptions {
    pub model: Option<String>,
    pub provider: Option<String>,
    pub input: Option<String>,
    pub animation_type: Option<String>,
    pub direction: Option<String>,
    pub duration: Option<u32>,
    pub style: Option<String>,
    pub output_format: Option<String>,
    pub fps: Option<u32>,
    #[serde(default)]
    pub params: Value,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct WorldOptions {
    pub model: Option<String>,
    pub provider: Option<String>,
    pub input: Option<String>,
    pub image_url: Option<String>,
    pub panorama_url: Option<String>,
    pub video_url: Option<String>,
    #[serde(default)]
    pub multi_image_url: Vec<String>,
    pub quality: Option<String>,
    pub display_name: Option<String>,
    #[serde(default)]
    pub params: Value,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TextOptions {
    pub model: Option<String>,
    pub provider: Option<String>,
    pub max_tokens: Option<u32>,
    #[serde(default)]
    pub params: Value,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct GeneratedAssetOptions {
    pub model: Option<String>,
    pub provider: Option<String>,
    pub size: Option<String>,
    pub quality: Option<String>,
    pub background: Option<String>,
    pub output_format: Option<String>,
    pub n: Option<u8>,
    pub preset: Option<String>,
    pub transparent: Option<bool>,
    pub input: Option<String>,
    #[serde(default)]
    pub params: Value,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct BatchSharedRequest {
    pub transparent: Option<bool>,
    pub size: Option<String>,
    pub model: Option<String>,
    pub provider: Option<String>,
    #[serde(default)]
    pub reference_images: Vec<String>,
    pub input_file: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct BatchComposeRequest {
    pub direction: Option<ComposeDirection>,
    pub columns: Option<u32>,
    pub padding: Option<u32>,
    pub frame_width: Option<u32>,
    pub frame_height: Option<u32>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchGenerateRequest {
    pub asset_type: AssetType,
    pub prompts: Vec<String>,
    #[serde(default)]
    pub shared: BatchSharedRequest,
    pub compose: Option<BatchComposeRequest>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchFrameResult {
    pub index: usize,
    pub provider_id: Option<String>,
    pub output_data: Option<String>,
    pub output_url: Option<String>,
    pub cost_usd: Option<f64>,
    pub elapsed_ms: Option<u64>,
    pub error: Option<String>,
    pub url: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchSpritesheetResult {
    pub output_data: String,
    pub width: u32,
    pub height: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchGenerateResponse {
    pub job_id: String,
    pub user_id: String,
    #[serde(default)]
    pub frames: Vec<BatchFrameResult>,
    pub total_cost_usd: Option<f64>,
    pub elapsed_ms: Option<u64>,
    pub spritesheet: Option<BatchSpritesheetResult>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CropMode {
    Tightest,
    PowerOf2,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ComposeDirection {
    Horizontal,
    Vertical,
    Grid,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "op", rename_all = "snake_case")]
pub enum ProcessOperation {
    SmartCrop {
        mode: Option<CropMode>,
    },
    Resize {
        width: u32,
        height: u32,
    },
    Compose {
        direction: ComposeDirection,
        columns: Option<u32>,
        padding: Option<u32>,
        frame_width: Option<u32>,
        frame_height: Option<u32>,
    },
    ExtractFrames {
        count: Option<u32>,
    },
    RemoveBg {
        bg_color: Option<String>,
    },
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessRequest {
    pub input: Option<String>,
    #[serde(default)]
    pub inputs: Vec<String>,
    pub operations: Vec<ProcessOperation>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessOutputItem {
    pub output_data: String,
    pub width: u32,
    pub height: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessResponse {
    pub output_data: String,
    pub width: u32,
    pub height: u32,
    #[serde(default)]
    pub outputs: Vec<ProcessOutputItem>,
    #[serde(default)]
    pub operations_applied: Vec<String>,
    pub elapsed_ms: u64,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Process3dRequest {
    pub task_id: Option<String>,
    pub operation: String,
    #[serde(default)]
    pub params: Value,
    pub frame_count: Option<u32>,
    pub resolution: Option<u32>,
    pub camera_angle: Option<String>,
    pub directions: Option<u32>,
}

pub type Process3dResponse = Value;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VoiceType {
    Vc,
    Vd,
}

impl VoiceType {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Vc => "vc",
            Self::Vd => "vd",
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VoiceCloneRequest {
    pub sample_data_url: Option<String>,
    pub audio_base64: Option<String>,
    pub audio_mime: Option<String>,
    pub text: Option<String>,
    pub preview_text: Option<String>,
    pub style: Option<String>,
    pub save_as: Option<String>,
    pub name: Option<String>,
    pub target_model: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VoiceDesignRequest {
    pub voice_prompt: String,
    pub text: Option<String>,
    pub preview_text: Option<String>,
    pub style: Option<String>,
    pub save_as: Option<String>,
    pub name: Option<String>,
    pub target_model: Option<String>,
    pub language: Option<String>,
}

pub type VoiceCloneResponse = Value;
pub type VoiceDesignResponse = Value;
pub type VoiceListResponse = Value;
pub type VoiceDeleteResponse = Value;

pub type LoginResponse = Value;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobStatusResponse {
    pub id: String,
    pub user_id: Option<String>,
    pub asset_type: String,
    pub provider_id: String,
    pub status: String,
    pub error_message: Option<String>,
    pub output_path: Option<String>,
    pub output_url: Option<String>,
    pub cost_usd: Option<f64>,
    pub created_at: String,
    pub started_at: Option<String>,
    pub completed_at: Option<String>,
    pub duration_ms: Option<i64>,
    pub progress: Option<f64>,
    pub current_step: Option<String>,
    pub step_index: Option<i64>,
    pub step_count: Option<i64>,
    #[serde(default)]
    pub request: Value,
    pub response: Option<Value>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct JobProgress {
    pub progress: Option<f64>,
    pub current_step: Option<String>,
    pub step_index: Option<i64>,
    pub step_count: Option<i64>,
    pub message: Option<String>,
    #[serde(default)]
    pub details: Value,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobEvent {
    pub kind: String,
    pub job_id: String,
    pub status: String,
    pub progress: Option<JobProgress>,
    pub output_url: Option<String>,
    pub error_message: Option<String>,
    pub timestamp: String,
    #[serde(default)]
    pub raw: Value,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobCancelResponse {
    pub id: String,
    pub previous_status: String,
    pub status: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderHealth {
    pub healthy: bool,
    pub latency_ms: Option<u64>,
    pub message: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderHealthEntry {
    pub id: String,
    pub health: ProviderHealth,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderHealthListResponse {
    pub providers: Vec<ProviderHealthEntry>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderHealthResponse {
    pub id: String,
    pub health: ProviderHealth,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UploadResult {
    pub filename: String,
    pub url: String,
    pub size: u64,
    pub content_type: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssetFile {
    pub filename: String,
    pub url: String,
    pub size: u64,
    pub content_type: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssetListResponse {
    #[serde(default)]
    pub files: Vec<AssetFile>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssetDeleteResponse {
    pub filename: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryItem {
    pub id: String,
    pub asset_type: String,
    pub name: String,
    pub description: String,
    #[serde(default)]
    pub tags: Vec<String>,
    pub file_path: String,
    pub file_url: String,
    pub file_size: i64,
    pub duration_seconds: Option<f64>,
    pub source: String,
    pub source_job_id: Option<String>,
    #[serde(default)]
    pub metadata: Value,
    pub created_at: String,
    pub visual_style: Option<String>,
    pub visual_substyle: Option<String>,
    pub visual_composition: Option<String>,
    pub visual_lighting: Option<String>,
    pub visual_color_tone: Option<String>,
    #[serde(default)]
    pub visual_color_palette: Vec<String>,
    #[serde(default)]
    pub visual_mood: Vec<String>,
    #[serde(default)]
    pub visual_theme: Vec<String>,
    pub visual_era: Option<String>,
    pub visual_detail_level: Option<String>,
    #[serde(default)]
    pub subjects: Vec<String>,
    #[serde(default)]
    pub materials: Vec<String>,
    pub environment: Option<String>,
    pub time_of_day: Option<String>,
    pub background_type: Option<String>,
    pub aesthetic_score: Option<f64>,
    #[serde(default)]
    pub safety_flags: Vec<String>,
    pub width: Option<i64>,
    pub height: Option<i64>,
    pub aspect_ratio: Option<String>,
    pub thumbnail_url: Option<String>,
    pub keyframe_url: Option<String>,
    pub catalog_text: Option<String>,
    pub pack_id: Option<String>,
    pub pack_asset_key: Option<String>,
    pub gameplay_role: Option<String>,
    pub ui_element: Option<String>,
    pub ui_state: Option<String>,
    pub ui_theme_pack: Option<String>,
    pub nine_slice: Option<bool>,
    pub size_class: Option<String>,
    pub variant_group: Option<String>,
    pub rig_name: Option<String>,
    #[serde(default)]
    pub compatible_rigs: Vec<String>,
    pub pack_format: Option<String>,
    pub license: Option<String>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LibrarySearchMode {
    Fts,
    Vector,
    Hybrid,
}

impl LibrarySearchMode {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Fts => "fts",
            Self::Vector => "vector",
            Self::Hybrid => "hybrid",
        }
    }
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LibrarySearchQuery {
    pub q: Option<String>,
    pub query: Option<String>,
    pub r#type: Option<String>,
    pub tags: Option<String>,
    pub source: Option<String>,
    pub pack_id: Option<String>,
    pub mode: Option<LibrarySearchMode>,
    pub style: Option<String>,
    pub composition: Option<String>,
    pub lighting: Option<String>,
    pub color_tone: Option<String>,
    pub mood: Option<String>,
    pub theme: Option<String>,
    pub era: Option<String>,
    pub gameplay_role: Option<String>,
    pub ui_element: Option<String>,
    pub ui_state: Option<String>,
    pub ui_theme_pack: Option<String>,
    pub nine_slice: Option<bool>,
    pub size_class: Option<String>,
    pub rig_name: Option<String>,
    pub compatible_rig: Option<String>,
    pub enrichment_status: Option<String>,
    pub min_long_edge_px: Option<i64>,
    pub min_width_px: Option<i64>,
    pub min_height_px: Option<i64>,
    pub max_file_size_bytes: Option<i64>,
    pub license: Option<String>,
    pub rerank: Option<bool>,
    pub limit: Option<i64>,
    pub offset: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibrarySearchResponse {
    #[serde(default)]
    pub items: Vec<LibraryItem>,
    pub total: i64,
    pub count: Option<i64>,
    pub limit: i64,
    pub offset: i64,
    pub broadened: Option<bool>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LibraryRelatedQuery {
    pub r#type: Option<String>,
    pub limit: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryRelatedResponse {
    #[serde(default)]
    pub items: Vec<LibraryItem>,
    pub total: i64,
    pub limit: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryBundleRequest {
    pub asset_ids: Vec<String>,
    pub format: Option<String>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum IngestSource {
    Manual,
    Generated,
    Pack,
    Imported,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LibraryPackSource {
    Builtin,
    Url,
    Upload,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryIngestRequest {
    pub source: IngestSource,
    pub asset_type: String,
    pub file_url: Option<String>,
    pub file_data_b64: Option<String>,
    pub thumbnail_url: Option<String>,
    pub name: Option<String>,
    #[serde(default)]
    pub user_tags: Vec<String>,
    #[serde(default)]
    pub user_metadata: Value,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub metadata: Option<Value>,
    pub pack_id: Option<String>,
    pub pack_asset_key: Option<String>,
    pub source_job_id: Option<String>,
}

pub type IngestRequest = LibraryIngestRequest;

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LibraryBackfillRequest {
    pub asset_type: Option<String>,
    pub since: Option<String>,
    pub limit: Option<i64>,
    pub dry_run: Option<bool>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LibraryReenrichRequest {
    #[serde(default)]
    pub ids: Vec<String>,
    pub asset_type: Option<String>,
    pub taxonomy_version_lt: Option<i32>,
    pub force_visual: Option<bool>,
    pub recompute_embedding: Option<bool>,
    pub limit: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryAdminBatchResponse {
    #[serde(default)]
    pub job_ids: Vec<String>,
    #[serde(default)]
    pub ids: Vec<String>,
    pub total: i64,
    pub dry_run: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryIngestResponse {
    pub ingest_job_id: String,
    pub estimated_duration_seconds: u64,
}

pub type IngestResponse = LibraryIngestResponse;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryIngestResult {
    pub library_id: String,
    pub skipped: bool,
    pub embedding_dim: u32,
}

pub type IngestResult = LibraryIngestResult;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryIngestJobInfo {
    pub id: String,
    pub job_kind: Option<String>,
    pub status: String,
    pub progress: Option<f64>,
    pub error_message: Option<String>,
    pub response: Option<Value>,
    #[serde(flatten)]
    pub extra: std::collections::BTreeMap<String, Value>,
}

pub type JobInfo = LibraryIngestJobInfo;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryPackInfo {
    pub id: String,
    pub version: String,
    pub name: String,
    pub description: String,
    pub author: Option<String>,
    pub license: Option<String>,
    pub source: LibraryPackSource,
    pub source_url: Option<String>,
    pub asset_count: i64,
    pub installed_at: String,
}

pub type PackInfo = LibraryPackInfo;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryPackListResponse {
    #[serde(default)]
    pub items: Vec<LibraryPackInfo>,
    pub total: i64,
}

pub type PacksList = LibraryPackListResponse;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryPackInstallRequest {
    pub source: LibraryPackSource,
    pub id: Option<String>,
    pub version: Option<String>,
    pub url: Option<String>,
    pub tarball_b64: Option<String>,
}

pub type PackInstallRequest = LibraryPackInstallRequest;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryPackInstallResponse {
    pub pack_id: String,
    pub item_count: i64,
    #[serde(default)]
    pub ingest_job_ids: Vec<String>,
}

pub type PackInstallResponse = LibraryPackInstallResponse;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryPackDeleteResponse {
    pub deleted: i64,
}

pub type DeleteResponse = LibraryPackDeleteResponse;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryPackRefreshResponse {
    pub pack_id: String,
    pub item_count: i64,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LibraryAddRequest {
    pub asset_type: String,
    pub name: String,
    pub description: Option<String>,
    #[serde(default)]
    pub tags: Vec<String>,
    pub file_url: String,
    pub file_path: Option<String>,
    pub file_size: Option<i64>,
    pub duration_seconds: Option<f64>,
    pub source: Option<String>,
    pub source_job_id: Option<String>,
    #[serde(default)]
    pub metadata: Value,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryAddResponse {
    pub id: String,
    pub asset_type: String,
    pub name: String,
    pub file_url: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryDeleteResponse {
    pub id: String,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LibraryCatalogJobsRequest {
    pub asset_type: Option<String>,
    pub after: Option<String>,
    pub provider_id: Option<String>,
    #[serde(default)]
    pub dry_run: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryCatalogJobsResponse {
    pub dry_run: Option<bool>,
    pub would_catalog: Option<i64>,
    pub cataloged: Option<u64>,
    pub errors: Option<usize>,
    #[serde(default)]
    pub error_details: Vec<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct JobListQuery {
    pub job_kind: Option<String>,
    pub status: Option<String>,
    pub asset_type: Option<String>,
    pub provider_id: Option<String>,
    pub since: Option<String>,
    pub provider_task_id: Option<String>,
    pub limit: Option<u32>,
    pub offset: Option<u32>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobSummary {
    pub id: String,
    pub user_id: Option<String>,
    #[serde(default)]
    pub job_kind: Option<String>,
    pub asset_type: String,
    pub provider_id: String,
    #[serde(default)]
    pub provider_task_id: Option<String>,
    pub status: String,
    pub error_message: Option<String>,
    pub output_path: Option<String>,
    pub output_url: Option<String>,
    pub cost_usd: Option<f64>,
    #[serde(default)]
    pub metadata: Value,
    pub created_at: String,
    pub started_at: Option<String>,
    pub completed_at: Option<String>,
    pub duration_ms: Option<i64>,
    pub progress: Option<f64>,
    pub current_step: Option<String>,
    pub step_index: Option<i64>,
    pub step_count: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderCapabilities {
    pub supports_transparency: bool,
    pub supports_streaming: bool,
    pub max_concurrent: u32,
    pub rate_limit_rpm: Option<u32>,
    pub priority: i32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderInfo {
    pub id: String,
    pub display_name: String,
    pub asset_types: Vec<AssetType>,
    pub capabilities: ProviderCapabilities,
}
