use crate::event_sourcing::*;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum OrderEventSymbol {
    OrderPlaced,
    PaymentCaptured,
    RefundIssued,
    StockReserved,
    OrderShipped,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum OrderEventValidationState {
    Start,
    Placed,
    Captured,
    Refunded,
    Shipped,
    Invalid,
}

pub fn domain_event_symbol(event: &DomainEvent) -> OrderEventSymbol {
    match event {
        DomainEvent::OrderPlaced(_, _) => OrderEventSymbol::OrderPlaced,
        DomainEvent::PaymentCaptured(_, _) => OrderEventSymbol::PaymentCaptured,
        DomainEvent::RefundIssued(_, _) => OrderEventSymbol::RefundIssued,
        DomainEvent::StockReserved(_, _) => OrderEventSymbol::StockReserved,
        DomainEvent::OrderShipped(_) => OrderEventSymbol::OrderShipped,
    }
}

pub fn domain_event_symbols(events: &[DomainEvent]) -> Vec<OrderEventSymbol> {
    events.iter().map(domain_event_symbol).collect()
}

pub fn order_event_validation_step(
    state: OrderEventValidationState,
    symbol: OrderEventSymbol,
) -> OrderEventValidationState {
    match (state, symbol) {
        (OrderEventValidationState::Start, OrderEventSymbol::OrderPlaced) => {
            OrderEventValidationState::Placed
        }
        (OrderEventValidationState::Placed, OrderEventSymbol::StockReserved) => {
            OrderEventValidationState::Placed
        }
        (OrderEventValidationState::Placed, OrderEventSymbol::PaymentCaptured) => {
            OrderEventValidationState::Captured
        }
        (OrderEventValidationState::Captured, OrderEventSymbol::StockReserved) => {
            OrderEventValidationState::Captured
        }
        (OrderEventValidationState::Captured, OrderEventSymbol::RefundIssued) => {
            OrderEventValidationState::Refunded
        }
        (OrderEventValidationState::Captured, OrderEventSymbol::OrderShipped) => {
            OrderEventValidationState::Shipped
        }
        _ => OrderEventValidationState::Invalid,
    }
}

pub fn validate_order_event_symbols(symbols: &[OrderEventSymbol]) -> OrderEventValidationState {
    symbols.iter().copied().fold(
        OrderEventValidationState::Start,
        order_event_validation_step,
    )
}

pub fn order_event_word_accepted(symbols: &[OrderEventSymbol]) -> bool {
    validate_order_event_symbols(symbols) != OrderEventValidationState::Invalid
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct OrderEventValidator;

impl OrderEventValidator {
    pub const fn start(self) -> OrderEventValidationState {
        OrderEventValidationState::Start
    }

    pub fn step(
        self,
        state: OrderEventValidationState,
        symbol: OrderEventSymbol,
    ) -> OrderEventValidationState {
        order_event_validation_step(state, symbol)
    }

    pub fn run(self, symbols: &[OrderEventSymbol]) -> OrderEventValidationState {
        validate_order_event_symbols(symbols)
    }

    pub fn accepts(self, symbols: &[OrderEventSymbol]) -> bool {
        order_event_word_accepted(symbols)
    }
}

pub fn order_event_validator() -> OrderEventValidator {
    OrderEventValidator
}
