use lettre::{
    message::header::ContentType, transport::smtp::authentication::Credentials, Message,
    SmtpTransport,Tokio1Executor, Transport,message::*,AsyncTransport, AsyncSmtpTransport, 
};
use tera::Tera;
use tera::Context;
use serde_json::json;
use std::time::Duration;
use std::thread;
use std::fs;
use std::io::prelude::*;
use serde::Deserialize;
use serde::Serialize;
use chrono::{TimeZone, Local, Datelike, Timelike, Utc,};
use rand::Rng;

const SECRETPASSWORD :&str = "sp1_censored";
pub const EMAILOFMIKIYA : &str = "email1_censor_placeholder";
pub const EMAILOFMARC : &str = "email2_censor_placeholder";
pub const EMAILOFNICLAS :&str = "email3_censor_placeholder";
pub const SMTPEMAIL :&str = "smtpmail_censor_placeholder";
pub static ADDRESSOFSELF :&str = "https://haushaltsserver.ddns.net";

//pub static ADDRESSOFSELF :&str = "http://192.168.0.81:7980";
#[derive(FromForm)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Table {
    pub marc: (String,String),
    pub mikiya: (String,String),
    pub niclas: (String,String),
    pub week: usize
}

#[derive(Clone, PartialEq, Deserialize, Debug, Serialize)]
enum WhichAdmin {
    Marc,
    Mikiya,
    Niclas,
    NoAdmin
}

impl WhichAdmin {
    fn to_str(&self) -> &'static str {
        match self {
            WhichAdmin::Marc =>"marc",
            WhichAdmin::Mikiya => "mikiya",
            WhichAdmin::Niclas => "niclas",
            WhichAdmin::NoAdmin => "No Admin",
        }
    }
    fn email(&self) -> &'static str{
        match self {
            WhichAdmin::Marc =>EMAILOFMARC,
            WhichAdmin::Mikiya => EMAILOFMIKIYA,
            WhichAdmin::Niclas => EMAILOFNICLAS,
            WhichAdmin::NoAdmin => "No Admin",
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone,)]
struct PendingTable {
    replacing: String,
    what: String,
    week: usize,
    requested_by: String,
    when_exactly: String,
    key: u64
}

pub fn sauber_polizei(){
    loop {  
        thread::sleep(Duration::from_secs(60*60*15));   
        //thread::sleep(Duration::from_secs(60*60*15));
        //read local json into string
        let contents = fs::read_to_string("static/example.json").expect("Error reading file");
        //read local json into vector of Table
        let mut tables_from_json: Vec<Table> = serde_json::from_str(&contents).expect("Error parsing JSON");

        let start_time = Local.ymd(2023, 01, 01).and_hms(08, 00, 0);
        let date_today = Local::now();
        let weeks_elapsed;

        if date_today>start_time{
            weeks_elapsed = (date_today-start_time).num_weeks();
        }
        else {
            weeks_elapsed = 0;
        }

        let mut week_date_start = start_time + chrono::Duration::weeks(weeks_elapsed);
        let mut week_date_end = start_time + chrono::Duration::weeks(weeks_elapsed) + chrono::Duration::days(6);       
        let days_remaining_in_this_week = (week_date_end-date_today).num_days();


/*         for blah in 0..14 {
            let temp_week_date=format!("{:02}-{:02}-{} bis {:02}-{:02}-{}", week_date_start.day(), week_date_start.month(), week_date_start.year(), week_date_end.day(), week_date_end.month(), week_date_end.year());
            fourteen_weeks.push(temp_week_date);
            week_date_start = week_date_start + chrono::Duration::days(7);   
            week_date_end = week_date_end + chrono::Duration::days(7);   
        } */
        let mut marc_which_active:Vec<usize>=vec![];
        let mut mikiya_which_active:Vec<usize>=vec![];
        let mut niclas_which_active:Vec<usize>=vec![];

        let mut marc_which_missing:Vec<usize>=vec![];
        let mut mikiya_which_missing:Vec<usize>=vec![];
        let mut niclas_which_missing:Vec<usize>=vec![];

       let mut marc_missing = 0;
       let mut mikiya_missing= 0;
       let mut niclas_missing= 0;

       let mut marc_left_at :usize = 0;
       let mut mikiya_left_at:usize =0;
       let mut niclas_left_at:usize=0;

       println!("commencing sauber_polizei");

       let min_week = tables_from_json.iter().map(|t| t.week).min().unwrap_or(0);
       let max_week = weeks_elapsed;

       let mut missing_weeks = Vec::new();
        for week in min_week..=max_week as usize{
            if !tables_from_json.iter().any(|t| t.week == week) {
                missing_weeks.push(week);
            }
        }
        println!("missing weeks : {:?}", missing_weeks);

        // Insert new Table elements with the missing weeks at the appropriate position
        for week in missing_weeks {
            let new_table = Table { marc:("0".to_owned(),"schau bitte online".to_owned()),mikiya:("0".to_owned(),"schau bitte online".to_owned()),niclas:("0".to_owned(),"schau bitte online".to_owned()), week:week };
            let pos = tables_from_json.iter().position(|t| t.week > week).unwrap_or(tables_from_json.len());
            tables_from_json.insert(pos, new_table);
    }

       for i in 0..tables_from_json.len() {
            match tables_from_json[i].marc.0.as_str() {
                "0"=>{
                    if i >= marc_left_at{
                        let mut missing_in_span=0;
                        while let Some(is_this_zero) = tables_from_json.get(i+missing_in_span+1){
                            match is_this_zero.marc.0.as_str(){
                                "0"=>{marc_missing=marc_missing+1;marc_which_missing.push(tables_from_json[i+missing_in_span+1].week);missing_in_span=missing_in_span+1;}
                                &_=>{marc_which_active.push(tables_from_json[i+missing_in_span+1].week);break;}
                            }
                            marc_left_at=i+missing_in_span+1;
                        }
                    }
                }
                &_ => {}
            }
            match tables_from_json[i].mikiya.0.as_str() {
                "0"=>{
                    if i >= mikiya_left_at{
                        let mut missing_in_span=0;
                        while let Some(is_this_zero) = tables_from_json.get(i+missing_in_span+1){
                            match is_this_zero.mikiya.0.as_str(){
                                "0"=>{mikiya_missing=mikiya_missing+1;mikiya_which_missing.push(tables_from_json[i+missing_in_span+1].week);missing_in_span=missing_in_span+1;}
                                &_=>{mikiya_which_active.push(tables_from_json[i+missing_in_span+1].week);break;}
                            }
                            mikiya_left_at=i+missing_in_span+1;
                        }
                    }
                }
                &_ => {}
            }
            match tables_from_json[i].niclas.0.as_str() {
                "0"=>{
                    if i >= niclas_left_at{
                        let mut missing_in_span=0;
                        while let Some(is_this_zero) = tables_from_json.get(i+missing_in_span+1){
                            match is_this_zero.niclas.0.as_str(){
                                "0"=>{niclas_missing=niclas_missing+1;niclas_which_missing.push(tables_from_json[i+missing_in_span+1].week);missing_in_span=missing_in_span+1;}
                                &_=>{niclas_which_active.push(tables_from_json[i+missing_in_span+1].week);break;}
                            }
                            niclas_left_at=i+missing_in_span+1;
                        }
                    }
                }
                &_ => {}
            }
        }

        let weeks_total: Vec<usize> = (1..=(weeks_elapsed as usize)).collect();

        let marcs_positive_weeks: Vec<usize> = weeks_total.iter().filter(|x| !marc_which_missing.contains(x)).cloned().collect();
        let marcs_last_week_covered = marcs_positive_weeks.iter().map(|t| t).max().unwrap_or(&(0 as usize));

        let mikiyas_positive_weeks: Vec<usize> = weeks_total.iter().filter(|x| !mikiya_which_missing.contains(x)).cloned().collect();
        let mikiyas_last_week_covered = mikiyas_positive_weeks.iter().map(|t| t).max().unwrap_or(&(0 as usize));

        let niclas_positive_weeks: Vec<usize> = weeks_total.iter().filter(|x| !niclas_which_missing.contains(x)).cloned().collect();
        let niclas_last_week_covered = niclas_positive_weeks.iter().map(|t| t).max().unwrap_or(&(0 as usize));

        println!("{:?}", marcs_positive_weeks);
        println!("marcs last week{}",marcs_last_week_covered);
        println!("marc missing{}",marc_missing);
        println!("marc missing{:#?}",marc_which_missing);
        println!("marc active{:#?}",marc_which_active);

        let contents = fs::read_to_string("private/pending.json").expect("Error reading file");
        let mut pending_tables_from_json: Vec<PendingTable> = serde_json::from_str(&contents).expect("Error parsing JSON");
        let mut rng = rand::thread_rng();
        let key1:u64 = rng.gen_range(0..184467);
        let key2:u64 = rng.gen_range(0..184467);
        let key3:u64 = rng.gen_range(0..184467);
        //get entry(s) of specified key
        //let pending_tables_with_key: Vec<PendingTable> = tables_from_json_pending.clone().into_iter().filter(|table| table.key == key).collect();
        //pending_tables_from_json.push(pending_request);
        //let temp_date=format!("{}-{:02}-{:02}", date_today.year(), date_today.month(), date_today.day());
        for mut item in vec![key1,key2,key3]{
            while pending_tables_from_json.iter().any(|s| s.key == item) {
                item = rng.gen_range(0..184467);
            }
        }

        match marcs_last_week_covered{
            y if y == &(weeks_elapsed as usize)=>{}
            &_=>{
                if days_remaining_in_this_week<=2{
                    if let Some(last) = tables_from_json.last(){
                        let pending_request = PendingTable {
                            what: "Excuse".to_string(),
                            week: weeks_elapsed as usize,
                            requested_by: "marc".to_string(),
                            when_exactly: format!("{}-{:02}-{:02}", date_today.year(), date_today.month(), date_today.day()),
                            replacing: "marc".to_string(),
                            key: key1
                        };
                        pending_tables_from_json.push(pending_request);
                        dringlichkeitsbenachrichtigung(WhichAdmin::Marc, days_remaining_in_this_week, format!("{:#?}",marcs_positive_weeks), format!("{:#?}",marc_which_missing), format!("{:#?}",marc_which_active), last.marc.1.clone(),key1);
                    }
                }
            }
        }

        match mikiyas_last_week_covered{
            y if y == &(weeks_elapsed as usize)=>{}
            &_=>{
                if days_remaining_in_this_week<=2{
                    if let Some(last) = tables_from_json.last(){
                        let pending_request = PendingTable {
                            what: "Excuse".to_string(),
                            week: weeks_elapsed as usize,
                            requested_by: "mikiya".to_string(),
                            when_exactly: format!("{}-{:02}-{:02}", date_today.year(), date_today.month(), date_today.day()),
                            replacing: "mikiya".to_string(),
                            key: key2
                        };
                        pending_tables_from_json.push(pending_request);
                        dringlichkeitsbenachrichtigung(WhichAdmin::Mikiya, days_remaining_in_this_week, format!("{:#?}",mikiyas_positive_weeks), format!("{:#?}",mikiya_which_missing), format!("{:#?}",mikiya_which_active), last.mikiya.1.clone(),key2);
                    }
                }
            }
        }

        match niclas_last_week_covered{
           y if y == &(weeks_elapsed as usize)=>{}
            &_=>{
                if days_remaining_in_this_week<=2{
                    if let Some(last) = tables_from_json.last(){
                        let pending_request = PendingTable {
                            what: "Excuse".to_string(),
                            week: weeks_elapsed as usize,
                            requested_by: "niclas".to_string(),
                            when_exactly: format!("{}-{:02}-{:02}", date_today.year(), date_today.month(), date_today.day()),
                            replacing: "niclas".to_string(),
                            key: key3
                        };
                        pending_tables_from_json.push(pending_request);
                        dringlichkeitsbenachrichtigung(WhichAdmin::Niclas, days_remaining_in_this_week, format!("{:#?}",niclas_positive_weeks), format!("{:#?}",niclas_missing), format!("{:#?}",niclas_which_active), last.niclas.1.clone(),key3);
                    }
                }
            }
        }
        let new_contents = serde_json::to_string_pretty(&pending_tables_from_json).expect("Error serializing tables");
        fs::write("private/pending.json", new_contents).expect("Error writing to file");
        
    }
}

fn dringlichkeitsbenachrichtigung(who: WhichAdmin, days_remaining:i64, covered:String, missing:String, active:String, task:String, excusekey:u64){
    //let to_address = who.email();
    let to_address = who.email();
    let subject = format!("Bitte mach innerhalb von {} Tagen  {}", days_remaining, task);
    let mailboxes: Mailboxes = to_address.parse().unwrap();
    let to_header: header::To = mailboxes.into();
    //let content = format!("Bestätige, dass {} das {} sauber gemacht hat", whodidit, what);

    let tera = match Tera::new("templates/email/*.html.tera") {
        Ok(t) => { println!("success with tera");t},
        Err(e) => {
            println!("Parsing error(s): {}", e);
            ::std::process::exit(1);
        }
    };
    let excusekeylink = format!("{}/Excusemon/{}",ADDRESSOFSELF,excusekey);
    let mut context = Context::new();
    context.insert("Daysremaining", &days_remaining);
    context.insert("covered",&covered);
    context.insert("missing", &missing);
    context.insert("active", &active);
    context.insert("task", &task);
    context.insert("who", who.to_str());
    context.insert("Excusekeylink", &excusekeylink);
    let mut beautiful_content="<p><b>Hello</b>, <i>world</i>! <img src=\"cid:123\"></p>".to_string();
    match tera.render("sauberpolizei.html.tera", &context) {
        Ok(result)=>{beautiful_content=result;}
        Err(error)=>{println!("{:#?}",error);}
    }
    let _email = MessageBuilder::new()
        .mailbox(to_header)
        .from("Haushaltsserver <smtpmail_censor_placeholder>".parse().unwrap())
        .subject(subject)
    //   .singlepart(SinglePart::html(mail_body))
        .multipart(MultiPart::alternative_plain_html(
        String::from("Hello, world! :)"),
        String::from(beautiful_content),))
        .unwrap();

        let creds = Credentials::new(SMTPEMAIL.to_owned(), SECRETPASSWORD.to_owned());

        // Open a remote connection to gmail
        let mailer= SmtpTransport::relay("mail.gmx.net")
            .unwrap()
            .credentials(creds)
            .build();

        // Send the email
        match mailer.send(&_email) {
            Ok(_) => println!("Email sent successfully!"),
            Err(e) => panic!("Could not send email: {e:?}"),
        }

}

pub fn lol(addressee: String, whodidit: String, when: String, what: String, magiclink: String) {
        //tracing_subscriber::fmt::init();
        //let to_address = "email2_censor_placeholder, mbasjp@gmail.com, email3_censor_placeholder";
        let to_address = addressee;
        let subject = format!("{} {} {}", what, when, whodidit);
        let mailboxes: Mailboxes = to_address.parse().unwrap();
        let to_header: header::To = mailboxes.into();
        let content = format!("Bestätige, dass {} das {} sauber gemacht hat", whodidit, what);

        //now we create a beautiful message
        let tera = match Tera::new("templates/email/*.html.tera") {
            Ok(t) => { println!("success with tera");t},
            Err(e) => {
                println!("Parsing error(s): {}", e);
                ::std::process::exit(1);
            }
        };
        let mut context = Context::new();
        context.insert("Headline", &content);
        context.insert("magiclink",&magiclink);
        let mut beautiful_content="<p><b>Hello</b>, <i>world</i>! <img src=\"cid:123\"></p>".to_string();
        match tera.render("pendingrequest.html.tera", &context) {
            Ok(result)=>{beautiful_content=result;}
            Err(error)=>{println!("{:#?}",error);}
        }
        let _email = MessageBuilder::new()
        .mailbox(to_header)
        .from("Haushaltsserver <smtpmail_censor_placeholder>".parse().unwrap())
        .subject(subject)
    //   .singlepart(SinglePart::html(mail_body))
        .multipart(MultiPart::alternative_plain_html(
        String::from("Hello, world! :)"),
        String::from(beautiful_content),))
        .unwrap();

        let creds = Credentials::new(SMTPEMAIL.to_owned(), SECRETPASSWORD.to_owned());

        // Open a remote connection to gmail
        let mailer= SmtpTransport::relay("mail.gmx.net")
            .unwrap()
            .credentials(creds)
            .build();

        // Send the email
        match mailer.send(&_email) {
            Ok(_) => println!("Email sent successfully!"),
            Err(e) => panic!("Could not send email: {e:?}"),
        }
//    });
}

/* #[tokio::main]
pub async fn lol(addressee: &str, whodidit: &str, when: &str, what: &str, magiclink: &str) {
    
    tracing_subscriber::fmt::init();
    //let to_address = "email2_censor_placeholder, mbasjp@gmail.com, email3_censor_placeholder";
    let to_address = addressee;
    let subject = format!("{} {} {}", what, when, whodidit);
    let mailboxes: Mailboxes = to_address.parse().unwrap();
    let to_header: header::To = mailboxes.into();
    let content = format!("Bestätige, dass {} das {} sauber gemacht hat", whodidit, what);

    //now we create a beautiful message
    let tera = match Tera::new("templates/**/*.html.tera") {
        Ok(t) => t,
        Err(e) => {
            println!("Parsing error(s): {}", e);
            ::std::process::exit(1);
        }
    };
    let mut context = Context::new();
    context.insert("Headline", &content);
    context.insert("magiclink",magiclink);
    let beautiful_content;
    match tera.render("pendingrequest.html.tera", &context) {
        Ok(result)=>{beautiful_content=result;}
        Err(error)=>{println!("{:#?}",error);}
    }
    let _email = MessageBuilder::new()
    .mailbox(to_header)
    .from("Haushaltsserver <smtpmail_censor_placeholder>".parse().unwrap())
    .subject(subject)
 //   .singlepart(SinglePart::html(mail_body))
    .multipart(MultiPart::alternative_plain_html(
    String::from("Hello, world! :)"),
    String::from("<p><b>Hello</b>, <i>world</i>! <img src=\"cid:123\"></p>"),))
    .unwrap();

    let creds = Credentials::new(SMTPEMAIL.to_owned(), SECRETPASSWORD.to_owned());

    // Open a remote connection to gmail
    let mailer:AsyncSmtpTransport<Tokio1Executor> = AsyncSmtpTransport::<Tokio1Executor>::relay("mail.gmx.net")
        .unwrap()
        .credentials(creds)
        .build();

    // Send the email
    match mailer.send(_email).await {
        Ok(_) => println!("Email sent successfully!"),
        Err(e) => panic!("Could not send email: {e:?}"),
    }
} */


/* pub fn lol() {
    tracing_subscriber::fmt::init();
    let to_address = "email2_censor_placeholder, mbasjp@gmail.com, email3_censor_placeholder";
    //"[email3_censor_placeholder](mailto:email3_censor_placeholder); [mbasjp@gmail.com](mailto:mbasjp@gmail.com); [email2_censor_placeholder](mailto:email2_censor_placeholder)";
    let mailboxes: Mailboxes = to_address.parse().unwrap();
    let to_header: header::To = mailboxes.into();
    let mail_body = MaybeString::String("Be happy!".into());

    let _email = MessageBuilder::new()
    .mailbox(to_header)
    .from("Haushaltsserver <smtpmail_censor_placeholder>".parse().unwrap())
    .subject("Happy new year")
 //   .singlepart(SinglePart::html(mail_body))
    .body(String::from("Be happy!"))
    .unwrap();

    let creds = Credentials::new(SMTPEMAIL.to_owned(), SECRETPASSWORD.to_owned());

    // Open a remote connection to gmail
    let mailer = SmtpTransport::relay("mail.gmx.net")
        .unwrap()
        .credentials(creds)
        .build();

    // Send the email
    match mailer.send(&_email) {
        Ok(_) => println!("Email sent successfully!"),
        Err(e) => panic!("Could not send email: {e:?}"),
    }
} */



/* 
use lettre::{
    transport::smtp::authentication::Credentials, AsyncSmtpTransport, AsyncTransport, Message,
    Tokio1Executor,message::header::ContentType,
};

extern crate imap;


// Creating basic data structure for the email
pub async fn lol() -> Result<(), Box<dyn std::error::Error>> {
    let smtp_credentials =
        Credentials::new("smtpmail_censor_placeholder".to_owned(), "".to_owned());

    let mailer = AsyncSmtpTransport::<Tokio1Executor>::relay("mail.gmx.net")?
        .credentials(smtp_credentials)
//        .port(587)
        .build();

    let from = "Sender <smtpmail_censor_placeholder>";
    let to = "receiver <email3_censor_placeholder>";
    let subject = "Sending email with Rust";
    let body = "<h1>This is my first email</h1>".to_string();

    if let Err(e) = send_email_smtp(&mailer, from, to, subject, body).await {
        println!("Failed to send email: {}", e);
        return Err(e.into());
    }

    Ok(())
}

// Email sending function
async fn send_email_smtp(
    mailer: &AsyncSmtpTransport<Tokio1Executor>,
    from: &str,
    to: &str,
    subject: &str,
    body: String,
) -> Result<(), Box<dyn std::error::Error>> {
    let email = Message::builder()
    .from(from.parse().unwrap())
       // .reply_to("Yuin <yuin@domain.tld>".parse().unwrap())
        .to(to.parse().unwrap())
        .subject(subject)
        .header(ContentType::TEXT_PLAIN)
        .body(String::from("Be happy!"))
        .unwrap();
/*         .from(from.parse()?)
        .to(to.parse()?)
        .subject(subject)
        .body(body.to_string())?; */
    

        if let Err(e) = mailer.send(email).await {
            println!("Failed to send email : {}", e);
            return Err(e.into());
        }
    
        println!("Email sent successfully");
        Ok(())


}
 */