//! Profile 领域服务。
//!
//! 封装 Profile 持久化和领域事件发射,让路由层只负责 HTTP 形状。

use crate::sidecar::db::profile_repo::{
    Profile, ProfileDetailServer, ProfileRepository, ProfileServerState, RemoveResult,
};
use crate::sidecar::db::Database;
use crate::sidecar::services::event_bus::{EventBus, Evt};
use std::sync::Arc;

pub struct ProfileService;

/// profile 操作可能产生的领域错误。与 ServerServiceError 对齐,
/// 让路由层能把 NotFound 等映射到正确的 HTTP 状态码。
pub enum ProfileServiceError {
    NotFound(String),
    Validation(String),
    Active(String),
    Internal(String),
}

/// 单一映射点:ProfileServiceError → AppError。
impl From<ProfileServiceError> for crate::sidecar::http::app_error::AppError {
    fn from(e: ProfileServiceError) -> Self {
        match e {
            ProfileServiceError::NotFound(m) => Self::not_found(m),
            ProfileServiceError::Validation(m) => Self::validation(m),
            ProfileServiceError::Active(m) => Self::active_profile(m),
            ProfileServiceError::Internal(m) => Self::internal(m),
        }
    }
}

impl ProfileService {
    pub fn list(db: &Database) -> Result<Vec<Profile>, ProfileServiceError> {
        ProfileRepository::new(db)
            .find_all()
            .map_err(ProfileServiceError::Internal)
    }

    pub fn create(db: &Database, name: &str) -> Result<Profile, ProfileServiceError> {
        if name.is_empty() {
            return Err(ProfileServiceError::Validation("name is required".into()));
        }
        ProfileRepository::new(db)
            .create(name)
            .map_err(ProfileServiceError::Internal)
    }

    pub fn get_detail(
        db: &Database,
        id: &str,
    ) -> Result<(Profile, Vec<ProfileDetailServer>), ProfileServiceError> {
        let repo = ProfileRepository::new(db);
        let profile = repo
            .find_by_id(id)
            .map_err(ProfileServiceError::Internal)?
            .ok_or_else(|| ProfileServiceError::NotFound("Profile not found".into()))?;
        let servers = repo
            .find_profile_servers(id)
            .map_err(ProfileServiceError::Internal)?;
        Ok((profile, servers))
    }

    pub fn update(
        db: &Database,
        id: &str,
        name: Option<&str>,
    ) -> Result<Profile, ProfileServiceError> {
        ProfileRepository::new(db)
            .update(id, name)
            .map_err(ProfileServiceError::Internal)?
            .ok_or_else(|| ProfileServiceError::NotFound("Profile not found".into()))
    }

    pub fn remove(db: &Database, id: &str) -> Result<(), ProfileServiceError> {
        match ProfileRepository::new(db)
            .remove(id)
            .map_err(ProfileServiceError::Internal)?
        {
            RemoveResult::Success => Ok(()),
            RemoveResult::NotFound => {
                Err(ProfileServiceError::NotFound("Profile not found".into()))
            }
            RemoveResult::Active => Err(ProfileServiceError::Active(
                "Cannot delete active profile".into(),
            )),
        }
    }

    /// 激活 profile 并发出 `profile:activated` 事件。
    /// 事件发射是领域规则——切了活动 profile,相关缓存(profiles/servers/logs)该失效——
    /// 所以它属于 service,不属于路由层。
    pub fn activate(
        db: &Database,
        event_bus: &Arc<EventBus>,
        id: &str,
    ) -> Result<Profile, ProfileServiceError> {
        let profile = ProfileRepository::new(db)
            .activate(id)
            .map_err(ProfileServiceError::Internal)?
            .ok_or_else(|| ProfileServiceError::NotFound("Profile not found".into()))?;
        event_bus.emit(Evt::ProfileActivated {
            profile_id: id.to_string(),
        });
        Ok(profile)
    }

    pub fn get_profile_server(
        db: &Database,
        profile_id: &str,
        server_id: &str,
    ) -> Result<ProfileDetailServer, ProfileServiceError> {
        let servers = ProfileRepository::new(db)
            .find_profile_servers(profile_id)
            .map_err(ProfileServiceError::Internal)?;
        servers
            .into_iter()
            .find(|s| s.server.id == server_id)
            .ok_or_else(|| ProfileServiceError::NotFound("Server not found in profile".into()))
    }

    pub fn upsert_profile_server(
        db: &Database,
        profile_id: &str,
        server_id: &str,
        enabled: Option<bool>,
        disabled_tools: Option<&Vec<String>>,
    ) -> Result<ProfileServerState, ProfileServiceError> {
        ProfileRepository::new(db)
            .upsert_profile_server(profile_id, server_id, enabled, disabled_tools)
            .map_err(ProfileServiceError::Internal)
    }
}

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

    #[test]
    fn validation_error_maps_to_bad_request() {
        let err: crate::sidecar::http::app_error::AppError =
            ProfileServiceError::Validation("name is required".into()).into();

        assert_eq!(err.status_code(), StatusCode::BAD_REQUEST);
        assert_eq!(err.code(), "VALIDATION_ERROR");
    }
}
