use chrono::Utc;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};

use crate::{PortalRole, role_envelope};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub(crate) struct ExecutiveAction {
    pub priority: ActionPriority,
    pub title: String,
    pub summary: String,
    pub owner_role: ActionOwnerRole,
    pub recommended_deadline: String,
    pub reason_codes: Vec<String>,
    pub evidence: Vec<String>,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ActionPriority {
    Low,
    Medium,
    High,
    Critical,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ActionOwnerRole {
    Executive,
    Manager,
    Security,
    Forensics,
    Admin,
}

pub(crate) fn build_action_center_from_report(report: &Value, role: PortalRole) -> Value {
    let actions = generate_actions(report)
        .into_iter()
        .filter(|action| action_visible_for_role(action.owner_role, role))
        .collect::<Vec<_>>();
    json!({
        "ok": true,
        "role_context": role_envelope(role, "actions"),
        "actions": actions,
        "model": {
            "type": "rule_based",
            "version": "executive-action-center-v1",
            "ml": false,
            "llm": false,
            "auto_remediation": false
        },
        "generated_at_utc": Utc::now().to_rfc3339(),
        "limitations": [
            "Рекомендуемые действия не выполняются автоматически",
            "Action Center не блокирует пользователей и не меняет политики",
            "Все действия требуют ручного подтверждения ответственным контуром"
        ]
    })
}

pub(crate) fn filter_actions_for_role(actions: &Value, role: PortalRole) -> Value {
    let filtered = actions
        .as_array()
        .into_iter()
        .flatten()
        .filter(|item| {
            item.get("owner_role")
                .and_then(Value::as_str)
                .and_then(parse_owner_role)
                .is_some_and(|owner| action_visible_for_role(owner, role))
        })
        .cloned()
        .collect::<Vec<_>>();
    Value::Array(filtered)
}

pub(crate) fn actions_from_center(center: &Value) -> Value {
    center
        .get("actions")
        .and_then(Value::as_array)
        .cloned()
        .map(Value::Array)
        .unwrap_or_else(|| Value::Array(Vec::new()))
}

fn generate_actions(report: &Value) -> Vec<ExecutiveAction> {
    let mut actions = Vec::new();
    add_workforce_kpi_action(report, &mut actions);
    add_coverage_action(report, &mut actions);
    add_ueba_confidence_action(report, &mut actions);
    add_ueba_action(report, &mut actions);
    add_security_correlation_action(report, &mut actions);
    add_incident_candidate_action(report, &mut actions);
    add_risk_narrative_action(report, &mut actions);
    if actions.is_empty() {
        actions.push(ExecutiveAction {
            priority: ActionPriority::Low,
            title: "Продолжить наблюдение".to_string(),
            summary: "Критичных управленческих действий по текущему срезу не требуется".to_string(),
            owner_role: ActionOwnerRole::Manager,
            recommended_deadline: "72h".to_string(),
            reason_codes: vec!["NORMAL_OBSERVATION".to_string()],
            evidence: vec!["Критичные сигналы не выявлены".to_string()],
        });
    }
    actions.sort_by(|left, right| {
        right
            .priority
            .cmp(&left.priority)
            .then_with(|| left.owner_role.as_str().cmp(right.owner_role.as_str()))
            .then_with(|| left.title.cmp(&right.title))
    });
    actions
}

fn add_workforce_kpi_action(report: &Value, actions: &mut Vec<ExecutiveAction>) {
    let Some(score) = report
        .pointer("/workforce_kpi_explain/kpi_score")
        .and_then(Value::as_u64)
    else {
        return;
    };
    if score >= 70 {
        return;
    }
    let mut reason_codes = vec!["LOW_WORKFORCE_KPI".to_string()];
    let mut evidence = vec![format!("Workforce KPI ниже целевого уровня: {score}%")];
    if has_kpi_factor(report, "remote_session_activity") {
        reason_codes.push("HIGH_REMOTE_ACTIVITY".to_string());
        evidence.push("Рост удаленных сессий влияет на управленческий риск".to_string());
    }
    if let Some(confidence) = report
        .pointer("/workforce_kpi_explain/confidence")
        .and_then(Value::as_str)
        .filter(|value| *value == "low")
    {
        reason_codes.push("LOW_KPI_CONFIDENCE".to_string());
        evidence.push(format!("Доверие к KPI: {confidence}"));
    }
    actions.push(ExecutiveAction {
        priority: if score < 50 {
            ActionPriority::Critical
        } else {
            ActionPriority::High
        },
        title: "Проверить подразделение с низким индексом активности".to_string(),
        summary:
            "Индекс активности ниже управленческого порога; требуется проверка причины просадки"
                .to_string(),
        owner_role: ActionOwnerRole::Manager,
        recommended_deadline: if score < 50 { "4h" } else { "24h" }.to_string(),
        reason_codes,
        evidence,
    });
}

fn add_coverage_action(report: &Value, actions: &mut Vec<ExecutiveAction>) {
    let coverage = report
        .pointer("/agent_coverage_sla/coverage_pct")
        .or_else(|| report.pointer("/workforce_kpi_explain/coverage/agent_coverage_percent"))
        .and_then(Value::as_u64)
        .unwrap_or(100);
    let sla_status = report
        .pointer("/agent_coverage_sla/sla_status")
        .and_then(Value::as_str)
        .unwrap_or("OK");
    if coverage >= 80 && !matches!(sla_status, "WARNING" | "CRITICAL") {
        return;
    }
    actions.push(ExecutiveAction {
        priority: if coverage < 60 || sla_status == "CRITICAL" {
            ActionPriority::Critical
        } else {
            ActionPriority::High
        },
        title: "Проверить состояние агентов".to_string(),
        summary: "Полнота данных ниже целевого уровня; показатели могут быть нерепрезентативны"
            .to_string(),
        owner_role: ActionOwnerRole::Admin,
        recommended_deadline: if coverage < 60 { "4h" } else { "24h" }.to_string(),
        reason_codes: vec!["LOW_COVERAGE".to_string()],
        evidence: vec![
            format!("Покрытие агентов: {coverage}%"),
            format!("SLA полноты данных: {sla_status}"),
        ],
    });
}

fn add_ueba_confidence_action(report: &Value, actions: &mut Vec<ExecutiveAction>) {
    let score = report
        .pointer("/ueba_risk/score")
        .and_then(Value::as_u64)
        .unwrap_or(0);
    let explicit_confidence = report
        .pointer("/ueba_risk/confidence_level")
        .and_then(Value::as_str);
    let explicit_classification = report
        .pointer("/ueba_risk/classification")
        .and_then(Value::as_str);
    if score < 70 && explicit_confidence.is_none() && explicit_classification.is_none() {
        return;
    }
    let confidence = report
        .pointer("/ueba_risk/confidence_level")
        .and_then(Value::as_str)
        .unwrap_or("unknown");
    let classification = report
        .pointer("/ueba_risk/classification")
        .and_then(Value::as_str)
        .unwrap_or("insufficient_data");
    if score < 70 && !matches!(classification, "needs_investigation" | "insufficient_data") {
        return;
    }
    if !matches!(confidence, "low" | "unknown")
        && !matches!(classification, "needs_investigation" | "insufficient_data")
    {
        return;
    }
    let reasons = report
        .pointer("/ueba_risk/confidence_reasons")
        .and_then(Value::as_array)
        .map(|items| {
            items
                .iter()
                .filter_map(Value::as_str)
                .take(3)
                .map(ToString::to_string)
                .collect::<Vec<_>>()
        })
        .unwrap_or_default();
    let mut evidence = vec![
        format!("UEBA confidence: {confidence}"),
        format!("UEBA classification: {classification}"),
    ];
    evidence.extend(reasons);
    actions.push(ExecutiveAction {
        priority: ActionPriority::Critical,
        title: "Проверить полноту данных".to_string(),
        summary: "Перед жестким выводом по UEBA нужно подтвердить покрытие, свежесть и полноту телеметрии"
            .to_string(),
        owner_role: ActionOwnerRole::Admin,
        recommended_deadline: "4h".to_string(),
        reason_codes: vec![
            "LOW_UEBA_CONFIDENCE".to_string(),
            "CHECK_DATA_COMPLETENESS".to_string(),
        ],
        evidence,
    });
}

fn add_ueba_action(report: &Value, actions: &mut Vec<ExecutiveAction>) {
    let score = report
        .pointer("/ueba_risk/score")
        .and_then(Value::as_u64)
        .unwrap_or(0);
    let level = report
        .pointer("/ueba_risk/level")
        .and_then(Value::as_str)
        .unwrap_or("low");
    if score < 70 && !matches!(level, "high" | "critical") {
        return;
    }
    actions.push(ExecutiveAction {
        priority: if score >= 90 || level == "critical" {
            ActionPriority::Critical
        } else {
            ActionPriority::High
        },
        title: "Передать данные в контур ИБ".to_string(),
        summary: "UEBA score повышен; требуется ручная проверка безопасности".to_string(),
        owner_role: ActionOwnerRole::Security,
        recommended_deadline: if score >= 90 { "4h" } else { "24h" }.to_string(),
        reason_codes: vec!["HIGH_UEBA".to_string()],
        evidence: vec![format!("UEBA score: {score}, уровень: {level}")],
    });
}

fn add_security_correlation_action(report: &Value, actions: &mut Vec<ExecutiveAction>) {
    let max_score = report
        .get("security_correlation")
        .and_then(Value::as_array)
        .into_iter()
        .flatten()
        .filter_map(|item| item.get("correlation_score").and_then(Value::as_u64))
        .max()
        .unwrap_or(0);
    if max_score < 60 {
        return;
    }
    actions.push(ExecutiveAction {
        priority: if max_score >= 80 {
            ActionPriority::Critical
        } else {
            ActionPriority::High
        },
        title: "Проверить связь активности и ИБ-событий".to_string(),
        summary: "Есть корреляция между операционным риском и событиями безопасности".to_string(),
        owner_role: ActionOwnerRole::Security,
        recommended_deadline: "24h".to_string(),
        reason_codes: vec!["HIGH_SECURITY_CORRELATION".to_string()],
        evidence: vec![format!("Security correlation score: {max_score}")],
    });
}

fn add_incident_candidate_action(report: &Value, actions: &mut Vec<ExecutiveAction>) {
    let Some(candidates) = report
        .get("risk_incident_candidates")
        .and_then(Value::as_array)
    else {
        return;
    };
    if candidates.is_empty() {
        return;
    }
    let critical = candidates.iter().any(|item| {
        item.get("risk_level")
            .and_then(Value::as_str)
            .is_some_and(|level| level.eq_ignore_ascii_case("critical"))
    });
    actions.push(ExecutiveAction {
        priority: if critical {
            ActionPriority::Critical
        } else {
            ActionPriority::High
        },
        title: "Провести расследование кандидатов".to_string(),
        summary: "В очереди есть кандидаты на проверку; требуется ручной разбор и фиксация решения"
            .to_string(),
        owner_role: ActionOwnerRole::Forensics,
        recommended_deadline: if critical { "4h" } else { "24h" }.to_string(),
        reason_codes: vec!["INCIDENT_CANDIDATE".to_string()],
        evidence: vec![format!("Кандидатов на проверку: {}", candidates.len())],
    });
}

fn add_risk_narrative_action(report: &Value, actions: &mut Vec<ExecutiveAction>) {
    let score = report
        .pointer("/risk_narrative/risk_score")
        .and_then(Value::as_u64)
        .unwrap_or(0);
    if score < 75 {
        return;
    }
    let level = report
        .pointer("/risk_narrative/risk_level")
        .and_then(Value::as_str)
        .unwrap_or("high");
    actions.push(ExecutiveAction {
        priority: if score >= 90 {
            ActionPriority::Critical
        } else {
            ActionPriority::High
        },
        title: "Назначить владельца корректирующих действий".to_string(),
        summary: "Риск-нарратив показывает высокий управленческий риск; нужен ответственный и срок контроля"
            .to_string(),
        owner_role: ActionOwnerRole::Executive,
        recommended_deadline: if score >= 90 { "4h" } else { "24h" }.to_string(),
        reason_codes: vec!["RISK_NARRATIVE_HIGH".to_string()],
        evidence: vec![format!("Risk Narrative: {score}/100, уровень: {level}")],
    });
}

fn has_kpi_factor(report: &Value, factor_name: &str) -> bool {
    report
        .pointer("/workforce_kpi_explain/factors")
        .and_then(Value::as_array)
        .into_iter()
        .flatten()
        .any(|item| item.get("name").and_then(Value::as_str) == Some(factor_name))
}

fn action_visible_for_role(owner_role: ActionOwnerRole, role: PortalRole) -> bool {
    match role {
        PortalRole::Admin => true,
        PortalRole::Executive => matches!(
            owner_role,
            ActionOwnerRole::Executive | ActionOwnerRole::Manager | ActionOwnerRole::Admin
        ),
        PortalRole::Manager => matches!(owner_role, ActionOwnerRole::Manager),
        PortalRole::Security => {
            matches!(
                owner_role,
                ActionOwnerRole::Security | ActionOwnerRole::Forensics
            )
        }
        PortalRole::Forensics => {
            matches!(
                owner_role,
                ActionOwnerRole::Forensics | ActionOwnerRole::Security
            )
        }
    }
}

fn parse_owner_role(value: &str) -> Option<ActionOwnerRole> {
    match value {
        "executive" => Some(ActionOwnerRole::Executive),
        "manager" => Some(ActionOwnerRole::Manager),
        "security" => Some(ActionOwnerRole::Security),
        "forensics" => Some(ActionOwnerRole::Forensics),
        "admin" => Some(ActionOwnerRole::Admin),
        _ => None,
    }
}

impl ActionOwnerRole {
    fn as_str(self) -> &'static str {
        match self {
            Self::Executive => "executive",
            Self::Manager => "manager",
            Self::Security => "security",
            Self::Forensics => "forensics",
            Self::Admin => "admin",
        }
    }
}

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

    fn sample_report() -> Value {
        json!({
            "workforce_kpi_explain": {
                "kpi_score": 48,
                "confidence": "low",
                "coverage": {"agent_coverage_percent": 58},
                "factors": [{"name": "remote_session_activity"}]
            },
            "ueba_risk": {"score": 91, "level": "critical"},
            "agent_coverage_sla": {"coverage_pct": 58, "sla_status": "CRITICAL"},
            "security_correlation": [{"correlation_score": 81}],
            "risk_incident_candidates": [{"risk_level": "CRITICAL"}],
            "risk_narrative": {"risk_score": 92, "risk_level": "critical"}
        })
    }

    #[test]
    fn generates_rule_based_actions_with_priorities() {
        let payload = build_action_center_from_report(&sample_report(), PortalRole::Admin);
        let actions = payload["actions"].as_array().unwrap();
        assert!(actions.len() >= 5);
        assert_eq!(payload["model"]["type"], "rule_based");
        assert_eq!(payload["model"]["ml"], false);
        assert_eq!(payload["model"]["llm"], false);
        assert!(actions.iter().any(|item| {
            item["reason_codes"]
                .as_array()
                .unwrap()
                .iter()
                .any(|code| code == "LOW_WORKFORCE_KPI")
        }));
        assert!(actions.iter().any(|item| {
            item["reason_codes"]
                .as_array()
                .unwrap()
                .iter()
                .any(|code| code == "LOW_UEBA_CONFIDENCE")
        }));
        assert!(actions.iter().any(|item| item["priority"] == "critical"));
    }

    #[test]
    fn filters_actions_by_role() {
        let admin = build_action_center_from_report(&sample_report(), PortalRole::Admin);
        let security = filter_actions_for_role(&actions_from_center(&admin), PortalRole::Security);
        let security_actions = security.as_array().unwrap();
        assert!(!security_actions.is_empty());
        assert!(
            security_actions
                .iter()
                .all(|item| item["owner_role"] == "security" || item["owner_role"] == "forensics")
        );

        let manager = filter_actions_for_role(&actions_from_center(&admin), PortalRole::Manager);
        let manager_actions = manager.as_array().unwrap();
        assert!(
            manager_actions
                .iter()
                .all(|item| item["owner_role"] == "manager")
        );
    }

    #[test]
    fn emits_observation_when_no_rule_matches() {
        let payload = build_action_center_from_report(&json!({}), PortalRole::Executive);
        let actions = payload["actions"].as_array().unwrap();
        assert_eq!(actions.len(), 1);
        assert_eq!(actions[0]["priority"], "low");
        assert_eq!(actions[0]["reason_codes"][0], "NORMAL_OBSERVATION");
    }
}
