use {
    crate::{
        client::{Client, ClientError},
        ifs::wl_surface::{
            AttachedSubsurfaceState, CommitAction, PendingState, StackElement, SurfaceExt,
            SurfaceRole, WlSurface, WlSurfaceError, WlSurfaceId, tray::TrayItemId,
        },
        leaks::Tracker,
        object::{Object, Version},
        tree::{Node, NodeLayerLink},
        utils::{
            clonecell::CloneCell,
            linkedlist::{LinkedNode, NodeRef},
            numcell::NumCell,
        },
        wire::{WlSubsurfaceId, wl_subsurface::*},
    },
    std::{
        cell::{Cell, RefCell, RefMut},
        collections::hash_map::OccupiedEntry,
        mem,
        rc::Rc,
    },
    thiserror::Error,
};

#[expect(dead_code)]
const BAD_SURFACE: u32 = 0;

const MAX_SUBSURFACE_DEPTH: u32 = 100;

linear_ids!(SubsurfaceIds, SubsurfaceId, u64);

pub struct WlSubsurface {
    id: WlSubsurfaceId,
    unique_id: SubsurfaceId,
    pub surface: Rc<WlSurface>,
    pub(super) parent: Rc<WlSurface>,
    pub position: Cell<(i32, i32)>,
    sync_requested: Cell<bool>,
    sync_ancestor: Cell<bool>,
    node: RefCell<Option<LinkedNode<StackElement>>>,
    latest_node: CloneCell<Option<NodeRef<StackElement>>>,
    depth: NumCell<u32>,
    pub tracker: Tracker<Self>,
    had_buffer: Cell<bool>,
    version: Version,
    initial_commit: Cell<bool>,
}

#[derive(Default)]
pub struct PendingSubsurfaceData {
    pub(super) state: Option<Box<PendingState>>,
    node: Option<LinkedNode<StackElement>>,
    position: Option<(i32, i32)>,
}

impl PendingSubsurfaceData {
    pub fn merge(&mut self, next: &mut Self, client: &Rc<Client>) {
        if let Some(mut new) = next.state.take() {
            match &mut self.state {
                Some(old) => old.merge(&mut new, client),
                _ => self.state = Some(new),
            }
        }

        macro_rules! opt {
            ($name:ident) => {
                if let Some(n) = next.$name.take() {
                    self.$name = Some(n);
                }
            };
        }
        opt!(node);
        opt!(position);
    }
}

fn update_children_attach(surface: &WlSubsurface) -> Result<(), WlSubsurfaceError> {
    if surface.depth.get() > MAX_SUBSURFACE_DEPTH {
        return Err(WlSubsurfaceError::MaxDepthExceeded);
    }
    let children = surface.surface.children.borrow();
    if let Some(children) = &*children {
        for child in children.subsurfaces.values() {
            child.sync_ancestor.set(surface.sync());
            child.depth.set(surface.depth.get() + 1);
            update_children_attach(child)?;
        }
    }
    Ok(())
}

impl WlSubsurface {
    pub fn new(
        id: WlSubsurfaceId,
        surface: &Rc<WlSurface>,
        parent: &Rc<WlSurface>,
        version: Version,
    ) -> Self {
        Self {
            id,
            unique_id: surface.client.state.subsurface_ids.next(),
            surface: surface.clone(),
            parent: parent.clone(),
            position: Cell::new(Default::default()),
            sync_requested: Cell::new(true),
            sync_ancestor: Cell::new(false),
            node: RefCell::new(None),
            latest_node: Default::default(),
            depth: NumCell::new(1),
            tracker: Default::default(),
            had_buffer: Cell::new(false),
            version,
            initial_commit: Cell::new(true),
        }
    }

    fn pending<'a>(self: &'a Rc<Self>) -> RefMut<'a, PendingSubsurfaceData> {
        RefMut::map(self.parent.pending.borrow_mut(), |m| {
            &mut m
                .subsurfaces
                .entry(self.unique_id)
                .or_insert_with(|| AttachedSubsurfaceState {
                    subsurface: self.clone(),
                    pending: Default::default(),
                })
                .pending
        })
    }

    pub fn apply_state(&self, pending: &mut PendingSubsurfaceData) -> Result<(), WlSurfaceError> {
        if self.initial_commit.take() {
            self.update_has_buffer();
        }
        if let Some(state) = &mut pending.state.take() {
            self.surface.apply_state(state)?;
        }
        let mut stacking_changed = false;
        let mut position_changed = false;
        if let Some(v) = pending.node.take() {
            v.pending.set(false);
            self.node.borrow_mut().replace(v);
            stacking_changed = true;
        }
        if let Some((mut x, mut y)) = pending.position.take() {
            if self.surface.toplevel.is_some() {
                self.damage();
            }
            client_wire_scale_to_logical!(self.surface.client, x, y);
            self.position.set((x, y));
            let (parent_x, parent_y) = self.parent.buffer_abs_pos.get().position();
            self.surface
                .set_absolute_position(parent_x + x, parent_y + y);
            self.parent.need_extents_update.set(true);
            position_changed = true;
        }
        if (position_changed && self.surface.toplevel.is_some())
            || (!position_changed && stacking_changed)
        {
            self.damage();
        }
        Ok(())
    }

    pub fn install(self: &Rc<Self>) -> Result<(), WlSubsurfaceError> {
        if self.surface.id == self.parent.id {
            return Err(WlSubsurfaceError::OwnParent(self.surface.id));
        }
        self.surface.set_role(SurfaceRole::Subsurface)?;
        if self.surface.ext.get().is_some() {
            return Err(WlSubsurfaceError::AlreadyAttached(self.surface.id));
        }
        if self.surface.id == self.parent.get_root().id {
            return Err(WlSubsurfaceError::Ancestor(self.surface.id, self.parent.id));
        }
        if let Some(ss) = self.parent.ext.get().into_subsurface() {
            self.sync_ancestor.set(ss.sync());
            self.depth.set(ss.depth.get() + 1);
        }
        let node = {
            let mut data = self.parent.children.borrow_mut();
            let data = data.get_or_insert_with(Default::default);
            data.subsurfaces.insert(self.surface.id, self.clone());
            data.above.add_last(StackElement {
                pending: Cell::new(true),
                sub_surface: self.clone(),
            })
        };
        self.latest_node.set(Some(node.to_ref()));
        self.pending().node = Some(node);
        self.surface.set_toplevel(self.parent.toplevel.get());
        self.surface.ext.set(self.clone());
        update_children_attach(self)?;
        let (x, y) = self.parent.buffer_abs_pos.get().position();
        self.surface.set_absolute_position(x, y);
        self.surface
            .set_output(&self.parent.output.get(), self.parent.location.get());
        Ok(())
    }

    fn place(self: &Rc<Self>, sibling: WlSurfaceId, above: bool) -> Result<(), WlSubsurfaceError> {
        if sibling == self.surface.id {
            return Err(WlSubsurfaceError::AboveSelf(sibling));
        }
        let pdata = self.parent.children.borrow();
        if let Some(pdata) = &*pdata {
            let element = StackElement {
                pending: Cell::new(true),
                sub_surface: self.clone(),
            };
            let node = if sibling == self.parent.id {
                match above {
                    true => pdata.above.add_first(element),
                    _ => pdata.below.add_last(element),
                }
            } else {
                let sibling = match pdata.subsurfaces.get(&sibling) {
                    Some(s) => s,
                    _ => return Err(WlSubsurfaceError::NotASibling(sibling, self.surface.id)),
                };
                let sibling_node = match sibling.latest_node.get() {
                    Some(n) => n,
                    _ => return Ok(()),
                };
                match above {
                    true => sibling_node.append(element),
                    _ => sibling_node.prepend(element),
                }
            };
            self.latest_node.set(Some(node.to_ref()));
            self.pending().node.replace(node);
        }
        Ok(())
    }

    pub fn sync(&self) -> bool {
        self.sync_requested.get() || self.sync_ancestor.get()
    }

    fn update_sync(&self, sync: bool) -> Result<(), WlSurfaceError> {
        let was_sync = self.sync();
        self.sync_requested.set(sync);
        let is_sync = self.sync();
        if was_sync != is_sync {
            self.handle_sync_change(is_sync)?;
        }
        Ok(())
    }

    fn handle_sync_change(&self, is_sync: bool) -> Result<(), WlSurfaceError> {
        if !is_sync {
            self.on_desync()?;
        }
        let children = self.surface.children.borrow();
        if let Some(children) = &*children {
            for child in children.subsurfaces.values() {
                let was_sync = child.sync();
                child.sync_ancestor.set(is_sync);
                let is_sync = child.sync();
                if was_sync != is_sync {
                    child.handle_sync_change(is_sync)?;
                }
            }
        }
        Ok(())
    }

    fn on_desync(&self) -> Result<(), WlSurfaceError> {
        let committed = &mut *self.parent.pending.borrow_mut();
        let committed = committed.subsurfaces.get_mut(&self.unique_id);
        if let Some(ps) = committed
            && let Some(mut state) = ps.pending.state.take()
        {
            self.surface
                .commit_timeline
                .commit(&self.surface, &mut state)?;
        }
        Ok(())
    }

    fn damage(&self) {
        if !self.surface.visible.get() {
            return;
        }
        let (x, y) = self.surface.buffer_abs_pos.get().position();
        let mut rect = self.surface.extents.get().move_(x, y);
        if let Some(tl) = self.surface.toplevel.get() {
            rect = rect.intersect(tl.node_absolute_position());
        }
        self.surface.client.state.damage(rect);
    }

    fn update_has_buffer(&self) {
        let has_buffer = self.surface.buffer.is_some();
        if self.had_buffer.replace(has_buffer) != has_buffer {
            if has_buffer {
                if self.parent.visible.get() {
                    self.surface.set_visible(true);
                    self.damage();
                }
            } else {
                if self.surface.toplevel.is_some() {
                    self.damage();
                }
                self.surface.destroy_node();
            }
        }
    }
}

impl WlSubsurfaceRequestHandler for WlSubsurface {
    type Error = WlSubsurfaceError;

    fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
        self.surface.unset_ext();
        self.parent.consume_pending_child(self.unique_id, |oe| {
            let oe = oe.remove();
            if let Some(mut state) = oe.pending.state {
                self.surface
                    .commit_timeline
                    .commit(&self.surface, &mut state)?;
            }
            Ok(())
        })?;
        *self.node.borrow_mut() = None;
        self.latest_node.take();
        {
            let mut children = self.parent.children.borrow_mut();
            if let Some(children) = &mut *children {
                children.subsurfaces.remove(&self.surface.id);
            }
        }
        if !self.surface.extents.get().is_empty() {
            let mut parent_opt = Some(self.parent.clone());
            while let Some(parent) = parent_opt.take() {
                if !parent.need_extents_update.get() {
                    break;
                }
                parent.calculate_extents(true);
                parent_opt = parent.ext.get().subsurface_parent();
            }
        }
        self.surface.client.remove_obj(self)?;
        if self.surface.toplevel.is_some() {
            self.damage();
        }
        self.surface.destroy_node();
        Ok(())
    }

    fn set_position(&self, req: SetPosition, slf: &Rc<Self>) -> Result<(), Self::Error> {
        slf.pending().position = Some((req.x, req.y));
        Ok(())
    }

    fn place_above(&self, req: PlaceAbove, slf: &Rc<Self>) -> Result<(), Self::Error> {
        slf.place(req.sibling, true)?;
        Ok(())
    }

    fn place_below(&self, req: PlaceBelow, slf: &Rc<Self>) -> Result<(), Self::Error> {
        slf.place(req.sibling, false)?;
        Ok(())
    }

    fn set_sync(&self, _req: SetSync, _slf: &Rc<Self>) -> Result<(), Self::Error> {
        self.update_sync(true)?;
        Ok(())
    }

    fn set_desync(&self, _req: SetDesync, _slf: &Rc<Self>) -> Result<(), Self::Error> {
        self.update_sync(false)?;
        Ok(())
    }
}

object_base! {
    self = WlSubsurface;
    version = self.version;
}

impl Object for WlSubsurface {
    fn break_loops(&self) {
        *self.node.borrow_mut() = None;
        self.latest_node.take();
    }
}

simple_add_obj!(WlSubsurface);

impl SurfaceExt for WlSubsurface {
    fn node_layer(&self) -> NodeLayerLink {
        self.parent.node_layer()
    }

    fn commit_requested(self: Rc<Self>, pending: &mut Box<PendingState>) -> CommitAction {
        if self.sync() {
            let mut parent_pending = self.pending();
            match &mut parent_pending.state {
                None => parent_pending.state = Some(mem::take(&mut *pending)),
                Some(state) => state.merge(pending, &self.surface.client),
            }
            return CommitAction::AbortCommit;
        }
        CommitAction::ContinueCommit
    }

    fn after_apply_commit(self: Rc<Self>) {
        self.update_has_buffer();
    }

    fn subsurface_parent(&self) -> Option<Rc<WlSurface>> {
        Some(self.parent.clone())
    }

    fn extents_changed(&self) {
        self.parent.need_extents_update.set(true);
    }

    fn into_subsurface(self: Rc<Self>) -> Option<Rc<WlSubsurface>> {
        Some(self)
    }

    fn focus_node(&self) -> Option<Rc<dyn Node>> {
        self.parent.ext.get().focus_node()
    }

    fn consume_pending_child(
        &self,
        surface: &WlSurface,
        child: SubsurfaceId,
        consume: &mut dyn FnMut(
            OccupiedEntry<SubsurfaceId, AttachedSubsurfaceState>,
        ) -> Result<(), WlSurfaceError>,
    ) -> Result<(), WlSurfaceError> {
        self.parent
            .consume_pending_child(self.unique_id, |mut oe| {
                let oe = oe.get_mut();
                match &mut oe.pending.state {
                    Some(state) => state.consume_child(child, &mut *consume),
                    _ => Ok(()),
                }
            })?;
        surface.pending.borrow_mut().consume_child(child, consume)
    }

    fn tray_item(self: Rc<Self>) -> Option<TrayItemId> {
        self.parent.node_tray_item()
    }
}

#[derive(Debug, Error)]
pub enum WlSubsurfaceError {
    #[error("Surface {0} already has an attached `wl_subsurface`")]
    AlreadyAttached(WlSurfaceId),
    #[error("Surface {0} cannot be made its own parent")]
    OwnParent(WlSurfaceId),
    #[error("Surface {0} cannot be made a subsurface of {1} because it's an ancestor of {1}")]
    Ancestor(WlSurfaceId, WlSurfaceId),
    #[error("Subsurfaces cannot be nested deeper than 100 levels")]
    MaxDepthExceeded,
    #[error(transparent)]
    WlSurfaceError(Box<WlSurfaceError>),
    #[error(transparent)]
    ClientError(Box<ClientError>),
    #[error("Cannot place {0} above/below itself")]
    AboveSelf(WlSurfaceId),
    #[error("{0} is not a sibling of {1}")]
    NotASibling(WlSurfaceId, WlSurfaceId),
}
efrom!(WlSubsurfaceError, WlSurfaceError);
efrom!(WlSubsurfaceError, ClientError);
