pub mod attachment;
pub mod bug;
pub mod comment;
pub mod user;

use crate::bugzilla::bug::*;
// use crate::bugzilla::user::*;

mod schema;

use rusqlite::Connection;

use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
use std::collections::HashMap;
use std::time::Duration;
use ureq::Agent;
// use ulid::Ulid;

use crate::util::*;

use std::io::Write;

#[derive(Debug, Deserialize, Serialize)]
pub struct BugsList {
    pub bugs: Vec<Bug>,
}

#[derive(Debug)]
pub struct BugzillaClient {
    token: String,
    client: ureq::Agent,
}

impl BugzillaClient {
    pub fn new(login: &str, password: &str) -> Result<Self> {
        let login_endpoint = "https://bugs.freebsd.org/bugzilla/rest/login";

        let client: Agent = ureq::AgentBuilder::new()
            .timeout_read(Duration::from_secs(5))
            .timeout_write(Duration::from_secs(5))
            .build();

        let mut params = HashMap::new();
        params.insert("login", login);
        params.insert("password", password);

        let response: serde_json::Value = client
            .get(login_endpoint)
            .query_pairs(params)
            .call()?
            .into_json()?;

        match response {
            Value::Object(fields) => Ok(Self {
                token: fields["token"]
                    .as_str()
                    .expect("no token in login response")
                    .to_string(),
                client,
            }),
            _ => Err("Bugzilla login failed".into()),
        }
    }

    /// Reads all bugs from bugzilla API and stores them in the sqlite database
    pub fn get_all_bugs(&self, sqlite: &Connection) {
        let bugs_endpoint = "https://bugs.freebsd.org/bugzilla/rest/bug";

        let batch_size = 1000;
        let mut offset = 0;
        let mut total = 0;

        loop {
            // add this to only get open bugs - open bugs have no resolution
            // .query("f1", "resolution")
            // .query("o1", "isempty")
            let bugs: BugsList = self
                .client
                .get(bugs_endpoint)
                .query("token", &self.token)
                .query("limit", &batch_size.to_string())
                .query("offset", &offset.to_string())
                .set("Content-Type", "application/json")
                .set("Accept", "application/json")
                .call()
                .expect("search bugs API call failed")
                .into_json()
                .expect("invalid json was received from API");

            let len = bugs.bugs.len();

            sqlite.execute("BEGIN TRANSACTION", []).unwrap();
            for bug in bugs.bugs.into_iter() {
                bug.insert(sqlite);
            }
            total += len;
            sqlite.execute("COMMIT TRANSACTION", []).unwrap();

            print!("\rGetting bugzilla bugs: {}", total);
            std::io::stdout().flush().unwrap();

            if len < batch_size {
                break;
            } else {
                offset += batch_size;
            }
        }
        println!();
    }

    //  pub fn get_bug_comments(&self, ids: Vec<u32>) -> HashMap<u32, Vec<Comment>> {

    //      if let Ok(comments) = Self::get_comments_from_file("comments.json") {
    //          println!("Read {} bugs from comments.json", comments.len());
    //          return comments;
    //      }
    //      let comment_endpoint = format!("https://bugs.freebsd.org/bugzilla/rest/bug/{}/comment", ids[0]);
    //      let mut result:HashMap<u32, Vec<Comment>> = HashMap::new();

    //      let mut builder =  self.client
    //          .get(&comment_endpoint)
    //          .query("token", self.token.as_str())
    //          .set("Content-Type", "application/json")
    //          .set("Accept", "application/json");

    //      for id in ids.iter() {
    //          builder = builder.query("ids", &id.to_string());
    //      }

    //      let response: serde_json::Value = builder.call()
    //          .expect("search bugs API call failed")
    //          .into_json().expect("invalid json was received from API");

    //      // "bugs" : {
    //      //    "261732" : {
    //      //       "comments" : [

    //      let comment_map = response.get("bugs")
    //          .unwrap()
    //          .as_object()
    //          .unwrap();

    //      for (strid, value) in comment_map.iter() {
    //          let bug_id: u32 = strid.clone().parse::<u32>().unwrap();
    //          assert!(ids.contains(&bug_id));
    //          let mut comments: Vec<Comment> = vec![];

    //          value.get("comments")
    //          .unwrap()
    //          .as_array().unwrap()
    //          .into_iter()
    //          .for_each(|v| {
    //              let c: Comment = serde_json::from_value(v.clone()).unwrap();
    //              comments.push(c);
    //          });

    //          result.insert(bug_id, comments);
    //      }

    //      result
    //  }
}