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

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

#[derive(Clone, Debug, Deserialize)]
pub struct UserCreateRequest {
    pub external_ref: Option<String>,
    pub preferred_language: String,
    pub device_platform: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct UserSnapshot {
    pub id: Uuid,
    pub external_ref: String,
    pub created_at: DateTime<Utc>,
    pub preferred_language: String,
    pub device_platform: String,
}

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

#[derive(Default)]
struct UsersState {
    users_by_id: HashMap<Uuid, UserSnapshot>,
    user_ids_by_external_ref: HashMap<String, Uuid>,
}

impl UsersStore {
    pub fn new() -> Self {
        Self::default()
    }

    fn insert_user(
        state: &mut UsersState,
        external_ref: String,
        preferred_language: String,
        device_platform: String,
    ) -> UserSnapshot {
        let snapshot = UserSnapshot {
            id: Uuid::new_v4(),
            external_ref: external_ref.clone(),
            created_at: Utc::now(),
            preferred_language,
            device_platform,
        };

        state.user_ids_by_external_ref.insert(external_ref, snapshot.id);
        state.users_by_id.insert(snapshot.id, snapshot.clone());
        snapshot
    }

    pub async fn upsert(&self, request: UserCreateRequest) -> UserSnapshot {
        let UserCreateRequest {
            external_ref,
            preferred_language,
            device_platform,
        } = request;

        let external_ref = external_ref.unwrap_or_else(|| format!("anon_{}", Uuid::new_v4()));
        let mut state = self.inner.write().await;

        let Some(user_id) = state.user_ids_by_external_ref.get(&external_ref).copied() else {
            return Self::insert_user(&mut state, external_ref, preferred_language, device_platform);
        };

        let Some(existing) = state.users_by_id.get_mut(&user_id) else {
            return Self::insert_user(&mut state, external_ref, preferred_language, device_platform);
        };

        existing.preferred_language = preferred_language;
        existing.device_platform = device_platform;
        existing.clone()
    }

    pub async fn get(&self, user_id: Uuid) -> Option<UserSnapshot> {
        self.inner.read().await.users_by_id.get(&user_id).cloned()
    }
}
