use crate::widget::{
    unit_list::{column::SysdColumn, menus::create_col_menu},
    unit_properties_selector::{data_browser::PropertyBrowseItem, save::UnitColumn},
};
use adw::subclass::prelude::ObjectSubclassIsExt;
use gtk::glib::{self};
use std::collections::HashSet;
use tracing::info;

glib::wrapper! {
    pub struct UnitPropertySelection(ObjectSubclass<imp2::UnitPropertySelectionImpl>)    ;
}

impl UnitPropertySelection {
    pub fn from_browser(broswer_property: PropertyBrowseItem) -> Self {
        let interface = broswer_property.interface();
        let unit_property = broswer_property.unit_property();
        let sign = broswer_property.signature();

        println!("{interface} {unit_property} {sign:?}");
        let sysd_col = SysdColumn::new_from_props(&unit_property, &interface, sign);
        println!(" {sysd_col:?}");

        let col = if let Some(col) = broswer_property.column() {
            info!("COL {:?} {:?}", col.id(), col.title());
            col
        } else {
            let menu = create_col_menu(&sysd_col);

            let col = gtk::ColumnViewColumn::builder()
                .title(sysd_col.property())
                .id(sysd_col.id())
                .header_menu(&menu)
                .resizable(true)
                .build();
            info!("New COL {:?} {:?}", col.id(), col.title());
            col
        };

        let this_object = Self::from_column_view_column(col, sysd_col);
        let p_imp = this_object.imp();
        p_imp.access.replace(broswer_property.access());
        this_object
    }

    pub fn from_column_view_column(column: gtk::ColumnViewColumn, sysd_col: SysdColumn) -> Self {
        let this_object: Self = glib::Object::new();

        let p_imp = this_object.imp();

        p_imp.column.replace(column);
        p_imp.sysd_column.replace(Some(sysd_col));

        this_object
    }

    // fn fill_from_id(p_imp: &imp2::UnitPropertySelectionImpl, id: &str) {
    //     let custom_id: SysdColumn = (id, None).into();
    //     p_imp.unit_property.replace(custom_id.id().to_string());
    //     p_imp.sysd_column.replace(Some(custom_id));

    //     debug!("UNIT TYPE FROM ID {} ", id);
    // }

    pub fn from_column_config(unit_column_config: UnitColumn) -> Self {
        let col = match SysdColumn::verify(&unit_column_config) {
            Ok(sc) => sc,
            Err(e) => e.1,
        };
        Self::from_column_config2(unit_column_config, col)
    }

    pub fn from_column_config2(unit_column_config: UnitColumn, sysd_col: SysdColumn) -> Self {
        let title = unit_column_config
            .title
            .unwrap_or(sysd_col.property().to_owned());

        let column = gtk::ColumnViewColumn::builder()
            .id(sysd_col.id())
            .fixed_width(unit_column_config.fixed_width)
            .expand(unit_column_config.expands)
            .resizable(unit_column_config.resizable)
            .visible(unit_column_config.visible)
            .title(title)
            // .sorter(&unit_column_config.sort)
            .build();

        let column_menu = create_col_menu(&sysd_col);
        column.set_header_menu(Some(&column_menu));
        let this_object = Self::from_column_view_column(column, sysd_col);

        //TODO simplify
        this_object.set_sort(unit_column_config.sort.unwrap_or_default());

        this_object
    }

    // pub fn from_column(column_name: String) -> Self {
    //     let this_object: Self = glib::Object::new();

    //     let p_imp = this_object.imp();

    //     p_imp.unit_property.replace(column_name);

    //     this_object
    // }

    pub fn is_custom(&self) -> bool {
        self.imp().is_custom()
    }

    pub fn copy(&self) -> Self {
        let this_object: Self = glib::Object::new();
        self.copy_to(&this_object);

        this_object
    }

    pub fn copy_to(&self, to: &Self) {
        let p_imp = to.imp();

        // p_imp.unit_property.replace(self.unit_property());
        // p_imp.prop_type.replace(self.prop_type());
        p_imp.access.replace(self.access());
        // p_imp.unit_type.set(self.unit_type());

        p_imp.sysd_column.replace(self.sysd_column());
        {
            let col = self.column();
            let cur_col = p_imp.column.borrow();

            Self::copy_col_to_col(&col, &cur_col);
        }
    }

    pub fn copy_col_to_col(from: &gtk::ColumnViewColumn, to: &gtk::ColumnViewColumn) {
        to.set_expand(from.expands());
        to.set_factory(from.factory().as_ref());
        to.set_fixed_width(from.fixed_width());
        to.set_header_menu(from.header_menu().as_ref());
        to.set_id(from.id().as_deref());
        to.set_resizable(from.is_resizable());
        to.set_sorter(from.sorter().as_ref());
        to.set_title(from.title().as_deref());
        to.set_visible(from.is_visible());
    }

    pub fn fill_property_fetcher(&self, property_list_send: &mut HashSet<SysdColumn>) {
        if let Some(col) = self.sysd_column()
            && !matches!(col, SysdColumn::FullName | SysdColumn::Active)
        {
            property_list_send.insert(col);
        }
    }

    pub fn sysd_column(&self) -> Option<SysdColumn> {
        self.imp().sysd_column()
    }

    pub fn prop_type(&self) -> Option<String> {
        self.imp().prop_type()
    }

    pub fn unit_property(&self) -> String {
        self.imp().unit_property()
    }
}

mod imp2 {
    use std::cell::{Cell, RefCell};

    use glib::GString;
    use gtk::{glib, prelude::*, subclass::prelude::*};

    use crate::{
        systemd::enums::UnitType,
        widget::{
            unit_list::{column::SysdColumn, get_clean_col_title},
            unit_properties_selector::save::SortType,
        },
    };

    #[derive(Debug, glib::Properties, Default)]
    #[properties(wrapper_type = super::UnitPropertySelection)]
    pub struct UnitPropertySelectionImpl {
        /*         #[property(get)]
        pub(super) interface: RefCell<String>, */
        // #[property(get)]
        // pub(super) unit_property: RefCell<String>,
        // #[property(get)]
        // pub(super) prop_type: RefCell<Option<String>>,
        #[property(get)]
        pub(super) access: RefCell<Option<String>>,
        #[property(name = "visible", get= Self::visible, set= Self::set_visible, type = bool)]
        #[property(name = "id", get= Self::id, set= Self::set_id, type = Option<GString>)]
        #[property(name = "title", get=Self::title, set=Self::set_title, type = Option<String>)]
        #[property(name = "fixed-width", get= Self::fixed_width, set= Self::set_fixed_width, type = i32)]
        #[property(name = "resizable", get= Self::resizable, set= Self::set_resizable, type = bool)]
        #[property(name = "expands", get= Self::expands, set= Self::set_expand, type = bool)]
        #[property(get, set)]
        pub(super) column: RefCell<gtk::ColumnViewColumn>,

        #[property(name = "interface", get= Self::interface, type = String)]
        // #[property(get, default)]
        // pub(super) unit_type: Cell<UnitType>,
        #[property(get, set, default)]
        pub(super) sort: Cell<SortType>,

        pub(super) sysd_column: RefCell<Option<SysdColumn>>,
    }

    impl UnitPropertySelectionImpl {
        pub fn is_custom(&self) -> bool {
            self.sysd_column
                .borrow()
                .as_ref()
                .map(|s| s.is_custom())
                .unwrap_or(true)
        }

        fn interface(&self) -> String {
            self.unit_type().interface().to_string()
        }

        fn visible(&self) -> bool {
            self.column.borrow().is_visible()
        }

        fn set_visible(&self, visible: bool) {
            self.column.borrow().set_visible(visible)
        }

        fn id(&self) -> Option<GString> {
            self.column.borrow().id()
        }

        fn set_id(&self, id: Option<&str>) {
            self.column.borrow().set_id(id)
        }

        fn title(&self) -> Option<String> {
            self.column
                .borrow()
                .title()
                .map(|t| get_clean_col_title(t.as_str()))
        }

        fn set_title(&self, title: Option<&str>) {
            self.column.borrow().set_title(title)
        }

        fn fixed_width(&self) -> i32 {
            self.column.borrow().fixed_width()
        }

        fn set_fixed_width(&self, fixed_width: i32) {
            self.column.borrow().set_fixed_width(fixed_width);
        }

        fn resizable(&self) -> bool {
            self.column.borrow().is_resizable()
        }

        fn set_resizable(&self, resizable: bool) {
            self.column.borrow().set_resizable(resizable)
        }

        fn expands(&self) -> bool {
            self.column.borrow().expands()
        }

        fn set_expand(&self, expand: bool) {
            self.column.borrow().set_expand(expand)
        }

        pub fn sysd_column(&self) -> Option<SysdColumn> {
            self.sysd_column.borrow().as_ref().cloned()
        }

        fn unit_type(&self) -> UnitType {
            self.sysd_column
                .borrow()
                .as_ref()
                .map(|s| s.utype())
                .unwrap_or(UnitType::Unknown)
        }

        pub(crate) fn prop_type(&self) -> Option<String> {
            self.sysd_column
                .borrow()
                .as_ref()
                .and_then(|s| s.property_type().clone())
        }

        pub(crate) fn unit_property(&self) -> String {
            self.sysd_column
                .borrow()
                .as_ref()
                .map(|s| s.property().to_owned())
                .unwrap_or_default()
        }
    }

    #[glib::object_subclass]
    impl ObjectSubclass for UnitPropertySelectionImpl {
        const NAME: &'static str = "UnitPropertySelection";
        type Type = super::UnitPropertySelection;
    }

    #[glib::derived_properties]
    impl ObjectImpl for UnitPropertySelectionImpl {}
    impl UnitPropertySelectionImpl {}
}
