//! Email/SMS draft validator.
//!
//! PRD checks:
//!   - Schema (TO/BODY fields present)
//!   - Length (SMS ≤ 160 chars; email subject ≤ 78 chars)
//!   - PII absence (no SSN / salary leaked into outgoing text)
//!   - Worker-name consistency (name in message matches worker record)
//!
//! Scaffold implements schema + length. PII regex (SSN pattern,
//! salary-number pattern) lives in `shared::pii::strip_pii` — plug in
//! a follow-up when the validator caller knows the worker record to
//! cross-check name consistency.

use crate::{Artifact, Report, Validator, ValidationError};
use std::time::Instant;

pub struct EmailValidator;

const SMS_MAX_CHARS: usize = 160;
const EMAIL_SUBJECT_MAX_CHARS: usize = 78;

impl Validator for EmailValidator {
    fn name(&self) -> &'static str { "staffing.email" }

    fn validate(&self, artifact: &Artifact) -> Result<Report, ValidationError> {
        let started = Instant::now();
        let value = match artifact {
            Artifact::EmailDraft(v) => v,
            other => return Err(ValidationError::Schema {
                field: "artifact".into(),
                reason: format!("EmailValidator expects EmailDraft, got {other:?}"),
            }),
        };

        let to = value.get("to").and_then(|v| v.as_str()).ok_or(
            ValidationError::Schema {
                field: "to".into(),
                reason: "missing or not a string".into(),
            },
        )?;
        let body = value.get("body").and_then(|v| v.as_str()).ok_or(
            ValidationError::Schema {
                field: "body".into(),
                reason: "missing or not a string".into(),
            },
        )?;

        let is_sms = value.get("kind").and_then(|k| k.as_str()) == Some("sms");
        if is_sms && body.len() > SMS_MAX_CHARS {
            return Err(ValidationError::Completeness {
                reason: format!("SMS body is {} chars, max {SMS_MAX_CHARS}", body.len()),
            });
        }

        if let Some(subject) = value.get("subject").and_then(|v| v.as_str()) {
            if subject.len() > EMAIL_SUBJECT_MAX_CHARS {
                return Err(ValidationError::Completeness {
                    reason: format!(
                        "email subject is {} chars, max {EMAIL_SUBJECT_MAX_CHARS}",
                        subject.len()
                    ),
                });
            }
        }

        let _ = to; // touched for future name-consistency check
        // TODO(phase-43 v2): PII scan + worker-name consistency.

        Ok(Report {
            findings: vec![],
            elapsed_ms: started.elapsed().as_millis() as u64,
        })
    }
}

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

    #[test]
    fn long_sms_fails_completeness() {
        let body = "x".repeat(200);
        let r = EmailValidator.validate(&Artifact::EmailDraft(serde_json::json!({
            "to": "+15555550123",
            "body": body,
            "kind": "sms"
        })));
        assert!(matches!(r, Err(ValidationError::Completeness { .. })));
    }

    #[test]
    fn long_email_subject_fails_completeness() {
        let r = EmailValidator.validate(&Artifact::EmailDraft(serde_json::json!({
            "to": "a@b.com",
            "body": "hi",
            "subject": "x".repeat(100)
        })));
        assert!(matches!(r, Err(ValidationError::Completeness { .. })));
    }

    #[test]
    fn missing_to_fails_schema() {
        let r = EmailValidator.validate(&Artifact::EmailDraft(serde_json::json!({"body": "hi"})));
        assert!(matches!(r, Err(ValidationError::Schema { field, .. }) if field == "to"));
    }

    #[test]
    fn well_formed_email_passes() {
        let r = EmailValidator.validate(&Artifact::EmailDraft(serde_json::json!({
            "to": "hiring@example.com",
            "subject": "Interview: Friday 10am",
            "body": "Hi Jane — confirming interview Friday 10am."
        })));
        assert!(r.is_ok(), "well-formed email should pass: {:?}", r);
    }
}
