use std::time::{Duration, Instant};

use super::super::button::ButtonFrame;
use super::drag::begin_drag;
use crate::backend::interface::BackendView;
use crate::compositor::interaction::{BloomDragCtx, HitNode, PointerState};
use crate::compositor::root::Halley;

pub(super) fn detach_bloom_drag_into_pointer_drag(
    st: &mut Halley,
    ps: &mut PointerState,
    backend: &impl BackendView,
    bloom_drag: BloomDragCtx,
    effective_sx: f32,
    effective_sy: f32,
) {
    let (bloom_w, bloom_h, bloom_local_sx, bloom_local_sy) =
        st.local_screen_in_monitor(bloom_drag.monitor.as_str(), effective_sx, effective_sy);
    let previous_monitor = st.begin_temporary_render_monitor(bloom_drag.monitor.as_str());
    let pointer_world =
        crate::spatial::screen_to_world(st, bloom_w, bloom_h, bloom_local_sx, bloom_local_sy);
    st.end_temporary_render_monitor(previous_monitor);
    let now = Instant::now();
    if st.detach_member_from_cluster(
        bloom_drag.cluster_id,
        bloom_drag.member_id,
        pointer_world,
        now,
    ) {
        st.assign_node_to_monitor(bloom_drag.member_id, bloom_drag.monitor.as_str());
        st.set_interaction_focus(Some(bloom_drag.member_id), 30_000, now);
        begin_drag(
            st,
            ps,
            backend,
            HitNode {
                node_id: bloom_drag.member_id,
                move_surface: false,
                is_core: false,
            },
            ButtonFrame {
                ws_w: bloom_w,
                ws_h: bloom_h,
                global_sx: effective_sx,
                global_sy: effective_sy,
                sx: bloom_local_sx,
                sy: bloom_local_sy,
                world_now: pointer_world,
                workspace_active: false,
            },
            pointer_world,
            false,
            false,
        );
    }
}

pub(super) fn handle_bloom_pull_motion(
    st: &mut Halley,
    ps: &mut PointerState,
    backend: &impl BackendView,
    effective_sx: f32,
    effective_sy: f32,
    now: Instant,
) -> bool {
    let Some(bloom_drag) = ps.bloom_drag.clone() else {
        return false;
    };

    let (_, _, bloom_local_sx, bloom_local_sy) =
        st.local_screen_in_monitor(bloom_drag.monitor.as_str(), effective_sx, effective_sy);
    let pointer_screen = halley_core::field::Vec2 {
        x: bloom_local_sx,
        y: bloom_local_sy,
    };
    let slot_screen = halley_core::field::Vec2 {
        x: bloom_drag.slot_screen.0,
        y: bloom_drag.slot_screen.1,
    };
    let raw_offset = halley_core::field::Vec2 {
        x: pointer_screen.x - slot_screen.x,
        y: pointer_screen.y - slot_screen.y,
    };
    let pull_dist = raw_offset.x.hypot(raw_offset.y);
    let slop_px = crate::compositor::interaction::state::bloom_pull_slop_px();
    let display_offset =
        crate::compositor::interaction::state::bloom_pull_constrained_offset(raw_offset);
    let now_ms = st.now_ms(now);
    let mut should_detach = false;
    if let Some(preview) = st.input.interaction_state.bloom_pull_preview.as_mut() {
        let outward_axis = halley_core::field::Vec2 {
            x: preview.slot_screen.x - preview.core_screen.x,
            y: preview.slot_screen.y - preview.core_screen.y,
        };
        let outward_len = outward_axis.x.hypot(outward_axis.y);
        let outward_pull = if outward_len > 0.001 {
            (raw_offset.x * (outward_axis.x / outward_len)
                + raw_offset.y * (outward_axis.y / outward_len))
                .max(0.0)
        } else {
            pull_dist
        };
        preview.pointer_screen = pointer_screen;
        preview.display_offset = display_offset;
        match preview.phase.clone() {
            crate::compositor::interaction::state::BloomPullPhase::Pressed => {
                preview.hold_progress = 0.0;
                if outward_pull >= slop_px {
                    preview.phase =
                        crate::compositor::interaction::state::BloomPullPhase::Tethered {
                            started_at_ms: now_ms,
                        };
                }
            }
            crate::compositor::interaction::state::BloomPullPhase::Tethered { started_at_ms } => {
                if outward_pull < slop_px * 0.75 {
                    preview.phase = crate::compositor::interaction::state::BloomPullPhase::Pressed;
                    preview.hold_progress = 0.0;
                } else {
                    preview.hold_progress = (now_ms.saturating_sub(started_at_ms) as f32
                        / crate::compositor::interaction::state::bloom_detach_hold_ms().max(1)
                            as f32)
                        .clamp(0.0, 1.0);
                    should_detach = preview.hold_progress >= 1.0;
                }
            }
            crate::compositor::interaction::state::BloomPullPhase::Snapback { .. } => {
                preview.phase = crate::compositor::interaction::state::BloomPullPhase::Pressed;
                preview.hold_progress = 0.0;
            }
        }
    }
    st.input.interaction_state.overlay_hover_target = None;
    ps.hover_node = None;
    ps.hover_started_at = None;
    if should_detach {
        ps.bloom_drag = None;
        st.input.interaction_state.bloom_pull_preview = None;
        ps.preview_block_until = Some(now + Duration::from_millis(500));
        detach_bloom_drag_into_pointer_drag(
            st,
            ps,
            backend,
            bloom_drag,
            effective_sx,
            effective_sy,
        );
        backend.request_redraw();
    } else {
        st.request_maintenance();
    }
    true
}
