use std::{collections::HashMap, sync::Arc};

use chrono::{DateTime, Duration as ChronoDuration, Utc};
use serde::{Deserialize, Serialize};
use tokio::sync::RwLock;
use uuid::Uuid;

use crate::fixtures::SampleStudyFixture;
use crate::events::{MarketSnapshot, OutcomeSnapshot};

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OutcomeOddsSnapshot {
    pub id: Uuid,
    pub odds_version_id: Uuid,
    pub outcome_id: Uuid,
    pub decimal_odds: f64,
    pub fractional_num: i32,
    pub fractional_den: i32,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OddsVersionSnapshot {
    pub id: Uuid,
    pub market_id: Uuid,
    pub version_no: i32,
    pub created_at: DateTime<Utc>,
    pub is_current: bool,
    pub odds: Vec<OutcomeOddsSnapshot>,
}

#[derive(Clone, Default)]
pub struct MarketsStore {
    inner: Arc<RwLock<MarketsState>>,
}

#[derive(Default)]
struct MarketsState {
    markets_by_event_id: HashMap<Uuid, Vec<MarketSnapshot>>,
    markets_by_id: HashMap<Uuid, MarketSnapshot>,
    outcomes_by_id: HashMap<Uuid, OutcomeSnapshot>,
    current_odds_by_market_id: HashMap<Uuid, OddsVersionSnapshot>,
}

impl MarketsStore {
    pub fn new() -> Self {
        Self::from_fixture(&crate::fixtures::load_sample_study_fixture())
    }

    pub fn from_fixture(fixture: &SampleStudyFixture) -> Self {
        let mut markets_by_event_id: HashMap<Uuid, Vec<MarketSnapshot>> = HashMap::new();
        let mut markets_by_id = HashMap::new();
        let mut outcomes_by_id = HashMap::new();
        let mut current_odds_by_market_id = HashMap::new();

        for market_fixture in &fixture.manifest.markets {
            let market = market_fixture.clone();
            markets_by_event_id.entry(market.event_id).or_default().push(market.clone());
            markets_by_id.insert(market.id, market.clone());

            for outcome in &market.outcomes {
                outcomes_by_id.insert(outcome.id, outcome.clone());
            }

            let Some(odds_version) = fixture
                .odds_versions
                .iter()
                .find(|odds_version| odds_version.market_id == market.id)
                .cloned() else {
                continue;
            };

            current_odds_by_market_id.insert(market.id, odds_version);
        }

        Self {
            inner: Arc::new(RwLock::new(MarketsState {
                markets_by_event_id,
                markets_by_id,
                outcomes_by_id,
                current_odds_by_market_id,
            })),
        }
    }

    #[allow(dead_code, unused_variables)]
    pub fn with_sample_data() -> Self {
        let event_id = Uuid::parse_str("11111111-1111-1111-1111-111111111111").expect("valid uuid");
        let market_id = Uuid::parse_str("22222222-2222-2222-2222-222222222222").expect("valid uuid");
        let odds_version_id = Uuid::parse_str("66666666-6666-6666-6666-666666666666").expect("valid uuid");
        let preview_lock_at = Utc::now() + ChronoDuration::minutes(10);
        let created_at = Utc::now();

        let home_outcome_id = Uuid::parse_str("44444444-4444-4444-4444-444444444444").expect("valid uuid");
        let away_outcome_id = Uuid::parse_str("55555555-5555-5555-5555-555555555555").expect("valid uuid");

        let outcomes = vec![
            OutcomeSnapshot {
                id: home_outcome_id,
                market_id,
                outcome_code: "home".to_string(),
                label_key: "outcome.home".to_string(),
                sort_order: 1,
            },
            OutcomeSnapshot {
                id: away_outcome_id,
                market_id,
                outcome_code: "away".to_string(),
                label_key: "outcome.away".to_string(),
                sort_order: 2,
            },
        ];

        let market = MarketSnapshot {
            id: market_id,
            event_id,
            question_key: "market.sample.winner".to_string(),
            market_type: "winner".to_string(),
            status: "open".to_string(),
            lock_at: preview_lock_at,
            settlement_rule_key: "settle_on_match_winner".to_string(),
            outcomes: outcomes.clone(),
        };

        let odds_version = OddsVersionSnapshot {
            id: odds_version_id,
            market_id,
            version_no: 1,
            created_at,
            is_current: true,
            odds: vec![
                OutcomeOddsSnapshot {
                    id: Uuid::parse_str("77777777-7777-7777-7777-777777777777").expect("valid uuid"),
                    odds_version_id,
                    outcome_id: home_outcome_id,
                    decimal_odds: 1.85,
                    fractional_num: 17,
                    fractional_den: 20,
                },
                OutcomeOddsSnapshot {
                    id: Uuid::parse_str("88888888-8888-8888-8888-888888888888").expect("valid uuid"),
                    odds_version_id,
                    outcome_id: away_outcome_id,
                    decimal_odds: 2.05,
                    fractional_num: 21,
                    fractional_den: 20,
                },
            ],
        };

        let mut markets_by_event_id = HashMap::new();
        markets_by_event_id.insert(event_id, vec![market.clone()]);

        let mut markets_by_id = HashMap::new();
        markets_by_id.insert(market_id, market);

        let mut outcomes_by_id = HashMap::new();
        for outcome in outcomes {
            outcomes_by_id.insert(outcome.id, outcome);
        }

        let mut current_odds_by_market_id = HashMap::new();
        current_odds_by_market_id.insert(market_id, odds_version);

        Self::from_fixture(&crate::fixtures::load_sample_study_fixture())
    }

    pub async fn insert_market(&self, market: MarketSnapshot) -> MarketSnapshot {
        let mut state = self.inner.write().await;

        state
            .markets_by_event_id
            .entry(market.event_id)
            .or_default()
            .retain(|existing| existing.id != market.id);
        state
            .markets_by_event_id
            .entry(market.event_id)
            .or_default()
            .push(market.clone());
        state.markets_by_id.insert(market.id, market.clone());

        for outcome in &market.outcomes {
            state.outcomes_by_id.insert(outcome.id, outcome.clone());
        }

        market
    }

    pub async fn publish_odds(&self, odds: OddsVersionSnapshot) -> OddsVersionSnapshot {
        let mut state = self.inner.write().await;
        state.current_odds_by_market_id.insert(odds.market_id, odds.clone());
        odds
    }

    pub async fn markets_for_event(&self, event_id: Uuid) -> Option<Vec<MarketSnapshot>> {
        let state = self.inner.read().await;
        let Some(markets) = state.markets_by_event_id.get(&event_id) else {
            return None;
        };
        Some(markets.clone())
    }

    pub async fn market(&self, market_id: Uuid) -> Option<MarketSnapshot> {
        let state = self.inner.read().await;
        let Some(market) = state.markets_by_id.get(&market_id) else {
            return None;
        };
        Some(market.clone())
    }

    pub async fn outcome(&self, outcome_id: Uuid) -> Option<OutcomeSnapshot> {
        let state = self.inner.read().await;
        let Some(outcome) = state.outcomes_by_id.get(&outcome_id) else {
            return None;
        };
        Some(outcome.clone())
    }

    pub async fn current_odds(&self, market_id: Uuid) -> Option<OddsVersionSnapshot> {
        let state = self.inner.read().await;
        let Some(odds) = state.current_odds_by_market_id.get(&market_id) else {
            return None;
        };
        Some(odds.clone())
    }
}
