// @generated by codegen/generate.py from defs/bloomberg.toml — DO NOT EDIT

use std::convert::Infallible;
use std::fmt;
use std::str::FromStr;

use serde::{Deserialize, Serialize};

/// Bloomberg service URIs.
///
/// Standard Bloomberg API services with URIs from the Bloomberg C++ SDK.
/// Use [Service::Custom] for services not listed here.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Service {
    /// Field metadata service for field info and search (bflds).
    ApiFlds,
    /// Bloomberg Query Language service (bql).
    BqlSvc,
    /// Excel/Search service for Bloomberg searches (bsrch).
    ExrSvc,
    /// Instruments service for security lookup (blkp).
    Instruments,
    /// Real-time streaming OHLC bars (mktbar).
    MktBar,
    /// Real-time market data subscriptions (subscribe, stream).
    MktData,
    /// Level 2 market depth / order book data (depth). Requires B-PIPE license.
    MktDepth,
    /// Option chains and futures chains (chains). Requires B-PIPE license.
    MktList,
    /// Real-time VWAP subscription service (vwap).
    MktVwap,
    /// Reference data service for bdp, bdh, bds, bdib, bdtick, beqs, bport.
    RefData,
    /// Technical Analysis service for study calculations (bta).
    TaSvc,
    /// Custom service URI not listed above.
    Custom(String),
}

impl Service {
    /// Returns the Bloomberg service URI string.
    pub fn as_str(&self) -> &str {
        match self {
            Self::ApiFlds => "//blp/apiflds",
            Self::BqlSvc => "//blp/bqlsvc",
            Self::ExrSvc => "//blp/exrsvc",
            Self::Instruments => "//blp/instruments",
            Self::MktBar => "//blp/mktbar",
            Self::MktData => "//blp/mktdata",
            Self::MktDepth => "//blp/mktdepthdata",
            Self::MktList => "//blp/mktlist",
            Self::MktVwap => "//blp/mktvwap",
            Self::RefData => "//blp/refdata",
            Self::TaSvc => "//blp/tasvc",
            Self::Custom(s) => s.as_str(),
        }
    }
}

impl fmt::Display for Service {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

impl FromStr for Service {
    type Err = Infallible;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let svc = match s {
            "//blp/apiflds" => Self::ApiFlds,
            "//blp/bqlsvc" => Self::BqlSvc,
            "//blp/exrsvc" => Self::ExrSvc,
            "//blp/instruments" => Self::Instruments,
            "//blp/mktbar" => Self::MktBar,
            "//blp/mktdata" => Self::MktData,
            "//blp/mktdepthdata" => Self::MktDepth,
            "//blp/mktlist" => Self::MktList,
            "//blp/mktvwap" => Self::MktVwap,
            "//blp/refdata" => Self::RefData,
            "//blp/tasvc" => Self::TaSvc,
            other => Self::Custom(other.to_string()),
        };
        Ok(svc)
    }
}

impl Serialize for Service {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_str(self.as_str())
    }
}

impl<'de> Deserialize<'de> for Service {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        Ok(match Self::from_str(&s) {
            Ok(service) => service,
            Err(never) => match never {},
        })
    }
}

/// Bloomberg request operation names.
///
/// Standard Bloomberg API operation types from the Bloomberg C++ SDK.
/// Use [Operation::Custom] for operations not listed here.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Operation {
    /// Bloomberg Equity Screening (BEQS).
    Beqs,
    /// Bloomberg Query Language query (bql).
    BqlSendQuery,
    /// List yield curves (bcurves).
    CurveList,
    /// Bloomberg Search/Excel grid request (bsrch).
    ExcelGetGrid,
    /// Get field metadata (type, description).
    FieldInfo,
    /// Search for fields by keyword.
    FieldSearch,
    /// List government securities (bgovts).
    GovtList,
    /// Historical time series data (bdh).
    HistoricalData,
    /// Security lookup by name (blkp).
    InstrumentList,
    /// Intraday OHLCV bars (bdib).
    IntradayBar,
    /// Intraday tick data (bdtick).
    IntradayTick,
    /// Portfolio data request (bport).
    PortfolioData,
    /// Raw session.sendRequest() call (no operation name needed).
    RawRequest,
    /// Single point-in-time data (bdp, bds).
    ReferenceData,
    /// Technical analysis study request (bta).
    StudyRequest,
    /// Custom operation name not listed above.
    Custom(String),
}

impl Operation {
    /// Returns the Bloomberg operation name string.
    pub fn as_str(&self) -> &str {
        match self {
            Self::Beqs => "BeqsRequest",
            Self::BqlSendQuery => "sendQuery",
            Self::CurveList => "curveListRequest",
            Self::ExcelGetGrid => "ExcelGetGridRequest",
            Self::FieldInfo => "FieldInfoRequest",
            Self::FieldSearch => "FieldSearchRequest",
            Self::GovtList => "govtListRequest",
            Self::HistoricalData => "HistoricalDataRequest",
            Self::InstrumentList => "instrumentListRequest",
            Self::IntradayBar => "IntradayBarRequest",
            Self::IntradayTick => "IntradayTickRequest",
            Self::PortfolioData => "PortfolioDataRequest",
            Self::RawRequest => "",
            Self::ReferenceData => "ReferenceDataRequest",
            Self::StudyRequest => "studyRequest",
            Self::Custom(s) => s.as_str(),
        }
    }

    /// Returns the default extractor for this operation.
    pub fn default_extractor(&self) -> ExtractorType {
        match self {
            Self::Beqs => ExtractorType::Generic,
            Self::BqlSendQuery => ExtractorType::Bql,
            Self::CurveList => ExtractorType::Generic,
            Self::ExcelGetGrid => ExtractorType::Bsrch,
            Self::FieldInfo => ExtractorType::FieldInfo,
            Self::FieldSearch => ExtractorType::Generic,
            Self::GovtList => ExtractorType::Generic,
            Self::HistoricalData => ExtractorType::HistData,
            Self::InstrumentList => ExtractorType::Generic,
            Self::IntradayBar => ExtractorType::IntradayBar,
            Self::IntradayTick => ExtractorType::IntradayTick,
            Self::PortfolioData => ExtractorType::Generic,
            Self::RawRequest => ExtractorType::Generic,
            Self::ReferenceData => ExtractorType::RefData,
            Self::StudyRequest => ExtractorType::Generic,
            Self::Custom(_) => ExtractorType::Generic,
        }
    }

    /// Returns the default service for this operation, if known.
    pub fn default_service(&self) -> Option<Service> {
        match self {
            Self::Beqs => Some(Service::RefData),
            Self::BqlSendQuery => Some(Service::BqlSvc),
            Self::CurveList => Some(Service::Instruments),
            Self::ExcelGetGrid => Some(Service::ExrSvc),
            Self::FieldInfo => Some(Service::ApiFlds),
            Self::FieldSearch => Some(Service::ApiFlds),
            Self::GovtList => Some(Service::Instruments),
            Self::HistoricalData => Some(Service::RefData),
            Self::InstrumentList => Some(Service::Instruments),
            Self::IntradayBar => Some(Service::RefData),
            Self::IntradayTick => Some(Service::RefData),
            Self::PortfolioData => Some(Service::RefData),
            Self::RawRequest => None,
            Self::ReferenceData => Some(Service::RefData),
            Self::StudyRequest => Some(Service::TaSvc),
            Self::Custom(_) => None,
        }
    }
}

impl fmt::Display for Operation {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

impl FromStr for Operation {
    type Err = Infallible;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let op = match s {
            "BeqsRequest" => Self::Beqs,
            "sendQuery" => Self::BqlSendQuery,
            "curveListRequest" => Self::CurveList,
            "ExcelGetGridRequest" => Self::ExcelGetGrid,
            "FieldInfoRequest" => Self::FieldInfo,
            "FieldSearchRequest" => Self::FieldSearch,
            "govtListRequest" => Self::GovtList,
            "HistoricalDataRequest" => Self::HistoricalData,
            "instrumentListRequest" => Self::InstrumentList,
            "IntradayBarRequest" => Self::IntradayBar,
            "IntradayTickRequest" => Self::IntradayTick,
            "PortfolioDataRequest" => Self::PortfolioData,
            "" => Self::RawRequest,
            "ReferenceDataRequest" => Self::ReferenceData,
            "studyRequest" => Self::StudyRequest,
            other => Self::Custom(other.to_string()),
        };
        Ok(op)
    }
}

impl Serialize for Operation {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_str(self.as_str())
    }
}

impl<'de> Deserialize<'de> for Operation {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        Ok(match Self::from_str(&s) {
            Ok(operation) => operation,
            Err(never) => match never {},
        })
    }
}

/// Extractor type hint for Arrow conversion.
///
/// Tells the pump which Arrow schema/extractor to use for the response.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ExtractorType {
    /// BQL: Bloomberg Query Language responses.
    Bql,
    /// BSRCH: Bloomberg Search responses.
    Bsrch,
    /// Bulk data extractor: [ticker, field, row_idx, col1, col2, ...]
    BulkData,
    /// Field info extractor: [field, type, description, category]
    FieldInfo,
    /// Generic flattener: [path, type, value_str, value_num, value_date]
    Generic,
    /// Historical data extractor: [ticker, date, field, value, ...]
    HistData,
    /// Intraday bar extractor: [ticker, time, open, high, low, close, volume, ...]
    IntradayBar,
    /// Intraday tick extractor: [ticker, time, type, value, size, ...]
    IntradayTick,
    /// Reference data extractor: [ticker, field, value, ...]
    #[default]
    RefData,
}

impl ExtractorType {
    /// Parse extractor type from string (from Python).
    pub fn parse(s: &str) -> Option<Self> {
        match s {
            "bql" => Some(Self::Bql),
            "bsrch" => Some(Self::Bsrch),
            "bulk" => Some(Self::BulkData),
            "fieldinfo" => Some(Self::FieldInfo),
            "generic" => Some(Self::Generic),
            "histdata" => Some(Self::HistData),
            "intraday_bar" => Some(Self::IntradayBar),
            "intraday_tick" => Some(Self::IntradayTick),
            "refdata" => Some(Self::RefData),
            _ => None,
        }
    }
}

/// Output format for reference data (bdp/bdh).
///
/// Controls the shape and typing of the output Arrow table.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Format {
    /// Long format with all values as strings (default, backwards-compatible).
    #[default]
    Long,
    /// Long format with typed value columns.
    LongTyped,
    /// Long format with string values and dtype metadata column.
    LongWithMetadata,
    /// Semi-long format with one row per ticker/timestamp and fields as columns.
    SemiLong,
}

impl Format {
    /// Returns the format identifier string.
    pub fn as_str(&self) -> &str {
        match self {
            Self::Long => "long",
            Self::LongTyped => "long_typed",
            Self::LongWithMetadata => "long_metadata",
            Self::SemiLong => "semi_long",
        }
    }
}

impl fmt::Display for Format {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

impl FromStr for Format {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "long" => Ok(Self::Long),
            "long_typed" => Ok(Self::LongTyped),
            "long_metadata" => Ok(Self::LongWithMetadata),
            "semi_long" => Ok(Self::SemiLong),
            other => Err(format!("Unknown format: {}", other)),
        }
    }
}

/// Output mode for generic requests.
///
/// Controls how Bloomberg responses are converted before returning.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OutputMode {
    /// Convert to Arrow RecordBatch using appropriate extractor.
    #[default]
    Arrow,
    /// Return raw JSON as a single-column Arrow table.
    Json,
}

impl OutputMode {
    /// Returns the output mode identifier string.
    pub fn as_str(&self) -> &str {
        match self {
            Self::Arrow => "arrow",
            Self::Json => "json",
        }
    }
}

impl fmt::Display for OutputMode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

impl FromStr for OutputMode {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "arrow" => Ok(Self::Arrow),
            "json" => Ok(Self::Json),
            other => Err(format!("Unknown output mode: {}", other)),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn service_roundtrip() {
        assert_eq!(
            Service::from_str("//blp/refdata").unwrap(),
            Service::RefData
        );
        assert_eq!(Service::RefData.as_str(), "//blp/refdata");
        assert_eq!(Service::RefData.to_string(), "//blp/refdata");
    }

    #[test]
    fn service_custom() {
        let custom = Service::from_str("//blp/custom").unwrap();
        assert_eq!(custom, Service::Custom("//blp/custom".to_string()));
        assert_eq!(custom.as_str(), "//blp/custom");
    }

    #[test]
    fn service_all_known() {
        for (uri, expected) in [
            ("//blp/apiflds", Service::ApiFlds),
            ("//blp/bqlsvc", Service::BqlSvc),
            ("//blp/exrsvc", Service::ExrSvc),
            ("//blp/instruments", Service::Instruments),
            ("//blp/mktbar", Service::MktBar),
            ("//blp/mktdata", Service::MktData),
            ("//blp/mktdepthdata", Service::MktDepth),
            ("//blp/mktlist", Service::MktList),
            ("//blp/mktvwap", Service::MktVwap),
            ("//blp/refdata", Service::RefData),
            ("//blp/tasvc", Service::TaSvc),
        ] {
            assert_eq!(Service::from_str(uri).unwrap(), expected);
            assert_eq!(expected.as_str(), uri);
        }
    }

    #[test]
    fn service_serde() {
        let svc = Service::RefData;
        let json = serde_json::to_string(&svc).unwrap();
        assert_eq!(json, r#""//blp/refdata""#);
        let back: Service = serde_json::from_str(&json).unwrap();
        assert_eq!(back, Service::RefData);
    }

    #[test]
    fn operation_roundtrip() {
        assert_eq!(
            Operation::from_str("ReferenceDataRequest").unwrap(),
            Operation::ReferenceData
        );
        assert_eq!(Operation::ReferenceData.as_str(), "ReferenceDataRequest");
        assert_eq!(Operation::ReferenceData.to_string(), "ReferenceDataRequest");
    }

    #[test]
    fn operation_custom() {
        let custom = Operation::from_str("CustomRequest").unwrap();
        assert_eq!(custom, Operation::Custom("CustomRequest".to_string()));
        assert_eq!(custom.as_str(), "CustomRequest");
    }

    #[test]
    fn operation_all_known() {
        for (op, expected) in [
            ("BeqsRequest", Operation::Beqs),
            ("sendQuery", Operation::BqlSendQuery),
            ("curveListRequest", Operation::CurveList),
            ("ExcelGetGridRequest", Operation::ExcelGetGrid),
            ("FieldInfoRequest", Operation::FieldInfo),
            ("FieldSearchRequest", Operation::FieldSearch),
            ("govtListRequest", Operation::GovtList),
            ("HistoricalDataRequest", Operation::HistoricalData),
            ("instrumentListRequest", Operation::InstrumentList),
            ("IntradayBarRequest", Operation::IntradayBar),
            ("IntradayTickRequest", Operation::IntradayTick),
            ("PortfolioDataRequest", Operation::PortfolioData),
            ("", Operation::RawRequest),
            ("ReferenceDataRequest", Operation::ReferenceData),
            ("studyRequest", Operation::StudyRequest),
        ] {
            assert_eq!(Operation::from_str(op).unwrap(), expected);
            assert_eq!(expected.as_str(), op);
        }
    }

    #[test]
    fn operation_serde() {
        let op = Operation::HistoricalData;
        let json = serde_json::to_string(&op).unwrap();
        assert_eq!(json, r#""HistoricalDataRequest""#);
        let back: Operation = serde_json::from_str(&json).unwrap();
        assert_eq!(back, Operation::HistoricalData);
    }

    #[test]
    fn operation_default_extractor() {
        assert_eq!(Operation::Beqs.default_extractor(), ExtractorType::Generic);
        assert_eq!(
            Operation::BqlSendQuery.default_extractor(),
            ExtractorType::Bql
        );
        assert_eq!(
            Operation::CurveList.default_extractor(),
            ExtractorType::Generic
        );
        assert_eq!(
            Operation::ExcelGetGrid.default_extractor(),
            ExtractorType::Bsrch
        );
        assert_eq!(
            Operation::FieldInfo.default_extractor(),
            ExtractorType::FieldInfo
        );
        assert_eq!(
            Operation::FieldSearch.default_extractor(),
            ExtractorType::Generic
        );
        assert_eq!(
            Operation::GovtList.default_extractor(),
            ExtractorType::Generic
        );
        assert_eq!(
            Operation::HistoricalData.default_extractor(),
            ExtractorType::HistData
        );
        assert_eq!(
            Operation::InstrumentList.default_extractor(),
            ExtractorType::Generic
        );
        assert_eq!(
            Operation::IntradayBar.default_extractor(),
            ExtractorType::IntradayBar
        );
        assert_eq!(
            Operation::IntradayTick.default_extractor(),
            ExtractorType::IntradayTick
        );
        assert_eq!(
            Operation::PortfolioData.default_extractor(),
            ExtractorType::Generic
        );
        assert_eq!(
            Operation::RawRequest.default_extractor(),
            ExtractorType::Generic
        );
        assert_eq!(
            Operation::ReferenceData.default_extractor(),
            ExtractorType::RefData
        );
        assert_eq!(
            Operation::StudyRequest.default_extractor(),
            ExtractorType::Generic
        );
        assert_eq!(
            Operation::Custom("foo".into()).default_extractor(),
            ExtractorType::Generic
        );
    }

    #[test]
    fn operation_default_service() {
        assert_eq!(Operation::Beqs.default_service(), Some(Service::RefData));
        assert_eq!(
            Operation::BqlSendQuery.default_service(),
            Some(Service::BqlSvc)
        );
        assert_eq!(
            Operation::CurveList.default_service(),
            Some(Service::Instruments)
        );
        assert_eq!(
            Operation::ExcelGetGrid.default_service(),
            Some(Service::ExrSvc)
        );
        assert_eq!(
            Operation::FieldInfo.default_service(),
            Some(Service::ApiFlds)
        );
        assert_eq!(
            Operation::FieldSearch.default_service(),
            Some(Service::ApiFlds)
        );
        assert_eq!(
            Operation::GovtList.default_service(),
            Some(Service::Instruments)
        );
        assert_eq!(
            Operation::HistoricalData.default_service(),
            Some(Service::RefData)
        );
        assert_eq!(
            Operation::InstrumentList.default_service(),
            Some(Service::Instruments)
        );
        assert_eq!(
            Operation::IntradayBar.default_service(),
            Some(Service::RefData)
        );
        assert_eq!(
            Operation::IntradayTick.default_service(),
            Some(Service::RefData)
        );
        assert_eq!(
            Operation::PortfolioData.default_service(),
            Some(Service::RefData)
        );
        assert_eq!(Operation::RawRequest.default_service(), None);
        assert_eq!(
            Operation::ReferenceData.default_service(),
            Some(Service::RefData)
        );
        assert_eq!(
            Operation::StudyRequest.default_service(),
            Some(Service::TaSvc)
        );
        assert_eq!(Operation::Custom("foo".into()).default_service(), None);
    }

    #[test]
    fn operation_lowercase_operations() {
        assert_eq!(
            Operation::from_str("sendQuery").unwrap(),
            Operation::BqlSendQuery
        );
        assert_eq!(Operation::BqlSendQuery.as_str(), "sendQuery");

        assert_eq!(
            Operation::from_str("curveListRequest").unwrap(),
            Operation::CurveList
        );
        assert_eq!(Operation::CurveList.as_str(), "curveListRequest");

        assert_eq!(
            Operation::from_str("govtListRequest").unwrap(),
            Operation::GovtList
        );
        assert_eq!(Operation::GovtList.as_str(), "govtListRequest");

        assert_eq!(
            Operation::from_str("instrumentListRequest").unwrap(),
            Operation::InstrumentList
        );
        assert_eq!(Operation::InstrumentList.as_str(), "instrumentListRequest");

        assert_eq!(
            Operation::from_str("studyRequest").unwrap(),
            Operation::StudyRequest
        );
        assert_eq!(Operation::StudyRequest.as_str(), "studyRequest");
    }

    #[test]
    fn format_roundtrip() {
        assert_eq!(Format::from_str("long").unwrap(), Format::Long);
        assert_eq!(Format::Long.as_str(), "long");
        assert_eq!(Format::Long.to_string(), "long");
    }

    #[test]
    fn format_all_variants() {
        for (s, expected) in [
            ("long", Format::Long),
            ("long_typed", Format::LongTyped),
            ("long_metadata", Format::LongWithMetadata),
            ("semi_long", Format::SemiLong),
        ] {
            assert_eq!(Format::from_str(s).unwrap(), expected);
            assert_eq!(expected.as_str(), s);
        }
    }

    #[test]
    fn format_invalid() {
        assert!(Format::from_str("invalid").is_err());
    }

    #[test]
    fn format_serde() {
        let fmt = Format::LongTyped;
        let json = serde_json::to_string(&fmt).unwrap();
        assert_eq!(json, r#""long_typed""#);
        let back: Format = serde_json::from_str(&json).unwrap();
        assert_eq!(back, Format::LongTyped);
    }

    #[test]
    fn output_mode_roundtrip() {
        assert_eq!(OutputMode::from_str("arrow").unwrap(), OutputMode::Arrow);
        assert_eq!(OutputMode::Arrow.as_str(), "arrow");
        assert_eq!(OutputMode::Arrow.to_string(), "arrow");
    }

    #[test]
    fn output_mode_all_variants() {
        for (s, expected) in [("arrow", OutputMode::Arrow), ("json", OutputMode::Json)] {
            assert_eq!(OutputMode::from_str(s).unwrap(), expected);
            assert_eq!(expected.as_str(), s);
        }
    }

    #[test]
    fn output_mode_invalid() {
        assert!(OutputMode::from_str("invalid").is_err());
    }

    #[test]
    fn output_mode_serde() {
        let mode = OutputMode::Json;
        let json = serde_json::to_string(&mode).unwrap();
        assert_eq!(json, r#""json""#);
        let back: OutputMode = serde_json::from_str(&json).unwrap();
        assert_eq!(back, OutputMode::Json);
    }
}
