use std::{
    sync::{Arc, MutexGuard},
    rc::Rc, cell::RefCell
};

use alfis::{
    blockchain::{
        types::MineResult,
        transaction::DomainData
    },
    crypto::CryptoBox,
    Keystore,
    Block,
    Bytes,
    Transaction,
    from_hex,
    is_yggdrasil_record,
    CLASS_DOMAIN,
    DOMAIN_LIFETIME,
    DOMAIN_DIFFICULTY,
    MAX_RECORDS
};

use crate::{Mutex, Context, Miner, ui::KeyData};
use log::{debug, error, info, warn};
use chrono::Utc;

use gtk::{
    prelude::*,
    AlertDialog,
    ApplicationWindow,
    Box,
    Button,
    Entry,
    FileDialog,
    Label,
    Orientation,
    Overlay,
    Popover
};


struct Domain<'a> {
    domain: &'a str,
    timestamp: u32,
    d: &'a str,
}


pub fn menu(app: &adw::Application, window: &ApplicationWindow, parent: &gtk::Box, context: &Arc<Mutex<Context>>, miner: &Arc<Mutex<Miner>>) {
    // let new_domain_btn = Button::with_label("New domain");
    let new_domain_btn = Button::from_icon_name("list-add");
    new_domain_btn.add_css_class("suggested-action");
    new_domain_btn.set_halign(gtk::Align::End);
    new_domain_btn.set_hexpand(false);
    parent.append(&new_domain_btn);

    // let new_domain_child = Box::new(Orientation::Vertical, 16);

    // let new_domain_desc = Box::new(Orientation::Horizontal, 8);
    // new_domain_child.append(&new_domain_desc);

    // let domain_entry = Entry::builder()
    //     .placeholder_text("example")
    //     .hexpand(true)
    //     .build();
    // new_domain_desc.append(&domain_entry);

    // let new_domain_pop = Popover::builder()
    //     .autohide(true)
    //     .child(&new_domain_child)
    //     .build();
    // parent.append(&new_domain_pop);

    // let _ = new_domain_btn.connect_clicked(move |_| { new_domain_pop.popup(); });

    // let win = ApplicationWindow::builder()
    //     .application(app)
    //     .title("Мое модальное окно")
    //     .default_width(400)
    //     .default_height(300)
    //     .modal(true) // Это делает окно модальным
    //     .build();

    // win.set_child(Some(&Label::new(Some("Meow"))));

    // // Устанавливаем родительское окно (допустим, `parent_window` - это ссылка на главное окно)
    // parent.append(&win);
    // // let tmp_modal = AlertDialog::builder()
    // //     .message("This is the test message")
    // //     .modal(true)
    // //     .build();
    // let tmp_layout = Overlay::builder()
    //     .child(&Label::new(Some("This is the test message")))
    //     .build();
    // parent.append(&tmp_layout);
    // // tmp_modal.show(Some(window));
}

fn load_domains(context: &mut MutexGuard<Context>) {
    // let _ = handle.dispatch(move |web_view|{
    //     web_view.eval("clearMyDomains();")
    // });
    let domains = context.chain.get_my_domains(context.get_keystore());
    let mut domains = domains.iter().map(|(_, d)| d).collect::<Vec<_>>();
    domains.sort_by(|a, b| a.0.cmp(&b.0));
    for (domain, timestamp, data) in domains {
        let d = serde_json::to_string(&data).unwrap();
        let d = d.replace("'", "\\'").replace("\\n", "\\\\n").replace("\"", "\\\"");
        let command = format!("addMyDomain('{}', {}, {}, '{}');", &domain, timestamp, timestamp + DOMAIN_LIFETIME, &d);
        // let _ = handle.dispatch(move |web_view|{
        //     web_view.eval(&command)
        // });
    }
    // let _ = handle.dispatch(move |web_view|{
    //     web_view.eval("refreshMyDomains();")
    // });
}

fn action_create_domain(
    context:    Arc<Mutex<Context>>,
    miner:      Arc<Mutex<Miner>>,
    name:       String,
    data:       String,
    signing:    String,
    encryption: String,
    renewal:    bool
) {
    debug!("Creating domain with data: {}", &data);
    let c = Arc::clone(&context);
    let context = context.lock().unwrap();
    if !context.has_keys() {
        // show_warning(web_view, "You don't have keys loaded!<br>Load or mine the keys and try again.");
        // let _ = web_view.eval("domainMiningUnavailable();");
        return;
    }
    if context.chain.is_waiting_signers() {
        // show_warning(web_view, "Waiting for last full block to be signed. Try again later.");
        // let _ = web_view.eval("domainMiningUnavailable();");
        info!("Waiting for last full block to be signed. Try again later.");
        return;
    }
    let keystore = context.get_keystore().unwrap().clone();
    let pub_key = keystore.get_public();
    let data = match serde_json::from_str::<DomainData>(&data) {
        Ok(data) => data,
        Err(e) => {
            // show_warning(web_view, "Something wrong with domain data. I cannot mine it.");
            // let _ = web_view.eval("domainMiningUnavailable();");
            warn!("Error parsing data: {}", e);
            return;
        }
    };
    info!("Parsed domain data:\n{:#?}", &data);
    if data.records.len() > MAX_RECORDS {
        // show_warning(web_view, "Too many records. Mining more than 30 records not allowed.");
        // let _ = web_view.eval("domainMiningUnavailable();");
        return;
    }
    // Check if yggdrasil only quality of zone is not violated
    let zones = context.chain.get_zones();
    for z in zones {
        if z.name == data.zone && z.yggdrasil {
            for record in &data.records {
                if !is_yggdrasil_record(record) {
                    // show_warning(web_view, &format!("Zone {} is Yggdrasil only, you cannot use IPs from clearnet!", &data.zone));
                    // let _ = web_view.eval("domainMiningUnavailable();");
                    return;
                }
            }
        }
    }
    let (signing, encryption) = if signing.is_empty() || encryption.is_empty() {
        (keystore.get_public(), keystore.get_encryption_public())
    } else {
        (Bytes::new(from_hex(&signing).unwrap()), Bytes::new(from_hex(&encryption).unwrap()))
    };
    match context.chain.can_mine_domain(context.chain.get_height(), &name, &pub_key) {
        MineResult::Fine => {
            drop(context);
            create_domain(c, miner, CLASS_DOMAIN, &name, data, DOMAIN_DIFFICULTY, &keystore, signing, encryption, renewal);
            // let _ = web_view.eval("domainMiningStarted();");
            // event_info(web_view, &format!("Mining of domain \\'{}\\' has started", &name));
        }
        MineResult::WrongName => {
            // show_warning(web_view, "You can't mine this domain!");
            // let _ = web_view.eval("domainMiningUnavailable();");
        }
        MineResult::WrongData => {
            // show_warning(web_view, "You have an error in records!");
            // let _ = web_view.eval("domainMiningUnavailable();");
        }
        MineResult::WrongKey => {
            // show_warning(web_view, "You can't mine with current key!");
            // let _ = web_view.eval("domainMiningUnavailable();");
        }
        MineResult::WrongZone => {
            // show_warning(web_view, "You can't mine domain in this zone!");
            // let _ = web_view.eval("domainMiningUnavailable();");
        }
        MineResult::NotOwned => {
            // show_warning(web_view, "This domain is already taken, and it is not yours!");
            // let _ = web_view.eval("domainMiningUnavailable();");
        }
        MineResult::Cooldown { time } => {
            // event_info(web_view, &format!("You have cooldown {}!", format_cooldown(time)));
            // show_warning(web_view, &format!("You have cooldown {}!", format_cooldown(time)));
            // let _ = web_view.eval("domainMiningUnavailable();");
        }
    }
}

fn create_domain(
    context:    Arc<Mutex<Context>>,
    miner:      Arc<Mutex<Miner>>,
    class:      &str,
    name:       &str,
    mut data:   DomainData,
    difficulty: u32,
    keystore:   &Keystore,
    signing:    Bytes,
    encryption: Bytes,
    renewal:    bool
) {
    let name = name.to_owned();
    let encrypted = CryptoBox::encrypt(encryption.as_slice(), name.as_bytes()).expect("Error encrypting domain name!");
    data.encrypted = Bytes::from_bytes(&encrypted);

    let data = serde_json::to_string(&data).unwrap();
    let (signing, encryption) = if signing.is_empty() || encryption.is_empty() {
        (keystore.get_public(), keystore.get_encryption_public())
    } else {
        (signing, encryption)
    };
    let transaction = Transaction::from_str(name, class.to_owned(), data, signing, encryption);
    // If this domain is already in blockchain we approve slightly smaller difficulty
    let height = context.lock().unwrap().chain.get_height();
    let discount = context.lock().unwrap().chain.get_identity_discount(&transaction.identity, renewal, height, Utc::now().timestamp());
    let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default(), difficulty - discount);
    miner.lock().unwrap().add_block(block, keystore.clone());
}

