use crate::store::ApprovalStore;
use crate::types::*;
use adk_mcp_sdk::{HealthCheck, HealthStatus};
use rmcp::{handler::server::wrapper::Parameters, schemars, tool, tool_router};
use serde::Deserialize;
use std::sync::Arc;

// ── inputs ────────────────────────────────────────────────────────────────

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct ListTemplatesInput { pub domain: Option<Domain> }

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct GetTemplateInput { pub id: String }

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct CreateTemplateInput {
    pub name: String,
    pub domain: Domain,
    pub request_type: RequestType,
    /// Ordered stages. Each: {name, rule(all_of|any_of|quorum), quorum, approvers[], sla_hours?}.
    pub stages: Vec<StageDef>,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct CreateRequestInput {
    pub domain: Domain,
    pub request_type: RequestType,
    pub title: String,
    pub description: String,
    pub requester: String,
    /// Opaque reference to the item under approval (contract/asset/post id).
    pub subject_ref: String,
    #[serde(default = "default_priority")]
    pub priority: Priority,
    /// Explicit workflow template id; if omitted, the default for the type is used.
    pub workflow_id: Option<String>,
    /// Optional expiry window in hours.
    pub expires_hours: Option<i64>,
    /// Submit immediately for review (activate the first stage).
    #[serde(default)]
    pub submit: bool,
}
fn default_priority() -> Priority { Priority::Medium }

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct RequestIdActorInput { pub id: String, pub actor: String }

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct GetRequestInput { pub id: String }

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct SearchRequestsInput {
    pub query: Option<String>,
    pub domain: Option<Domain>,
    pub request_type: Option<RequestType>,
    pub status: Option<RequestStatus>,
    pub approver: Option<String>,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct DecideInput {
    pub id: String,
    pub approver: String,
    pub decision: Decision,
    pub comment: Option<String>,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct MyQueueInput { pub approver: String }

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct CancelInput { pub id: String, pub actor: String, pub reason: String }

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct EscalateInput { pub id: String, pub actor: String, pub new_approvers: Vec<String>, pub reason: String }

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct DelegateInput { pub id: String, pub from: String, pub to: String, pub reason: Option<String> }

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct CommentInput { pub id: String, pub author: String, pub body: String }

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct AttachmentInput { pub id: String, pub actor: String, pub kind: String, pub description: String, pub uri: String }

// ── server ──────────────────────────────────────────────────────────────

#[derive(Clone)]
pub struct ApprovalServer { pub store: Arc<ApprovalStore> }

#[tool_router(server_handler)]
impl ApprovalServer {
    // ── workflow templates ───────────────────────────────────────────────

    #[tool(description = "List workflow templates, optionally by domain. Templates define the ordered approval stages and their rules.")]
    fn list_workflows(&self, Parameters(i): Parameters<ListTemplatesInput>) -> String {
        let t = self.store.list_templates(i.domain);
        let out: Vec<serde_json::Value> = t.iter().map(|w| serde_json::json!({
            "id": w.id, "name": w.name, "domain": w.domain, "request_type": w.request_type,
            "stages": w.stages.iter().map(|s| serde_json::json!({"name": s.name, "rule": s.rule, "approvers": s.approvers.len()})).collect::<Vec<_>>(),
        })).collect();
        serde_json::to_string_pretty(&serde_json::json!({"count": out.len(), "workflows": out})).unwrap()
    }

    #[tool(description = "Get a workflow template's full stage definitions.")]
    fn get_workflow(&self, Parameters(i): Parameters<GetTemplateInput>) -> String {
        match self.store.get_template(&i.id) {
            Some(w) => serde_json::to_string_pretty(&w).unwrap(),
            None => format!("Workflow not found: {}", i.id),
        }
    }

    #[tool(description = "Create a custom workflow template: ordered stages, each with a rule (all_of | any_of | quorum), quorum count, eligible approvers, and optional SLA hours.")]
    fn create_workflow(&self, Parameters(i): Parameters<CreateTemplateInput>) -> String {
        let w = self.store.create_template(i.name, i.domain, i.request_type, i.stages);
        serde_json::to_string_pretty(&serde_json::json!({"workflow_id": w.id, "name": w.name, "stages": w.stages.len(), "created": true})).unwrap()
    }

    // ── requests ───────────────────────────────────────────────────────────

    #[tool(description = "Create an approval request bound to a workflow (explicit workflow_id or the default for request_type). Set submit=true to route it for review immediately.")]
    fn create_request(&self, Parameters(i): Parameters<CreateRequestInput>) -> String {
        match self.store.create_request(i.domain, i.request_type, i.title, i.description, i.requester.clone(), i.subject_ref, i.priority, i.workflow_id, i.expires_hours) {
            Ok(mut r) => {
                if i.submit {
                    match self.store.submit(&r.id, &i.requester) {
                        Ok(sr) => r = sr,
                        Err(e) => return format!("Created {} but submit failed: {e}", r.id),
                    }
                }
                serde_json::to_string_pretty(&serde_json::json!({
                    "request_id": r.id, "status": r.status, "workflow": r.workflow_name,
                    "stages": r.stages.len(), "current_stage": r.current_stage,
                })).unwrap()
            }
            Err(e) => format!("Error: {e}"),
        }
    }

    #[tool(description = "Get an approval request's full detail: status, stages, votes, comments, attachments, delegations, and audit trail.")]
    fn get_request(&self, Parameters(i): Parameters<GetRequestInput>) -> String {
        match self.store.get_request(&i.id) {
            Some(r) => serde_json::to_string_pretty(&r).unwrap(),
            None => format!("Request not found: {}", i.id),
        }
    }

    #[tool(description = "Search/filter approval requests by text, domain, request_type, status, or an eligible approver.")]
    fn search_requests(&self, Parameters(i): Parameters<SearchRequestsInput>) -> String {
        let rs = self.store.search_requests(i.query.as_deref(), i.domain, i.request_type, i.status, i.approver.as_deref());
        let out: Vec<serde_json::Value> = rs.iter().map(|r| serde_json::json!({
            "id": r.id, "title": r.title, "domain": r.domain, "request_type": r.request_type,
            "status": r.status, "priority": r.priority,
            "current_stage": r.current_stage.map(|c| r.stages[c].name.clone()),
        })).collect();
        serde_json::to_string_pretty(&serde_json::json!({"count": out.len(), "requests": out})).unwrap()
    }

    #[tool(description = "Submit a draft (or changes-requested) request for review — activates the first stage.")]
    fn submit_request(&self, Parameters(i): Parameters<RequestIdActorInput>) -> String {
        match self.store.submit(&i.id, &i.actor) {
            Ok(r) => serde_json::to_string_pretty(&serde_json::json!({"id": r.id, "status": r.status, "current_stage": r.current_stage.map(|c| r.stages[c].name.clone())})).unwrap(),
            Err(e) => format!("Error: {e}"),
        }
    }

    // ── decisions (the engine) ──────────────────────────────────────────────

    #[tool(description = "Record an approver's decision on the active stage (approve | reject | request_changes | abstain). Evaluates the stage rule and advances, rejects, or bounces the request. Honors delegation. Gated: requires approval in production.")]
    fn decide(&self, Parameters(i): Parameters<DecideInput>) -> String {
        match self.store.decide(&i.id, &i.approver, i.decision, i.comment) {
            Ok(r) => serde_json::to_string_pretty(&serde_json::json!({
                "id": r.id, "status": r.status,
                "current_stage": r.current_stage.map(|c| r.stages[c].name.clone()),
                "stage_states": r.stages.iter().map(|s| serde_json::json!({"name": s.name, "status": s.status, "approvals": s.votes.iter().filter(|v| v.decision == Decision::Approve).count()})).collect::<Vec<_>>(),
                "decided_at": r.decided_at,
            })).unwrap(),
            Err(e) => format!("Error: {e}"),
        }
    }

    #[tool(description = "List requests awaiting a given approver's decision on the currently active stage (their approval queue).")]
    fn my_queue(&self, Parameters(i): Parameters<MyQueueInput>) -> String {
        let rs = self.store.pending_for_approver(&i.approver);
        let out: Vec<serde_json::Value> = rs.iter().map(|r| serde_json::json!({
            "id": r.id, "title": r.title, "request_type": r.request_type, "priority": r.priority,
            "stage": r.current_stage.map(|c| r.stages[c].name.clone()),
            "expires_at": r.expires_at,
        })).collect();
        serde_json::to_string_pretty(&serde_json::json!({"approver": i.approver, "count": out.len(), "queue": out})).unwrap()
    }

    // ── lifecycle ────────────────────────────────────────────────────────

    #[tool(description = "Cancel/withdraw a request with a reason (not allowed once approved/rejected). Gated.")]
    fn cancel_request(&self, Parameters(i): Parameters<CancelInput>) -> String {
        match self.store.cancel(&i.id, &i.actor, &i.reason) {
            Ok(r) => serde_json::to_string_pretty(&serde_json::json!({"id": r.id, "status": r.status})).unwrap(),
            Err(e) => format!("Error: {e}"),
        }
    }

    #[tool(description = "Escalate the active stage by reassigning it to a new set of approvers (e.g. on SLA breach). Gated.")]
    fn escalate(&self, Parameters(i): Parameters<EscalateInput>) -> String {
        match self.store.escalate(&i.id, &i.actor, i.new_approvers, &i.reason) {
            Ok(r) => serde_json::to_string_pretty(&serde_json::json!({"id": r.id, "current_stage": r.current_stage.map(|c| serde_json::json!({"name": r.stages[c].name, "approvers": r.stages[c].approvers}))})).unwrap(),
            Err(e) => format!("Error: {e}"),
        }
    }

    #[tool(description = "Delegate approval authority from one approver to another for this request (the delegate may then decide on the delegator's behalf). Gated.")]
    fn delegate(&self, Parameters(i): Parameters<DelegateInput>) -> String {
        match self.store.delegate(&i.id, &i.from, &i.to, i.reason) {
            Ok(r) => serde_json::to_string_pretty(&serde_json::json!({"id": r.id, "delegations": r.delegations})).unwrap(),
            Err(e) => format!("Error: {e}"),
        }
    }

    // ── annotations ────────────────────────────────────────────────────────

    #[tool(description = "Add a comment to the request thread.")]
    fn add_comment(&self, Parameters(i): Parameters<CommentInput>) -> String {
        match self.store.add_comment(&i.id, &i.author, &i.body) {
            Ok(c) => serde_json::to_string_pretty(&serde_json::json!({"comment_id": c.id, "added": true})).unwrap(),
            Err(e) => format!("Error: {e}"),
        }
    }

    #[tool(description = "Attach a document/evidence reference (stored by URI) to the request.")]
    fn add_attachment(&self, Parameters(i): Parameters<AttachmentInput>) -> String {
        match self.store.add_attachment(&i.id, &i.actor, &i.kind, &i.description, &i.uri) {
            Ok(a) => serde_json::to_string_pretty(&serde_json::json!({"attachment_id": a.id, "kind": a.kind, "added": true})).unwrap(),
            Err(e) => format!("Error: {e}"),
        }
    }

    #[tool(description = "Get the immutable audit trail (who did what, when) for a request — defensible record for legal/board use.")]
    fn get_audit_trail(&self, Parameters(i): Parameters<GetRequestInput>) -> String {
        match self.store.get_audit_trail(&i.id) {
            Some(t) => serde_json::to_string_pretty(&serde_json::json!({"id": i.id, "entries": t.len(), "audit_trail": t})).unwrap(),
            None => format!("Request not found: {}", i.id),
        }
    }

    // ═══════════════════════════════════════════════════════════════════
    // AGENTIC TOOLS
    // ═══════════════════════════════════════════════════════════════════

    #[tool(description = "Concise status of a request: where it is in the workflow, what's outstanding on the active stage, and what happens next. Read-only.")]
    fn request_status(&self, Parameters(i): Parameters<GetRequestInput>) -> String {
        let r = match self.store.get_request(&i.id) {
            Some(r) => r,
            None => return format!("Request not found: {}", i.id),
        };
        let active = r.current_stage.map(|ci| {
            let st = &r.stages[ci];
            let approvals = st.votes.iter().filter(|v| v.decision == Decision::Approve).count();
            let voted: Vec<&String> = st.votes.iter().map(|v| &v.approver).collect();
            let outstanding: Vec<&String> = st.approvers.iter().filter(|a| !voted.contains(a)).collect();
            let needed = match st.rule { StageRule::AnyOf => 1, StageRule::Quorum => st.quorum as usize, StageRule::AllOf => st.approvers.len() };
            serde_json::json!({
                "stage": st.name, "rule": st.rule,
                "approvals": approvals, "needed": needed,
                "outstanding_approvers": outstanding,
            })
        });
        serde_json::to_string_pretty(&serde_json::json!({
            "id": r.id, "title": r.title, "status": r.status, "priority": r.priority,
            "progress": format!("{} of {} stages resolved", r.stages.iter().filter(|s| matches!(s.status, StageStatus::Approved | StageStatus::Skipped)).count(), r.stages.len()),
            "active_stage": active,
            "expires_at": r.expires_at,
        })).unwrap()
    }

    #[tool(description = "Intake: create AND submit an approval request in one step, auto-selecting the default workflow for the request_type. Returns the routed request and its first active stage with the outstanding approvers.")]
    fn intake_and_route(&self, Parameters(i): Parameters<CreateRequestInput>) -> String {
        match self.store.create_request(i.domain, i.request_type, i.title, i.description, i.requester.clone(), i.subject_ref, i.priority, i.workflow_id, i.expires_hours) {
            Ok(r) => match self.store.submit(&r.id, &i.requester) {
                Ok(sr) => {
                    let stage = sr.current_stage.map(|c| serde_json::json!({"name": sr.stages[c].name, "rule": sr.stages[c].rule, "approvers": sr.stages[c].approvers}));
                    serde_json::to_string_pretty(&serde_json::json!({
                        "outcome": "routed",
                        "request_id": sr.id,
                        "workflow": sr.workflow_name,
                        "status": sr.status,
                        "active_stage": stage,
                    })).unwrap()
                }
                Err(e) => format!("Created {} but routing failed: {e}", r.id),
            },
            Err(e) => format!("Error: {e}"),
        }
    }
}

#[async_trait::async_trait]
impl HealthCheck for ApprovalServer {
    async fn check_health(&self) -> HealthStatus {
        HealthStatus { healthy: true, message: Some("operational".into()), latency_ms: Some(1) }
    }
}
