use std::collections::HashMap;

use super::lru::Entry;

#[derive(Debug, Default)]
pub(super) struct LruList {
    pub(super) most_recent: Option<String>,
    pub(super) least_recent: Option<String>,
}

impl LruList {
    pub(super) fn insert_most_recent(
        &mut self,
        entries: &mut HashMap<String, Entry>,
        key: String,
        mut entry: Entry,
    ) {
        let old_head = self.most_recent.clone();
        entry.previous = None;
        entry.next = old_head.clone();
        entries.insert(key.clone(), entry);
        if let Some(head) = old_head {
            if let Some(head_entry) = entries.get_mut(&head) {
                head_entry.previous = Some(key.clone());
            }
        } else {
            self.least_recent = Some(key.clone());
        }
        self.most_recent = Some(key);
    }

    pub(super) fn move_to_most_recent(&mut self, entries: &mut HashMap<String, Entry>, key: &str) {
        if self.most_recent.as_deref() == Some(key) || !entries.contains_key(key) {
            return;
        }
        let key = key.to_string();
        self.detach_existing(entries, &key);
        let old_head = self.most_recent.clone();
        if let Some(entry) = entries.get_mut(&key) {
            entry.previous = None;
            entry.next = old_head.clone();
        }
        if let Some(head) = old_head {
            if let Some(head_entry) = entries.get_mut(&head) {
                head_entry.previous = Some(key.clone());
            }
        } else {
            self.least_recent = Some(key.clone());
        }
        self.most_recent = Some(key);
    }

    pub(super) fn remove_entry(
        &mut self,
        entries: &mut HashMap<String, Entry>,
        key: &str,
    ) -> Option<Entry> {
        if !entries.contains_key(key) {
            return None;
        }
        self.detach_existing(entries, key);
        entries.remove(key)
    }

    fn detach_existing(&mut self, entries: &mut HashMap<String, Entry>, key: &str) {
        let Some((previous, next)) = entries
            .get(key)
            .map(|entry| (entry.previous.clone(), entry.next.clone()))
        else {
            return;
        };

        if let Some(previous_key) = &previous {
            if let Some(previous_entry) = entries.get_mut(previous_key) {
                previous_entry.next = next.clone();
            }
        } else {
            self.most_recent = next.clone();
        }

        if let Some(next_key) = &next {
            if let Some(next_entry) = entries.get_mut(next_key) {
                next_entry.previous = previous.clone();
            }
        } else {
            self.least_recent = previous.clone();
        }

        if let Some(entry) = entries.get_mut(key) {
            entry.previous = None;
            entry.next = None;
        }
    }
}
