use lettre::message::Mailbox;
use lettre::transport::smtp::client::Tls;
use lettre::{Message, SmtpTransport, Transport};

pub fn send_notification(
    id: i64,
    question: &str,
    notify_email: &str,
    mail_domain: &str,
    reply_domain: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let truncated = if question.len() > 50 {
        format!("{}...", &question[..50])
    } else {
        question.to_string()
    };

    let from: Mailbox = format!("Q&A <qa@{mail_domain}>").parse()?;
    let reply_to: Mailbox = format!("qa@{reply_domain}").parse()?;
    let to: Mailbox = notify_email.parse()?;

    let email = Message::builder()
        .from(from)
        .reply_to(reply_to)
        .to(to)
        .subject(format!("{id} - {truncated}"))
        .body(question.to_string())?;

    let mailer = SmtpTransport::builder_dangerous("localhost")
        .tls(Tls::None)
        .hello_name(lettre::transport::smtp::extension::ClientId::Domain(
            mail_domain.to_string(),
        ))
        .build();
    mailer.send(&email)?;

    Ok(())
}

pub fn extract_id_from_subject(subject: &str) -> Result<i64, Box<dyn std::error::Error>> {
    let subject = subject.trim();
    let start = subject
        .char_indices()
        .find_map(|(idx, ch)| ch.is_ascii_digit().then_some(idx))
        .ok_or("Subject missing question id")?;
    let digits: String = subject[start..]
        .chars()
        .take_while(|c| c.is_ascii_digit())
        .collect();

    if digits.is_empty() {
        return Err("Subject missing numeric question id".into());
    }

    let remainder = subject[start + digits.len()..].trim_start();
    if !(remainder.starts_with('-') || remainder.starts_with(':')) {
        return Err("Subject missing separator after question id".into());
    }

    Ok(digits.parse()?)
}

pub fn extract_plain_text_body(contents: &str) -> String {
    let normalized = contents.replace("\r\n", "\n");
    let body = if let Some((headers, body)) = normalized.split_once("\n\n") {
        if headers.lines().any(|line| line.contains(':')) {
            body
        } else {
            normalized.as_str()
        }
    } else {
        normalized.as_str()
    };

    strip_quoted_text(body)
}

pub fn strip_quoted_text(body: &str) -> String {
    let mut result = Vec::new();
    for line in body.lines() {
        if line.starts_with('>') {
            continue;
        }
        if line.starts_with("On ") && line.ends_with("wrote:") {
            break;
        }
        result.push(line);
    }
    result.join("\n").trim().to_string()
}

#[cfg(test)]
mod tests {
    use super::{extract_id_from_subject, extract_plain_text_body};

    #[test]
    fn extracts_id_from_subject() {
        assert_eq!(extract_id_from_subject("Re: 42 - Hello").unwrap(), 42);
    }

    #[test]
    fn extracts_plain_text_from_raw_email() {
        let raw = "Subject: Q&A #42: Hello\r\nFrom: Jet <jet@example.com>\r\n\r\nThis is the answer.\r\n\r\nOn earlier mail wrote:\r\n> quoted";
        assert_eq!(extract_plain_text_body(raw), "This is the answer.");
    }
}
