use std::path::PathBuf;

use serde::Serialize;

use crate::types::ModelId;

/// Structured error type for Kon.
///
/// Implements `Serialize` so errors can be sent to the frontend as
/// structured JSON rather than opaque strings.
#[derive(Debug, thiserror::Error, Serialize)]
pub enum KonError {
    #[error("model not found: {0}")]
    ModelNotFound(ModelId),

    #[error("model not downloaded: {0}")]
    ModelNotDownloaded(ModelId),

    #[error("engine not loaded: call load_model first")]
    EngineNotLoaded,

    #[error("transcription failed: {0}")]
    TranscriptionFailed(String),

    #[error("audio decode failed: {0}")]
    AudioDecodeFailed(String),

    #[error("audio capture failed: {0}")]
    AudioCaptureFailed(String),

    #[error("model download failed: {0}")]
    DownloadFailed(String),

    #[error("file not found: {}", .0.display())]
    FileNotFound(PathBuf),

    #[error("storage error: {0}")]
    StorageError(String),

    #[error("io error: {0}")]
    Io(
        #[from]
        #[serde(serialize_with = "serialize_io_error")]
        std::io::Error,
    ),

    #[error("{0}")]
    Other(String),
}

/// Serialises `std::io::Error` as its display string, since it does
/// not implement `Serialize` natively.
fn serialize_io_error<S: serde::Serializer>(
    err: &std::io::Error,
    s: S,
) -> std::result::Result<S::Ok, S::Error> {
    s.serialize_str(&err.to_string())
}

pub type Result<T> = std::result::Result<T, KonError>;
