/* commit_log_view/model.rs
 *
 * Copyright 2026 Christian Kruse
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

use std::collections::HashMap;

use adw::prelude::*;
use relm4::gtk::glib::BoxedAnyObject;
use relm4::prelude::*;

use crate::git::{BranchKind, BranchRefs, CommitInfo, UpstreamRef};

pub(super) struct CommitItem {
    pub(super) commit: CommitInfo,
    pub(super) tags: Vec<String>,
    pub(super) upstream_name: Option<String>,
    pub(super) local_branches: Vec<String>,
    pub(super) remote_branches: Vec<String>,
}

pub(super) fn build_commit_item(
    commit: CommitInfo,
    tag_map: &HashMap<String, Vec<String>>,
    branch_map: &HashMap<String, BranchRefs>,
    upstream: Option<&UpstreamRef>,
    current_ref: Option<&str>,
    current_kind: Option<BranchKind>,
) -> CommitItem {
    let tags = tag_map.get(&commit.oid).cloned().unwrap_or_default();

    let upstream_name = upstream.and_then(|u| {
        if u.oid == commit.oid {
            Some(u.name.clone())
        } else {
            None
        }
    });

    let (mut local_branches, mut remote_branches) = branch_map
        .get(&commit.oid)
        .map(|r| (r.local.clone(), r.remote.clone()))
        .unwrap_or_default();

    if matches!(current_kind, Some(BranchKind::Local))
        && let Some(name) = current_ref
    {
        local_branches.retain(|b| b != name);
    }

    if matches!(current_kind, Some(BranchKind::Remote))
        && let Some(name) = current_ref
    {
        remote_branches.retain(|b| b != name);
    }

    if let Some(u) = upstream {
        remote_branches.retain(|b| b != &u.name);
    }

    CommitItem {
        commit,
        tags,
        upstream_name,
        local_branches,
        remote_branches,
    }
}

#[derive(Clone)]
pub(super) struct CommitListModel {
    store: gtk::gio::ListStore,
    pub(super) selection_model: gtk::MultiSelection,
}

impl CommitListModel {
    pub(super) fn new() -> Self {
        let store = gtk::gio::ListStore::new::<BoxedAnyObject>();
        let selection_model = gtk::MultiSelection::new(Some(store.clone()));
        Self { store, selection_model }
    }

    pub(super) fn len(&self) -> u32 {
        self.store.n_items()
    }

    pub(super) fn is_empty(&self) -> bool {
        self.store.n_items() == 0
    }

    pub(super) fn clear(&self) {
        self.store.remove_all();
    }

    pub(super) fn get(&self, pos: u32) -> Option<BoxedAnyObject> {
        self.store.item(pos).and_downcast::<BoxedAnyObject>()
    }

    pub(super) fn extend_from_iter<I: IntoIterator<Item = CommitItem>>(&self, iter: I) {
        for item in iter {
            self.store.append(&BoxedAnyObject::new(item));
        }
    }

    pub(super) fn iter(&self) -> impl Iterator<Item = BoxedAnyObject> + '_ {
        let n = self.store.n_items();
        (0..n).filter_map(move |i| self.store.item(i).and_downcast::<BoxedAnyObject>())
    }
}
