use chrono::NaiveDate;

use crate::data::{CandidateEligibility, DailyMarketSnapshot, PriceField};
use crate::portfolio::Position;

#[derive(Debug, Clone)]
pub struct RuleCheck {
    pub allowed: bool,
    pub reason: Option<String>,
}

impl RuleCheck {
    pub fn allow() -> Self {
        Self {
            allowed: true,
            reason: None,
        }
    }

    pub fn reject(reason: impl Into<String>) -> Self {
        Self {
            allowed: false,
            reason: Some(reason.into()),
        }
    }
}

pub trait EquityRuleHooks {
    fn can_buy(
        &self,
        execution_date: NaiveDate,
        snapshot: &DailyMarketSnapshot,
        candidate: &CandidateEligibility,
        price_field: PriceField,
    ) -> RuleCheck;

    fn can_sell(
        &self,
        execution_date: NaiveDate,
        snapshot: &DailyMarketSnapshot,
        candidate: &CandidateEligibility,
        position: &Position,
        price_field: PriceField,
    ) -> RuleCheck;
}

#[derive(Debug, Clone, Default)]
pub struct ChinaEquityRuleHooks;

impl ChinaEquityRuleHooks {
    fn at_upper_limit(snapshot: &DailyMarketSnapshot, price_field: PriceField) -> bool {
        snapshot.is_at_upper_limit_price(snapshot.buy_price(price_field))
    }

    fn at_lower_limit(snapshot: &DailyMarketSnapshot, price_field: PriceField) -> bool {
        let check_price = match price_field {
            PriceField::Last => snapshot.price(PriceField::Last),
            _ => snapshot.sell_price(price_field),
        };
        snapshot.is_at_lower_limit_price(check_price)
    }
}

impl EquityRuleHooks for ChinaEquityRuleHooks {
    fn can_buy(
        &self,
        _execution_date: NaiveDate,
        snapshot: &DailyMarketSnapshot,
        candidate: &CandidateEligibility,
        price_field: PriceField,
    ) -> RuleCheck {
        if snapshot.paused || candidate.is_paused {
            return RuleCheck::reject("paused");
        }
        if !candidate.allow_buy {
            return RuleCheck::reject("buy disabled by eligibility flags");
        }
        if Self::at_upper_limit(snapshot, price_field) {
            return RuleCheck::reject("open at or above upper limit");
        }

        RuleCheck::allow()
    }

    fn can_sell(
        &self,
        execution_date: NaiveDate,
        snapshot: &DailyMarketSnapshot,
        candidate: &CandidateEligibility,
        position: &Position,
        price_field: PriceField,
    ) -> RuleCheck {
        if snapshot.paused || candidate.is_paused {
            return RuleCheck::reject("paused");
        }
        if !candidate.allow_sell {
            return RuleCheck::reject("sell disabled by eligibility flags");
        }
        if Self::at_lower_limit(snapshot, price_field) {
            return RuleCheck::reject("open at or below lower limit");
        }
        if position.sellable_qty(execution_date) == 0 {
            return RuleCheck::reject("t+1 sellable quantity is zero");
        }

        RuleCheck::allow()
    }
}
