// #![cfg_attr(not(feature = "simulator"), no_main)]

use core::{cell::RefCell, ffi::CStr, pin::Pin};

use alloc::{
    borrow::Cow,
    boxed::Box,
    ffi::CString,
    format,
    rc::Rc,
    string::{String, ToString},
    vec,
    vec::Vec,
};
use embassy_time::Instant;
use hex::FromHexError;
use i_slint_core::model::{ModelChangeListener, ModelChangeListenerContainer};
use log::{error, info, warn};
use password_hash::Key;
use slint::{
    Model, ModelExt, ModelNotify, ModelRc, ModelTracker, SharedString, StandardListViewItem,
    VecModel,
};
use spectre_api_sys::{
    SpectreAlgorithm, SpectreCounter, SpectreKeyID, SpectreKeyPurpose, SpectreResultType,
    SpectreUserKey,
};

#[cfg(feature = "limit-fps")]
use crate::FRAME_DURATION_MIN;
use crate::{
    PSRAM_ALLOCATOR,
    db::{
        AcidDatabase, DbKey, DbPathSpectreUserSite, DbPathSpectreUserSites, DbPathSpectreUsers,
        PartitionAcid, ReadTransactionExt,
    },
    ffi::{alloc::__spre_free, crypto::ACTIVE_ENCRYPTED_USER_KEY},
    proxy::OUTPUT_STRING_CHANNEL,
    ram::PsramAllocator,
    ui::{
        backend::SlintBackend,
        messages::{
            CallbackMessage, CallbackMessageLogin, CallbackMessageUserEdit,
            CallbackMessageUserSites, CallbackMessageUsers, LoginResult,
        },
        storage::{SpectreSite, SpectreSiteConfig, SpectreUserConfig, SpectreUsersConfig},
    },
    util::DurationExt,
};

pub mod backend;
pub mod messages;
pub mod storage;
pub mod window_adapter;

slint::include_modules!();

#[derive(Clone, Copy)]
struct EmptyPassword;

fn spectre_derive_user_key(
    username: &CStr,
    password: &CStr,
    encrypted_key: Option<Key>,
) -> Result<SpectreUserKey, EmptyPassword> {
    if password.is_empty() {
        return Err(EmptyPassword);
    }

    if let Some(encrypted_key) = encrypted_key {
        ACTIVE_ENCRYPTED_USER_KEY.lock(|user_key| {
            user_key.set(encrypted_key);
        });
    }

    let user_key_start = Instant::now();

    unsafe {
        let user_key = &*spectre_api_sys::spectre_user_key(
            username.as_ptr(),
            password.as_ptr(),
            SpectreAlgorithm::Current,
        );
        let user_key_duration = Instant::now().duration_since(user_key_start);
        warn!(
            "User key derived in {} seconds:\n{user_key:02x?}",
            user_key_duration.display_as_secs()
        );
        let user_key_stack = *user_key;

        // TODO: Erase memory before freeing
        __spre_free(user_key as *const _ as *mut _);

        Ok(user_key_stack)
    }
}

fn spectre_derive_site_password(user_key: &SpectreUserKey, site_name: &CStr) -> String {
    unsafe {
        let site_password_c = &*spectre_api_sys::spectre_site_result(
            user_key as *const SpectreUserKey,
            site_name.as_ptr(),
            SpectreResultType::SpectreResultDefaultResult,
            core::ptr::null(),
            SpectreCounter::Initial,
            SpectreKeyPurpose::Authentication,
            core::ptr::null(),
        );
        let site_password = CStr::from_ptr(site_password_c)
            .to_str()
            .unwrap()
            .to_string();
        __spre_free(site_password_c as *const _ as *mut _);
        site_password
    }
}

#[embassy_executor::task]
pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: PartitionAcid) {
    let db = AcidDatabase::mount(flash_part_acid).await;

    // TODO:
    // * Store a config as a versioned postcard-serialized struct
    // * Store accounts and sites as ranges in the DB

    i_slint_core::properties::ALLOCATOR
        .set(&PSRAM_ALLOCATOR)
        .ok()
        .unwrap();
    slint::platform::set_platform(Box::new(backend)).expect("backend already initialized");

    let main = AppWindow::new().unwrap();
    let state = State::new(db, main).await;
    let window = state.borrow().window.clone_strong();

    State::run_event_loop(window).await;
}

struct State {
    window: AppWindow,
    db: Rc<AcidDatabase, PsramAllocator>,
    users: SpectreUsersConfig,
    /// Currently active view.
    view: AppState,
    // Retained state for each view.
    state_login: StateLogin,
    state_users: StateUsers,
    state_user_edit: StateUserEdit,
    state_user_sites: Option<StateUserSites>,
}

impl State {
    async fn new(db: AcidDatabase, main: AppWindow) -> Rc<RefCell<Self>, PsramAllocator> {
        let users = Self::load_users(&db).await;
        let usernames = users.users.clone().map(|user| user.username);

        let state = Rc::new_in(
            RefCell::new(State {
                window: main.clone_strong(),
                users,
                db: Rc::new_in(db, &PSRAM_ALLOCATOR),
                view: AppState::Login,
                state_login: Default::default(),
                state_users: Default::default(),
                state_user_edit: Default::default(),
                state_user_sites: Default::default(),
            }),
            &PSRAM_ALLOCATOR,
        );

        main.on_enter_view({
            let state = state.clone();
            move |view| {
                state.borrow_mut().set_view(view, true, true);
            }
        });

        main.on_escape({
            let state = state.clone();
            move || {
                State::process_callback_message(&state, CallbackMessage::Escape);
            }
        });

        main.set_login_usernames(ModelRc::new(usernames));

        main.on_login_pw_accepted({
            let state = state.clone();
            move |user_index, username, password| {
                State::process_callback_message(
                    &state,
                    CallbackMessage::Login(CallbackMessageLogin::PwAccepted {
                        user_index,
                        username,
                        password,
                    }),
                );
            }
        });

        main.on_login_test_string_accepted(|string| {
            slint::spawn_local(async move {
                OUTPUT_STRING_CHANNEL.send(string.to_string()).await;
            })
            .unwrap();
        });

        main.on_users_edit_user({
            let state = state.clone();
            move |username, new| {
                State::process_callback_message(
                    &state,
                    CallbackMessage::Users(CallbackMessageUsers::EditUser { username, new }),
                );
            }
        });

        main.on_user_edit_compute_identicon({
            let state = state.clone();
            move |password| {
                State::process_callback_message(
                    &state,
                    CallbackMessage::UserEdit(CallbackMessageUserEdit::ComputeIdenticon {
                        password,
                    }),
                );
            }
        });

        main.on_user_edit_compute_key_id({
            let state = state.clone();
            move |key| {
                State::process_callback_message(
                    &state,
                    CallbackMessage::UserEdit(CallbackMessageUserEdit::ComputeKeyId { key }),
                );
            }
        });

        main.on_user_edit_confirm({
            let state = state.clone();
            move |_encrypted_key| {
                State::process_callback_message(
                    &state,
                    CallbackMessage::UserEdit(CallbackMessageUserEdit::ConfirmRequest),
                );
            }
        });

        main.on_user_sites_site_name_edited({
            let state = state.clone();
            move |query| {
                State::process_callback_message(
                    &state,
                    CallbackMessage::UserSites(CallbackMessageUserSites::SiteNameEdited { query }),
                );
            }
        });

        main.on_user_sites_site_name_accepted({
            let state = state.clone();
            move |site_list_index| {
                State::process_callback_message(
                    &state,
                    CallbackMessage::UserSites(CallbackMessageUserSites::SiteNameAccepted {
                        site_list_index,
                    }),
                );
            }
        });

        // let sites = Rc::new(VecModel::default());
        // sites.push("First".into());
        // sites.push("Second".into());
        // main.set_sites(ModelRc::new(ModelRc::new(sites.clone()).map(
        //     |mut site: StandardListViewItem| {
        //         site.text += "10";
        //         site
        //     },
        // )));

        state
    }

    async fn load_users(db: &AcidDatabase) -> SpectreUsersConfig {
        let read = db.read_transaction().await;
        let mut buffer = vec![0_u8; 128];
        match read
            .read_to_vec(&DbKey::new(DbPathSpectreUsers), &mut buffer)
            .await
        {
            Ok(bytes) => postcard::from_bytes::<SpectreUsersConfig>(bytes).unwrap(),
            Err(ekv::ReadError::KeyNotFound) => Default::default(),
            Err(error) => panic!("Failed to read the users config: {error:?}"),
        }
    }

    async fn save_users(db: &AcidDatabase, users: &SpectreUsersConfig) {
        let mut write = db.write_transaction().await;
        let buffer = postcard::to_allocvec(&users).unwrap();
        write
            .write(&DbKey::new(DbPathSpectreUsers), &buffer)
            .await
            .unwrap();
        write.commit().await.unwrap();
    }

    fn make_site_list(
        sites: &Rc<VecModel<SpectreSite>>,
        query: &SharedString,
    ) -> Rc<VecModel<SiteListEntry>> {
        let site_list = Rc::new(VecModel::default());

        for site in sites.iter() {
            site_list.push(SiteListEntry::Existing(site.site_name));
        }

        if !query.is_empty() {
            site_list.push(SiteListEntry::New(query.clone()))
        }

        site_list
    }

    fn update_site_list(&mut self) {
        if let Some(StateUserSites {
            sites,
            query,
            site_list,
            ..
        }) = self.state_user_sites.as_mut()
        {
            *site_list = State::make_site_list(sites, query);
            self.window
                .set_user_sites_sites(ModelRc::from(Rc::new(site_list.clone().map(|site| {
                    let mut item = StandardListViewItem::default();
                    item.text = site.to_string();
                    item
                }))));
        } else {
            self.window.invoke_user_sites_site_name_clear();
            self.window.set_user_sites_sites(Default::default());
        }
    }

    fn process_callback_message(
        state_rc: &Rc<RefCell<State>, PsramAllocator>,
        mut message: CallbackMessage,
    ) {
        let view = state_rc.borrow().view;
        while let Some(next_message) = match view {
            AppState::Login => StateLogin::process_callback_message(state_rc, message),
            AppState::Users => StateUsers::process_callback_message(state_rc, message),
            AppState::UserEdit => StateUserEdit::process_callback_message(state_rc, message),
            AppState::UserSites => StateUserSites::process_callback_message(state_rc, message),
        } {
            message = next_message
        }
    }

    fn reset_view(&mut self) {
        match self.view {
            AppState::Login => self.state_login = Default::default(),
            AppState::Users => self.state_users = Default::default(),
            AppState::UserEdit => self.state_user_edit = Default::default(),
            AppState::UserSites => self.state_user_sites = Default::default(),
        }
    }

    fn set_view(&mut self, view: AppState, reset_source: bool, reset_target: bool) {
        if reset_source {
            self.reset_view();
        }

        self.view = view;
        self.window.set_app_state(view);

        if reset_target {
            self.reset_view();
        }
    }

    /// Instead of having a `loop` in the non-async `SlintBackend::run_event_loop`, we achieve
    /// async by having only one iteration of the loop run, and `await`ing here.
    /// The following block is analogous to `main.run()`.
    async fn run_event_loop(window: AppWindow) -> ! {
        window.show().unwrap();

        loop {
            slint::run_event_loop().unwrap();
            #[cfg(feature = "limit-fps")]
            embassy_time::Timer::after(FRAME_DURATION_MIN).await;
        }

        #[expect(unreachable_code)]
        window.hide().unwrap();
    }
}

trait AppViewTrait {
    fn process_callback_message(
        _state_rc: &Rc<RefCell<State>, PsramAllocator>,
        _message: CallbackMessage,
    ) -> Option<CallbackMessage> {
        None
    }
}

#[derive(Default)]
struct StateLogin {}

impl AppViewTrait for StateLogin {
    fn process_callback_message(
        state_rc: &Rc<RefCell<State>, PsramAllocator>,
        message: CallbackMessage,
    ) -> Option<CallbackMessage> {
        let mut state = state_rc.borrow_mut();
        match message {
            CallbackMessage::Login(CallbackMessageLogin::PwAccepted {
                user_index,
                username: _,
                password,
            }) => {
                let Some(user) = state.users.users.row_data(user_index as usize) else {
                    error!("Failed to find a user with index {user_index}.");
                    return None;
                };
                let username_c = CString::new(&*user.username)
                    .expect("Username cannot be converted to a C string.");
                let password_c =
                    CString::new(&*password).expect("Password cannot be converted to a C string.");
                let Ok(user_key) =
                    spectre_derive_user_key(&username_c, &password_c, Some(user.encrypted_key))
                else {
                    error!("Password empty.");
                    return None;
                };

                // {
                //     let mut write = db.write_transaction().await;
                //     write
                //         .write(
                //             &DbKey::new(DbPathSpectreUserSite {
                //                 user_sites: DbPathSpectreUserSites {
                //                     username: "test".into(),
                //                 },
                //                 site: "example.org".into(),
                //             }),
                //             &postcard::to_allocvec(&SpectreSiteConfig::default()).unwrap(),
                //         )
                //         .await
                //         .unwrap();
                //     write
                //         .write(
                //             &DbKey::new(DbPathSpectreUserSite {
                //                 user_sites: DbPathSpectreUserSites {
                //                     username: "test".into(),
                //                 },
                //                 site: "sub.example.org".into(),
                //             }),
                //             &postcard::to_allocvec(&SpectreSiteConfig::default()).unwrap(),
                //         )
                //         .await
                //         .unwrap();
                //     write
                //         .write(
                //             &DbKey::new(DbPathSpectreUsers),
                //             &postcard::to_allocvec(&value).unwrap(),
                //         )
                //         .await
                //         .unwrap();
                //     write.commit().await.unwrap();
                // }

                if user.key_id != user_key.keyID.bytes {
                    return Some(CallbackMessage::Login(CallbackMessageLogin::LoginResult {
                        username: user.username,
                        result: LoginResult::Failure,
                    }));
                }

                slint::spawn_local({
                    let state_rc = state_rc.clone();
                    let username = user.username.clone();
                    let db = state.db.clone();

                    async move {
                        let read = db.read_transaction().await;
                        // TODO: https://github.com/embassy-rs/ekv/issues/20
                        let mut buffer = vec![0; 256];
                        // let slice = read
                        //     .read_to_vec(&DbKey::new(DbPathSpectreUsers), &mut buffer)
                        //     .await
                        //     .unwrap();
                        // let read_value = postcard::from_bytes::<SpectreUsersConfig>(&slice).unwrap();

                        let key_sites = DbKey::new(DbPathSpectreUserSites {
                            username: Cow::Borrowed(&username),
                        });
                        let mut key_buffer = [0_u8; ekv::config::MAX_KEY_SIZE];
                        let mut cursor = read
                            .read_range(key_sites.range_of_children())
                            .await
                            .unwrap();
                        let sites = VecModel::default();

                        while let Some((key_len, value_len)) =
                            cursor.next(&mut key_buffer, &mut buffer).await.unwrap()
                        {
                            let key = DbKey::from_raw(key_buffer[..key_len].into());
                            let mut key_segments = key.segments();
                            let value = &buffer[..value_len];
                            let site_config =
                                postcard::from_bytes::<SpectreSiteConfig>(value).unwrap();
                            let site = SpectreSite {
                                config: site_config,
                                username: key_segments.nth(2).unwrap().as_ref().into(),
                                site_name: key_segments.nth(1).unwrap().as_ref().into(),
                            };

                            info!("site = {:#?}", site);
                            sites.push(site);
                        }

                        State::process_callback_message(
                            &state_rc,
                            CallbackMessage::Login(CallbackMessageLogin::LoginResult {
                                username: user.username,
                                result: LoginResult::Success {
                                    user_key,
                                    sites: Rc::new(sites),
                                },
                            }),
                        );
                    }
                })
                .unwrap();
            }
            CallbackMessage::Login(CallbackMessageLogin::LoginResult {
                username,
                result: LoginResult::Success { user_key, sites },
            }) => {
                info!("Correct password entered for user {:?}.", username);
                state.state_user_sites = Some(StateUserSites {
                    username,
                    user_key,
                    query: SharedString::new(),
                    sites: sites.clone(),
                    site_list: Default::default(),
                });
                state.update_site_list();
                state.set_view(AppState::UserSites, true, false);
            }
            CallbackMessage::Login(CallbackMessageLogin::LoginResult {
                username,
                result: LoginResult::Failure,
            }) => {
                warn!("Incorrect password entered for user {:?}.", username);
            }
            _ => (),
        }

        None
    }
}

#[derive(Default)]
struct StateUsers {}

impl AppViewTrait for StateUsers {
    fn process_callback_message(
        state_rc: &Rc<RefCell<State>, PsramAllocator>,
        message: CallbackMessage,
    ) -> Option<CallbackMessage> {
        let mut state = state_rc.borrow_mut();
        match message {
            CallbackMessage::Escape => {
                state.set_view(AppState::Login, true, false);
            }
            CallbackMessage::Users(CallbackMessageUsers::EditUser { username, new }) => {
                state.state_user_edit = StateUserEdit {
                    username: username.clone(),
                    new,
                    password: None,
                    encrypted_key: None,
                };
                state.window.set_user_edit_username(username);
                state.set_view(AppState::UserEdit, true, false);
            }
            _ => (),
        }

        None
    }
}

#[derive(Default)]
struct StateUserEdit {
    username: SharedString,
    new: bool,
    password: Option<SharedString>,
    encrypted_key: Option<(Key, SpectreUserKey)>,
}

impl AppViewTrait for StateUserEdit {
    fn process_callback_message(
        state_rc: &Rc<RefCell<State>, PsramAllocator>,
        message: CallbackMessage,
    ) -> Option<CallbackMessage> {
        let state = state_rc.clone();
        let mut state = state.borrow_mut();
        match message {
            CallbackMessage::Escape => {
                state.set_view(AppState::Users, true, false);
            }
            CallbackMessage::UserEdit(CallbackMessageUserEdit::ComputeIdenticon { password }) => {
                let username_c = CString::new(&*state.state_user_edit.username)
                    .expect("Username cannot be converted to a C string.");
                let password_c =
                    CString::new(&*password).expect("Password cannot be converted to a C string.");
                // let user_key = spectre_derive_user_key(&username_c, &password_c);
                let identicon: SharedString = unsafe {
                    let identicon = spectre_api_sys::spectre_identicon(
                        username_c.as_ptr(),
                        password_c.as_ptr(),
                    );
                    // TODO: identicon.color
                    format!(
                        "{}{}{}{}",
                        CStr::from_ptr(identicon.leftArm).to_str().unwrap(),
                        CStr::from_ptr(identicon.body).to_str().unwrap(),
                        CStr::from_ptr(identicon.rightArm).to_str().unwrap(),
                        CStr::from_ptr(identicon.accessory).to_str().unwrap()
                    )
                    .into()
                };

                warn!("Identicon: {identicon} ({identicon:?})");
                state.window.set_user_edit_identicon(identicon.clone());
                state.state_user_edit.password = Some(password);
            }
            CallbackMessage::UserEdit(CallbackMessageUserEdit::ComputeKeyId {
                key: key_string,
            }) => {
                let Some(password) = state.state_user_edit.password.as_ref() else {
                    warn!("Attempted to compute a key ID when no password has been entered.");
                    return None;
                };

                let mut key: Key = [0; _];
                if let Err(key_decode_error) = hex::decode_to_slice(&*key_string, &mut key) {
                    let message = match key_decode_error {
                        FromHexError::InvalidStringLength | FromHexError::OddLength => {
                            let required_size = key.len() * 2;
                            let provided_size = key_string.len();
                            let delta = provided_size as i32 - required_size as i32;

                            if delta < 0 {
                                slint::format!("Missing {} characters.", -delta)
                            } else if delta > 0 {
                                slint::format!("{} too many characters.", delta)
                            } else {
                                slint::format!("Invalid key length.")
                            }
                        }
                        FromHexError::InvalidHexCharacter { c, index } => {
                            slint::format!("Invalid character {c:?} at position {index}.")
                        }
                    };
                    state.window.set_user_edit_key_error(message);
                    return None;
                }

                let username_c = CString::new(&*state.state_user_edit.username)
                    .expect("Username cannot be converted to a C string.");
                let password_c =
                    CString::new(&**password).expect("Password cannot be converted to a C string.");
                let Ok(user_key) = spectre_derive_user_key(&username_c, &password_c, Some(key))
                else {
                    error!("Password empty.");
                    return None;
                };

                state.window.set_user_edit_key_error(SharedString::new());
                state.window.set_user_edit_key_id(
                    CStr::from_bytes_with_nul(&user_key.keyID.hex)
                        .unwrap()
                        .to_str()
                        .unwrap()
                        .into(),
                );
                state.state_user_edit.encrypted_key = Some((key, user_key));
            }
            CallbackMessage::UserEdit(CallbackMessageUserEdit::ConfirmRequest) => {
                let Some((
                    encrypted_key,
                    SpectreUserKey {
                        keyID: SpectreKeyID { bytes: key_id, .. },
                        ..
                    },
                )) = state.state_user_edit.encrypted_key.take()
                else {
                    warn!("Encrypted key is not set.");
                    return None;
                };

                // If a user with that username already exists, overwrite it.
                let user = SpectreUserConfig {
                    username: state.state_user_edit.username.clone(),
                    encrypted_key,
                    key_id,
                };

                let mut existing_index = None;

                for index in 0..state.users.users.row_count() {
                    if let Some(current_user) = state.users.users.row_data(index)
                        && current_user.username == user.username
                    {
                        existing_index = Some(index);
                    }
                }

                if let Some(existing_index) = existing_index {
                    state.users.users.set_row_data(existing_index, user);
                } else {
                    state.users.users.push(user);
                }

                slint::spawn_local({
                    let state_rc = state_rc.clone();
                    let db = state.db.clone();
                    let users = state.users.clone();

                    async move {
                        State::save_users(&db, &users).await;
                        State::process_callback_message(
                            &state_rc,
                            CallbackMessage::UserEdit(CallbackMessageUserEdit::ConfirmProcessed),
                        );
                    }
                })
                .unwrap();
            }
            CallbackMessage::UserEdit(CallbackMessageUserEdit::ConfirmProcessed) => {
                state.state_user_edit = Default::default();
                state.set_view(AppState::Users, true, true);
            }
            _ => (),
        }

        None
    }
}

struct StateUserSites {
    username: SharedString,
    user_key: SpectreUserKey,
    query: SharedString,
    sites: Rc<VecModel<SpectreSite>>,
    site_list: Rc<VecModel<SiteListEntry>>,
}

impl AppViewTrait for StateUserSites {
    fn process_callback_message(
        state_rc: &Rc<RefCell<State>, PsramAllocator>,
        message: CallbackMessage,
    ) -> Option<CallbackMessage> {
        let state = state_rc.clone();
        let mut state = state.borrow_mut();
        match message {
            CallbackMessage::Escape => {
                state.set_view(AppState::Login, true, false);
            }
            CallbackMessage::UserSites(CallbackMessageUserSites::SiteNameEdited { query }) => {
                if let Some(user_sites) = state.state_user_sites.as_mut() {
                    user_sites.query = query;
                    state.update_site_list();
                }
            }
            CallbackMessage::UserSites(CallbackMessageUserSites::SiteNameAccepted {
                site_list_index,
            }) => {
                let Some(user_sites) = state.state_user_sites.as_mut() else {
                    error!("User sites uninitialized.");
                    return None;
                };
                let Some(site_list_entry) = user_sites.site_list.row_data(site_list_index as usize)
                else {
                    error!("Invalid site list entry index: {site_list_index}");
                    return None;
                };
                warn!("Site name accepted: {site_list_entry:?}");
                let site_name = match site_list_entry {
                    SiteListEntry::New(site_name) => site_name,
                    SiteListEntry::Existing(site_name) => site_name,
                };
                let site_name_c = CString::new(&*site_name).unwrap();
                let site_password =
                    spectre_derive_site_password(&user_sites.user_key, &site_name_c);

                warn!("Site password: {site_password:?}");

                slint::spawn_local({
                    let username = user_sites.username.clone();
                    let db = state.db.clone();
                    async move {
                        // Send password to the host.
                        OUTPUT_STRING_CHANNEL.send(site_password).await;

                        // Update the stored site.
                        let mut write = db.write_transaction().await;
                        let key = DbKey::new(DbPathSpectreUserSite {
                            user_sites: DbPathSpectreUserSites {
                                username: Cow::Borrowed(&username),
                            },
                            site: Cow::Borrowed(&site_name),
                        });
                        let site = SpectreSiteConfig::default();
                        let site_bytes =
                            postcard::to_extend(&site, Vec::<u8, _>::new_in(&PSRAM_ALLOCATOR))
                                .unwrap();

                        write.write(&key, &site_bytes).await.unwrap();
                        write.commit().await.unwrap();
                    }
                })
                .unwrap();
            }
            _ => (),
        }

        None
    }
}

pub struct SitesModel<M>(Pin<Box<ModelChangeListenerContainer<SitesModelInner<M>>>>)
where
    M: Model + 'static;

struct SitesModelInner<M>
where
    M: Model + 'static,
{
    wrapped_model: M,
    notify: ModelNotify,
}

impl<M> ModelChangeListener for SitesModelInner<M>
where
    M: Model + 'static,
{
    fn row_changed(self: Pin<&Self>, row: usize) {
        self.notify
            .row_changed(self.wrapped_model.row_count() - 1 - row);
    }

    fn row_added(self: Pin<&Self>, index: usize, count: usize) {
        let row_count = self.wrapped_model.row_count();
        let old_row_count = row_count - count;
        let index = old_row_count - index;
        self.notify.row_added(index, count);
    }

    fn row_removed(self: Pin<&Self>, index: usize, count: usize) {
        let row_count = self.wrapped_model.row_count();
        self.notify.row_removed(row_count - index, count);
    }

    fn reset(self: Pin<&Self>) {
        self.notify.reset()
    }
}

impl<M> SitesModel<M>
where
    M: Model + 'static,
{
    pub fn new(wrapped_model: M) -> Self {
        let inner = SitesModelInner {
            wrapped_model,
            notify: Default::default(),
        };
        let container = Box::pin(ModelChangeListenerContainer::new(inner));
        container
            .wrapped_model
            .model_tracker()
            .attach_peer(container.as_ref().model_peer());
        Self(container)
    }

    /// Returns a reference to the inner model
    pub fn source_model(&self) -> &M {
        &self.0.as_ref().get().get_ref().wrapped_model
    }
}

impl<M> Model for SitesModel<M>
where
    M: Model + 'static,
{
    type Data = M::Data;

    fn row_count(&self) -> usize {
        self.0.wrapped_model.row_count()
    }

    fn row_data(&self, row: usize) -> Option<Self::Data> {
        let count = self.0.wrapped_model.row_count();
        self.0.wrapped_model.row_data(count.checked_sub(row + 1)?)
    }
    fn set_row_data(&self, row: usize, data: Self::Data) {
        let count = self.0.as_ref().wrapped_model.row_count();
        self.0.wrapped_model.set_row_data(count - row - 1, data);
    }

    fn model_tracker(&self) -> &dyn ModelTracker {
        &self.0.notify
    }

    fn as_any(&self) -> &dyn core::any::Any {
        self
    }
}

#[derive(Clone, Debug)]
pub enum SiteListEntry {
    New(SharedString),
    Existing(SharedString),
}

impl SiteListEntry {
    pub fn to_string(&self) -> SharedString {
        match self {
            SiteListEntry::New(site) => slint::format!("{site} <Add new site>"),
            SiteListEntry::Existing(site) => site.clone(),
        }
    }
}
