use std::cell::Cell;
use std::collections::HashSet;

use axum::extract::{Extension, Query};
use axum::http::{HeaderMap, HeaderValue, header};
use axum::response::IntoResponse;
use axum::response::sse::{Event, KeepAlive, Sse};
use futures::stream::Stream;
use sea_orm::ConnectionTrait;
use serde::Deserialize;
use uuid::Uuid;

use erp_core::error::AppError;
use erp_core::types::TenantContext;

/// 包装 SSE 响应，添加 Cache-Control: no-store 头
pub struct NoCacheSse<S>(Sse<S>);

impl<S> IntoResponse for NoCacheSse<S>
where
    S: Stream<Item = Result<Event, std::convert::Infallible>> + Send + 'static,
{
    fn into_response(self) -> axum::response::Response {
        let mut response = self.0.into_response();
        response.headers_mut().insert(
            header::CACHE_CONTROL,
            HeaderValue::from_static("no-store, no-cache, must-revalidate"),
        );
        response
    }
}

use crate::message_state::MessageState;

/// SSE 查询参数
#[derive(Debug, Deserialize, Default)]
pub struct SseQuery {
    /// 逗号分隔的患者 ID 列表，为空则订阅所有管床患者
    pub patient_ids: Option<String>,
}

/// SSE 消息推送端点。
///
/// 监听所有事件，按类型分发为不同 SSE event：
/// - `message.sent` → SSE event: `message`
/// - `alert.triggered` → SSE event: `alert`
/// - `device.readings.synced` → SSE event: `vital_update`
///
/// 增强：
/// - Event ID（支持 Last-Event-ID 断点续传）
/// - 30s 心跳保活
/// - 患者选择性订阅（?patient_ids=id1,id2）
pub async fn message_stream(
    axum::extract::State(state): axum::extract::State<MessageState>,
    Extension(ctx): Extension<TenantContext>,
    headers: HeaderMap,
    Query(query): Query<SseQuery>,
) -> Result<NoCacheSse<impl Stream<Item = Result<Event, std::convert::Infallible>>>, AppError> {
    let user_id = ctx.user_id;
    let tenant_id = ctx.tenant_id;

    let last_event_id: Option<Uuid> = headers
        .get("Last-Event-ID")
        .and_then(|v| v.to_str().ok())
        .and_then(|s| Uuid::parse_str(s).ok());

    let subscribed_patient_ids: Option<HashSet<String>> = query.patient_ids.as_ref().map(|s| {
        s.split(',')
            .map(|id| id.trim().to_string())
            .filter(|id| !id.is_empty())
            .collect()
    });

    let (mut rx, _handle) = state.event_bus.subscribe_filtered(String::new());

    let db = state.db.clone();
    let last_event_id_cell = Cell::new(last_event_id);

    let sse_stream = async_stream::stream! {
        loop {
            let result = tokio::time::timeout(
                std::time::Duration::from_secs(30),
                rx.recv(),
            ).await;

            match result {
                Ok(Some(event)) => {
                    if event.tenant_id != tenant_id {
                        continue;
                    }

                    // Last-Event-ID 恢复：跳过已发送的事件
                    if let Some(skip_until) = last_event_id_cell.take()
                        && event.id <= skip_until {
                            last_event_id_cell.set(Some(skip_until));
                            continue;
                        }

                    match event.event_type.as_str() {
                        "message.sent" => {
                            let is_recipient = event.payload.get("recipient_id")
                                .and_then(|v| v.as_str())
                                .map(|s| s == user_id.to_string())
                                .unwrap_or(false);
                            if !is_recipient {
                                continue;
                            }
                            let data = serde_json::to_string(&event.payload)
                                .unwrap_or_default();
                            yield Ok(Event::default()
                                .event("message")
                                .id(event.id.to_string())
                                .data(data));
                        }
                        "alert.triggered" => {
                            let patient_id = event.payload.get("patient_id")
                                .and_then(|v| v.as_str());

                            // 患者订阅过滤
                            if let (Some(pid_str), Some(subscribed)) = (patient_id, &subscribed_patient_ids)
                                && !subscribed.contains(pid_str) {
                                    continue;
                                }

                            if let Some(pid_str) = patient_id {
                                let pid = Uuid::parse_str(pid_str).ok();
                                if let Some(pid) = pid {
                                    let is_doctor = is_doctor_for_patient(
                                        &db, tenant_id, user_id, pid,
                                    ).await;
                                    if !is_doctor {
                                        continue;
                                    }
                                }
                            }
                            let data = serde_json::to_string(&event.payload)
                                .unwrap_or_default();
                            yield Ok(Event::default()
                                .event("alert")
                                .id(event.id.to_string())
                                .data(data));
                        }
                        "device.readings.synced" => {
                            let patient_id = event.payload.get("patient_id")
                                .and_then(|v| v.as_str());

                            // 患者订阅过滤
                            if let (Some(pid_str), Some(subscribed)) = (patient_id, &subscribed_patient_ids)
                                && !subscribed.contains(pid_str) {
                                    continue;
                                }

                            if let Some(pid_str) = patient_id {
                                let pid = Uuid::parse_str(pid_str).ok();
                                if let Some(pid) = pid {
                                    let is_doctor = is_doctor_for_patient(
                                        &db, tenant_id, user_id, pid,
                                    ).await;
                                    if !is_doctor {
                                        continue;
                                    }
                                }
                            }
                            let data = serde_json::to_string(&event.payload)
                                .unwrap_or_default();
                            yield Ok(Event::default()
                                .event("vital_update")
                                .id(event.id.to_string())
                                .data(data));
                        }
                        _ => {}
                    }
                }
                Ok(None) => {
                    break;
                }
                Err(_) => {
                    // 超时 = 发送心跳
                    yield Ok(Event::default().comment("ping"));
                }
            }
        }
    };

    Ok(NoCacheSse(
        Sse::new(sse_stream).keep_alive(
            KeepAlive::new()
                .interval(std::time::Duration::from_secs(30))
                .text("ping"),
        ),
    ))
}

/// 检查 user_id 对应的医生是否是某患者的管床医生。
async fn is_doctor_for_patient(
    db: &sea_orm::DatabaseConnection,
    tenant_id: Uuid,
    user_id: Uuid,
    patient_id: Uuid,
) -> bool {
    let sql = sea_orm::Statement::from_sql_and_values(
        sea_orm::DatabaseBackend::Postgres,
        r#"SELECT COUNT(*) AS cnt FROM patient_doctor_relation
           WHERE tenant_id = $1 AND doctor_id = $2 AND patient_id = $3 AND deleted_at IS NULL"#,
        [tenant_id.into(), user_id.into(), patient_id.into()],
    );
    match db.query_one(sql).await {
        Ok(Some(row)) => {
            let cnt: i64 = row.try_get::<i64>("", "cnt").unwrap_or(0);
            cnt > 0
        }
        _ => {
            tracing::warn!(
                user_id = %user_id,
                patient_id = %patient_id,
                "查询医患关系失败，跳过推送"
            );
            false
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn patient_id_parsing_from_payload() {
        let payload = serde_json::json!({
            "patient_id": "550e8400-e29b-41d4-a716-446655440000",
            "severity": "critical",
            "rule_name": "心率过高",
        });
        let pid_str = payload.get("patient_id").and_then(|v| v.as_str());
        assert!(pid_str.is_some());
        let pid = Uuid::parse_str(pid_str.unwrap()).ok();
        assert!(pid.is_some());
        assert_eq!(
            pid.unwrap(),
            Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap()
        );
    }

    #[test]
    fn patient_id_missing_returns_none() {
        let payload = serde_json::json!({
            "severity": "warning",
        });
        let pid_str = payload.get("patient_id").and_then(|v| v.as_str());
        assert!(pid_str.is_none());
    }

    #[test]
    fn patient_id_invalid_uuid_returns_none() {
        let payload = serde_json::json!({
            "patient_id": "not-a-uuid",
        });
        let pid_str = payload.get("patient_id").and_then(|v| v.as_str());
        assert!(pid_str.is_some());
        let pid = Uuid::parse_str(pid_str.unwrap()).ok();
        assert!(pid.is_none());
    }

    #[test]
    fn sse_query_parses_patient_ids() {
        let query = SseQuery {
            patient_ids: Some("id1,id2,id3".into()),
        };
        assert!(query.patient_ids.is_some());
        let ids = query.patient_ids.unwrap();
        assert_eq!(ids, "id1,id2,id3");
    }

    #[test]
    fn sse_query_default_is_empty() {
        let query = SseQuery::default();
        assert!(query.patient_ids.is_none());
    }

    #[test]
    fn subscribed_patient_ids_parsing() {
        let query = SseQuery {
            patient_ids: Some("aaa,bbb,ccc".into()),
        };
        let set: Option<HashSet<String>> = query.patient_ids.map(|s: String| {
            s.split(',')
                .map(|id: &str| id.trim().to_string())
                .filter(|id: &String| !id.is_empty())
                .collect()
        });
        assert!(set.is_some());
        let set = set.unwrap();
        assert_eq!(set.len(), 3);
        assert!(set.contains("aaa"));
        assert!(set.contains("bbb"));
        assert!(set.contains("ccc"));
    }

    #[test]
    fn last_event_id_parsing_from_headers() {
        let event_id = Uuid::now_v7();
        let mut headers = HeaderMap::new();
        headers.insert("Last-Event-ID", event_id.to_string().parse().unwrap());

        let parsed: Option<Uuid> = headers
            .get("Last-Event-ID")
            .and_then(|v| v.to_str().ok())
            .and_then(|s| Uuid::parse_str(s).ok());

        assert_eq!(parsed, Some(event_id));
    }

    #[test]
    fn last_event_id_missing_returns_none() {
        let headers = HeaderMap::new();
        let parsed: Option<Uuid> = headers
            .get("Last-Event-ID")
            .and_then(|v| v.to_str().ok())
            .and_then(|s| Uuid::parse_str(s).ok());
        assert!(parsed.is_none());
    }
}
