use tui_pages::{
    BufferState, FocusController, FocusIntent, FocusManager, FocusTarget, FocusWrap,
    NavigationCoordinator, NavigationEvent, NavigationRouter, PaneSplit,
};

#[derive(Debug, Clone, PartialEq, Eq)]
enum View {
    Home,
    Form,
}

#[derive(Debug)]
struct Router {
    current: View,
}

impl NavigationRouter<View> for Router {
    fn sync_to_view(&mut self, view: &View) {
        self.current = view.clone();
    }

    fn current_view(&self) -> View {
        self.current.clone()
    }

    fn focus_targets(&self) -> Vec<FocusTarget> {
        match self.current {
            View::Home => vec![FocusTarget::Button(0), FocusTarget::Button(1)],
            View::Form => vec![FocusTarget::CanvasField(0), FocusTarget::Button(0)],
        }
    }
}

#[test]
fn focus_manager_moves_between_buttons_without_wrapping() {
    let mut focus: FocusManager<(), ()> = FocusManager::new();
    focus.apply_focus_intent(FocusIntent::RegisterPage(vec![
        FocusTarget::Button(0),
        FocusTarget::Button(1),
    ]));

    assert_eq!(focus.current(), Some(FocusTarget::Button(0)));
    focus.apply_focus_intent(FocusIntent::Next);
    assert_eq!(focus.current(), Some(FocusTarget::Button(1)));
    focus.apply_focus_intent(FocusIntent::Next);
    assert_eq!(focus.current(), Some(FocusTarget::Button(1)));
}

#[test]
fn activate_enters_a_registered_section_and_next_steps_out_at_the_edge() {
    let mut focus: FocusManager = FocusManager::new();
    focus.register_page(vec![FocusTarget::Section(0), FocusTarget::Button(0)]);
    focus.set_section_items(vec![(0, 3)]);

    // Focus starts on the section header.
    assert_eq!(focus.current(), Some(FocusTarget::Section(0)));

    // Activate enters it — no item count passed by the caller.
    focus.apply_focus_intent(FocusIntent::Activate);
    assert_eq!(
        focus.current(),
        Some(FocusTarget::SectionItem {
            section: 0,
            item: 0
        })
    );

    // Next moves within the section...
    focus.apply_focus_intent(FocusIntent::Next);
    focus.apply_focus_intent(FocusIntent::Next);
    assert_eq!(
        focus.current(),
        Some(FocusTarget::SectionItem {
            section: 0,
            item: 2
        })
    );

    // ...and at the last item (Clamp) steps out to the next top-level target.
    focus.apply_focus_intent(FocusIntent::Next);
    assert_eq!(focus.current(), Some(FocusTarget::Button(0)));
}

#[test]
fn section_edges_step_out_even_when_page_focus_wraps() {
    let mut focus: FocusManager = FocusManager::new();
    focus.set_focus_wrap(FocusWrap::Wrap);
    focus.register_page(vec![
        FocusTarget::Button(0),
        FocusTarget::Section(1),
        FocusTarget::Button(2),
    ]);
    focus.set_section_items(vec![(1, 2)]);

    focus.apply_focus_intent(FocusIntent::Next); // section header
    focus.apply_focus_intent(FocusIntent::Activate);
    focus.apply_focus_intent(FocusIntent::Next);
    assert_eq!(
        focus.current(),
        Some(FocusTarget::SectionItem {
            section: 1,
            item: 1
        })
    );

    focus.apply_focus_intent(FocusIntent::Next);
    assert_eq!(focus.current(), Some(FocusTarget::Button(2)));

    focus.apply_focus_intent(FocusIntent::Prev); // section header
    focus.apply_focus_intent(FocusIntent::Activate);
    focus.apply_focus_intent(FocusIntent::Prev);
    assert_eq!(focus.current(), Some(FocusTarget::Button(0)));
}

#[test]
fn prev_leaves_a_section_at_its_first_item() {
    let mut focus: FocusManager = FocusManager::new();
    focus.register_page(vec![FocusTarget::Button(0), FocusTarget::Section(1)]);
    focus.set_section_items(vec![(1, 2)]);

    focus.apply_focus_intent(FocusIntent::Next); // onto the section header
    focus.apply_focus_intent(FocusIntent::Activate); // enter it
    assert_eq!(
        focus.current(),
        Some(FocusTarget::SectionItem {
            section: 1,
            item: 0
        })
    );

    // Prev at the first item leaves the section for the target before it.
    focus.apply_focus_intent(FocusIntent::Prev);
    assert_eq!(focus.current(), Some(FocusTarget::Button(0)));
}

#[test]
fn activate_is_a_no_op_off_a_section() {
    let mut focus: FocusManager = FocusManager::new();
    focus.register_page(vec![FocusTarget::Button(0), FocusTarget::Section(1)]);
    focus.set_section_items(vec![(1, 2)]);

    // On a button, Activate does nothing — button activation stays the app's job.
    focus.apply_focus_intent(FocusIntent::Activate);
    assert_eq!(focus.current(), Some(FocusTarget::Button(0)));

    // A count-less section cannot be entered automatically either.
    let mut bare: FocusManager = FocusManager::new();
    bare.register_page(vec![FocusTarget::Section(0)]);
    bare.apply_focus_intent(FocusIntent::Activate);
    assert_eq!(bare.current(), Some(FocusTarget::Section(0)));
}

#[test]
fn entered_section_count_refreshes_without_reregistering_targets() {
    let mut focus: FocusManager = FocusManager::new();
    focus.register_page(vec![FocusTarget::Section(0)]);
    focus.set_section_items(vec![(0, 4)]);
    focus.apply_focus_intent(FocusIntent::Activate);
    focus.apply_focus_intent(FocusIntent::Next);
    focus.apply_focus_intent(FocusIntent::Next);
    assert_eq!(
        focus.current(),
        Some(FocusTarget::SectionItem {
            section: 0,
            item: 2
        })
    );

    focus.set_section_items(vec![(0, 2)]);
    assert_eq!(
        focus.current(),
        Some(FocusTarget::SectionItem {
            section: 0,
            item: 1
        })
    );

    focus.set_section_items(vec![(0, 0)]);
    assert_eq!(focus.current(), Some(FocusTarget::Section(0)));
}

#[test]
fn focus_wrap_is_opt_in() {
    let mut focus: FocusManager = FocusManager::new();
    focus.set_focus_wrap(FocusWrap::Wrap);
    focus.apply_focus_intent(FocusIntent::RegisterPage(vec![
        FocusTarget::Button(0),
        FocusTarget::Button(1),
    ]));

    // Forward off the last element wraps to the first.
    focus.apply_focus_intent(FocusIntent::Next);
    assert_eq!(focus.current(), Some(FocusTarget::Button(1)));
    focus.apply_focus_intent(FocusIntent::Next);
    assert_eq!(focus.current(), Some(FocusTarget::Button(0)));
    // Backward off the first wraps to the last.
    focus.apply_focus_intent(FocusIntent::Prev);
    assert_eq!(focus.current(), Some(FocusTarget::Button(1)));

    // Default is still clamp.
    let mut clamped: FocusManager = FocusManager::new();
    clamped.apply_focus_intent(FocusIntent::RegisterPage(vec![
        FocusTarget::Button(0),
        FocusTarget::Button(1),
    ]));
    clamped.apply_focus_intent(FocusIntent::Next);
    clamped.apply_focus_intent(FocusIntent::Next);
    assert_eq!(clamped.current(), Some(FocusTarget::Button(1)));
}

#[test]
fn overlay_type_is_application_defined() {
    // The crate provides no overlay names; the app supplies its own type as `O`.
    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
    enum Overlay {
        Palette,
        Sidebar,
    }

    let mut focus: FocusManager<Overlay> = FocusManager::new();
    focus.register_page(vec![FocusTarget::Button(0)]);

    focus.apply_focus_intent(FocusIntent::Open(FocusTarget::Overlay(Overlay::Palette)));
    assert_eq!(
        focus.current(),
        Some(FocusTarget::Overlay(Overlay::Palette))
    );
    assert!(focus.is_overlay_open(&FocusTarget::Overlay(Overlay::Palette)));
    assert!(!focus.is_overlay_open(&FocusTarget::Overlay(Overlay::Sidebar)));

    focus.apply_focus_intent(FocusIntent::ClearOverlay);
    assert_eq!(focus.current(), Some(FocusTarget::Button(0)));
}

#[test]
fn pane_cycling_follows_focus_wrap() {
    // Two panes; active pane starts on the second (the freshly split one).
    let mut buffer = BufferState::new(View::Home);
    buffer.split_active_pane(PaneSplit::Vertical);
    assert_eq!(buffer.active_pane_index(), 1);

    // Clamp: stepping forward off the last pane stays put.
    buffer.focus_next_pane(FocusWrap::Clamp);
    assert_eq!(buffer.active_pane_index(), 1);

    // Wrap: stepping forward off the last pane returns to the first.
    buffer.focus_next_pane(FocusWrap::Wrap);
    assert_eq!(buffer.active_pane_index(), 0);
    // ...and backward off the first wraps to the last.
    buffer.focus_previous_pane(FocusWrap::Wrap);
    assert_eq!(buffer.active_pane_index(), 1);
}

#[test]
fn buffer_switching_follows_focus_wrap() {
    let mut router = Router {
        current: View::Home,
    };
    let mut buffer = BufferState::new(View::Home);
    buffer.update_history(View::Form); // history [Home, Form], active on Form (index 1)
    assert_eq!(buffer.active_index, 1);

    // Clamp: NextBuffer off the last buffer is a no-op on the index.
    NavigationCoordinator::navigate(
        NavigationEvent::NextBuffer,
        &mut router,
        &mut buffer,
        View::Home,
        FocusWrap::Clamp,
    );
    assert_eq!(buffer.active_index, 1);

    // Wrap: NextBuffer off the last buffer cycles back to the first.
    NavigationCoordinator::navigate(
        NavigationEvent::NextBuffer,
        &mut router,
        &mut buffer,
        View::Home,
        FocusWrap::Wrap,
    );
    assert_eq!(buffer.active_index, 0);
}

#[test]
fn navigation_returns_focus_registration_intent() {
    let mut router = Router {
        current: View::Home,
    };
    let mut buffer = BufferState::new(View::Home);

    let (result, focus_intent) = NavigationCoordinator::navigate(
        NavigationEvent::NavigateTo(View::Form),
        &mut router,
        &mut buffer,
        View::Home,
        FocusWrap::Clamp,
    );

    assert!(matches!(
        result,
        tui_pages::NavigationResult::Navigated { .. }
    ));
    assert!(matches!(
        focus_intent,
        Some(FocusIntent::RegisterPage(targets)) if targets == vec![
            FocusTarget::CanvasField(0),
            FocusTarget::Button(0),
        ]
    ));
}
