use std::collections::HashMap;

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

// ── Event (write-side) ──────────────────────────────────────────────────────

/// A usage/billing event to be ingested by the Monk Events API.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Event {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub customer_id: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub external_customer_id: Option<String>,

    pub event_name: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub idempotency_key: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub timestamp: Option<DateTime<Utc>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub properties: Option<HashMap<String, serde_json::Value>>,
}

impl Event {
    pub fn builder() -> EventBuilder {
        EventBuilder::default()
    }
}

/// Ergonomic builder for constructing [`Event`] instances.
#[derive(Debug, Default)]
pub struct EventBuilder {
    customer_id: Option<String>,
    external_customer_id: Option<String>,
    event_name: Option<String>,
    idempotency_key: Option<String>,
    timestamp: Option<DateTime<Utc>>,
    properties: Option<HashMap<String, serde_json::Value>>,
}

impl EventBuilder {
    pub fn customer_id(mut self, id: impl Into<String>) -> Self {
        self.customer_id = Some(id.into());
        self
    }

    pub fn external_customer_id(mut self, id: impl Into<String>) -> Self {
        self.external_customer_id = Some(id.into());
        self
    }

    pub fn event_name(mut self, name: impl Into<String>) -> Self {
        self.event_name = Some(name.into());
        self
    }

    pub fn idempotency_key(mut self, key: impl Into<String>) -> Self {
        self.idempotency_key = Some(key.into());
        self
    }

    pub fn timestamp(mut self, ts: DateTime<Utc>) -> Self {
        self.timestamp = Some(ts);
        self
    }

    /// Insert a single property. Converts any serializable value to `serde_json::Value`.
    pub fn property(mut self, key: impl Into<String>, value: impl Into<serde_json::Value>) -> Self {
        self.properties
            .get_or_insert_with(HashMap::new)
            .insert(key.into(), value.into());
        self
    }

    /// Set all properties at once, replacing any previously added.
    pub fn properties(mut self, props: HashMap<String, serde_json::Value>) -> Self {
        self.properties = Some(props);
        self
    }

    /// Build the event. Returns an error if `event_name` is missing.
    pub fn build(self) -> crate::error::Result<Event> {
        let event_name = self
            .event_name
            .ok_or_else(|| crate::error::MonkError::Validation("eventName is required".into()))?;
        Ok(Event {
            customer_id: self.customer_id,
            external_customer_id: self.external_customer_id,
            event_name,
            idempotency_key: self.idempotency_key,
            timestamp: self.timestamp,
            properties: self.properties,
        })
    }
}

// ── Batch Request ───────────────────────────────────────────────────────────

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BatchRequest {
    pub events: Vec<Event>,
}

// ── Ingest Responses ────────────────────────────────────────────────────────

/// Response from `POST /v1/events`.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct IngestResponse {
    pub status: String,
    pub idempotency_key: String,
    pub request_id: String,
}

/// Response from `POST /v1/events/batch`.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BatchIngestResponse {
    pub status: String,
    pub count: usize,
    pub request_id: String,
}

// ── Event List (read-side) ──────────────────────────────────────────────────

/// A single event returned by the list endpoint (no `organizationId`).
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EventResponse {
    #[serde(default)]
    pub customer_id: Option<String>,

    #[serde(default)]
    pub external_customer_id: Option<String>,

    pub event_name: String,
    pub idempotency_key: String,
    pub timestamp: DateTime<Utc>,

    #[serde(default)]
    pub properties: Option<HashMap<String, serde_json::Value>>,
}

/// Paginated list of events returned by `GET /v1/events`.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EventListResponse {
    pub events: Vec<EventResponse>,
    pub total: u64,
    pub limit: usize,
    pub offset: usize,
    pub has_more: bool,
}

// ── Usage ───────────────────────────────────────────────────────────────────

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UsageBucket {
    pub date: String,
    pub count: u64,
}

/// Response from `GET /v1/usage`.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UsageResponse {
    pub organization_id: String,

    #[serde(default)]
    pub event_name: Option<String>,

    pub start: String,
    pub end: String,
    pub total_count: u64,

    #[serde(default)]
    pub breakdown: Vec<UsageBucket>,
}

// ── API Error ───────────────────────────────────────────────────────────────

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ApiErrorDetail {
    pub code: String,
    pub message: String,
}

/// Structured error body returned by the Monk Events API.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ApiErrorBody {
    pub error: ApiErrorDetail,
    pub request_id: String,
    pub retry: bool,
}

// ── Health ──────────────────────────────────────────────────────────────────

#[derive(Debug, Clone, Deserialize)]
pub struct HealthResponse {
    pub status: String,
}

#[derive(Debug, Clone, Deserialize)]
pub struct ReadyResponse {
    pub status: String,
    pub checks: HashMap<String, String>,
}
