use rust_decimal::Decimal;
use serde::Serialize;
use utoipa::{
    openapi::{SchemaFormat, Type},
    PartialSchema, ToSchema,
};

use crate::utils::fixed::Fixed128;

#[derive(Serialize)]
pub struct SchemedDecimal(pub Decimal);

impl<const PRECISION: u8> From<Fixed128<PRECISION>> for SchemedDecimal {
    fn from(value: Fixed128<PRECISION>) -> Self {
        SchemedDecimal(value.into_decimal())
    }
}

impl From<Decimal> for SchemedDecimal {
    fn from(value: Decimal) -> Self {
        SchemedDecimal(value)
    }
}

impl ToSchema for SchemedDecimal {
    fn name() -> std::borrow::Cow<'static, str> {
        let full_type_name = std::any::type_name::<Self>();
        let type_name_without_generic = full_type_name.split_once("<").map(|(s1, _)| s1).unwrap_or(full_type_name);
        let type_name = type_name_without_generic.rsplit_once("::").map(|(_, tn)| tn).unwrap_or(type_name_without_generic);
        std::borrow::Cow::Borrowed(type_name)
    }
}

impl PartialSchema for SchemedDecimal {
    fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
        utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(
            utoipa::openapi::ObjectBuilder::new()
                .title(Some("decimal".to_string()))
                .schema_type(Type::String)
                .format(Some(SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Float)))
                .description(Some("`Decimal` represents a 128 bit representation of a fixed-precision decimal number."))
                .default(Some(serde_json::to_value("0").unwrap()))
                .minimum(Some(0))
                .examples(vec![
                    Some(serde_json::to_value("0").unwrap()),
                    Some(serde_json::to_value("1.0").unwrap()),
                    Some(serde_json::to_value("3.5").unwrap()),
                    Some(serde_json::to_value("13.31").unwrap()),
                ])
                .build(),
        ))
    }
}
impl<const PRECISION: u8> ToSchema for Fixed128<PRECISION> {
    fn name() -> std::borrow::Cow<'static, str> {
        let full_type_name = std::any::type_name::<Self>();
        let type_name_without_generic = full_type_name.split_once("<").map(|(s1, _)| s1).unwrap_or(full_type_name);
        let type_name = type_name_without_generic.rsplit_once("::").map(|(_, tn)| tn).unwrap_or(type_name_without_generic);
        std::borrow::Cow::Borrowed(type_name)
    }
}

impl<const PRECISION: u8> PartialSchema for Fixed128<PRECISION> {
    fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
        utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(
            utoipa::openapi::ObjectBuilder::new()
                .title(Some("fixed".to_string()))
                .schema_type(Type::String)
                .format(Some(SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Float)))
                .description(Some("128-bit unsigned fixed point number with const generic precision."))
                .default(Some(serde_json::to_value("0").unwrap()))
                .minimum(Some(0))
                .examples(vec![
                    Some(serde_json::to_value("0").unwrap()),
                    Some(serde_json::to_value("1.0").unwrap()),
                    Some(serde_json::to_value("3.5").unwrap()),
                    Some(serde_json::to_value("13.31").unwrap()),
                ])
                .build(),
        ))
    }
}
