use ruin_app::prelude::*;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{EnvFilter, fmt};

fn install_tracing() {
    let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warn"));
    let fmt_layer = fmt::layer()
        .with_target(true)
        .with_thread_ids(true)
        .compact();
    let _ = tracing_subscriber::registry()
        .with(filter)
        .with(fmt_layer)
        .try_init();
}

#[ruin_runtime::async_main]
async fn main() -> ruin_app::Result<()> {
    install_tracing();
    App::new()
        .window(
            Window::new()
                .title("RUIN Tasks")
                .app_id("dev.ruin.fine-grained-list")
                .size(1180.0, 780.0),
        )
        .mount(view! {
            TaskBoard() {}
        })
        .run()
        .await
}

#[component]
fn TaskBoard() -> impl IntoView {
    let filter = use_signal(|| TaskFilter::All);
    let tasks = use_signal(seed_tasks);
    let list_scroll = use_signal(|| 0.0_f32);

    let total_count = use_memo({
        let tasks = tasks.clone();
        move || tasks.with(|tasks| tasks.len())
    });
    let open_count = use_memo({
        let tasks = tasks.clone();
        move || {
            tasks.with(|tasks| {
                tasks
                    .iter()
                    .filter(|task| !matches!(task.status, TaskStatus::Done))
                    .count()
            })
        }
    });
    let done_count = use_memo({
        let tasks = tasks.clone();
        move || {
            tasks.with(|tasks| {
                tasks
                    .iter()
                    .filter(|task| matches!(task.status, TaskStatus::Done))
                    .count()
            })
        }
    });
    let visible_ids = use_memo({
        let filter = filter.clone();
        let tasks = tasks.clone();
        move || {
            tasks.with(|tasks| {
                tasks
                    .iter()
                    .filter(|task| filter.get().matches(task.status))
                    .map(|task| task.id)
                    .collect::<Vec<_>>()
            })
        }
    });

    use_window_title({
        let visible_ids = visible_ids.clone();
        let total_count = total_count.clone();
        move || {
            format!(
                "RUIN Tasks ({}/{})",
                visible_ids.with(|ids| ids.len()),
                total_count.get()
            )
        }
    });

    let all_label = filter_label(matches!(filter.get(), TaskFilter::All), "All");
    let open_label = filter_label(matches!(filter.get(), TaskFilter::OpenOnly), "Open");
    let done_label = filter_label(
        matches!(filter.get(), TaskFilter::CompletedOnly),
        "Completed",
    );
    let task_rows = visible_ids.with(|ids| {
        ids.iter()
            .map(|task_id| {
                TaskRow::builder()
                    .tasks(tasks.clone())
                    .task_id(*task_id)
                    .children(())
            })
            .collect::<Vec<_>>()
    });

    view! {
        column(gap = 16.0, padding = 24.0) {
            text(role = TextRole::Heading(1), size = 30.0, weight = FontWeight::Semibold) {
                "Fine-grained list updates"
            }

            text(color = colors::muted(), wrap = TextWrap::Word) {
                "This is an honest slice of aspirational example 03: filtering, row mutation, \
                 reordering, and wrapped-list reflow all work end-to-end. True keyed diffing and \
                 layout-island invalidation are still future work, so this example exercises the \
                 real runtime we have rather than pretending those optimizations already exist."
            }

            row(gap = 8.0) {
                button(on_press = {
                    let filter = filter.clone();
                    move |_| {
                        let _ = filter.set(TaskFilter::All);
                    }
                }) { all_label }

                button(on_press = {
                    let filter = filter.clone();
                    move |_| {
                        let _ = filter.set(TaskFilter::OpenOnly);
                    }
                }) { open_label }

                button(on_press = {
                    let filter = filter.clone();
                    move |_| {
                        let _ = filter.set(TaskFilter::CompletedOnly);
                    }
                }) { done_label }

                button(on_press = {
                    let tasks = tasks.clone();
                    move |_| {
                        tasks.replace(seed_tasks());
                    }
                }) { "Reset board" }
            }

            row(gap = 16.0) {
                block(
                    flex = 1.0,
                    padding = 16.0,
                    gap = 12.0,
                    background = surfaces::raised(),
                    border_radius = 12.0,
                ) {
                    text(size = 18.0, weight = FontWeight::Semibold) { "Tasks" }

                    text(color = colors::muted()) {
                        visible_ids.with(|ids| ids.len());
                        " visible · ";
                        total_count.clone();
                        " total"
                    }

                    scroll_box(
                        offset_y = list_scroll.clone(),
                        height = 520.0,
                        padding = 12.0,
                        background = surfaces::canvas(),
                        border_radius = 10.0,
                        border = (2.0, colors::muted()),
                    ) {
                        column(gap = 10.0) {
                            task_rows
                        }
                    }
                }

                block(
                    width = 300.0,
                    padding = 16.0,
                    gap = 12.0,
                    background = surfaces::raised(),
                    border_radius = 12.0,
                ) {
                    text(size = 18.0, weight = FontWeight::Semibold) { "Board summary" }

                    block(
                        padding = 12.0,
                        gap = 8.0,
                        background = surfaces::canvas(),
                        border_radius = 10.0,
                    ) {
                        text() { "Open tasks: "; open_count.clone() }
                        text() { "Completed tasks: "; done_count.clone() }
                        text() { "Current filter: "; filter.get().label() }
                    }

                    text(color = colors::muted(), wrap = TextWrap::Word) {
                        "Each task row can advance its status, expand notes, and move within the \
                         backing list. The scroll box is intentional here: resizing the window \
                         changes wrapping pressure, which is useful for validating list reflow and \
                         scroll clamping under real content."
                    }
                }
            }
        }
    }
}

#[component]
fn TaskRow(tasks: Signal<Vec<Task>>, task_id: u64) -> impl IntoView {
    let task = tasks.with(|tasks| {
        tasks
            .iter()
            .find(|task| task.id == task_id)
            .cloned()
            .expect("task row should only render live tasks")
    });
    let task_index = tasks.with(|tasks| {
        tasks
            .iter()
            .position(|task| task.id == task_id)
            .expect("task row should know its current index")
    });
    let can_move_up = task_index > 0;
    let can_move_down = tasks.with(|tasks| task_index + 1 < tasks.len());

    let mut detail_views = vec![
        text()
            .weight(if matches!(task.status, TaskStatus::Done) {
                FontWeight::Medium
            } else {
                FontWeight::Semibold
            })
            .children(task.title.clone()),
        text().color(colors::muted()).children((
            "Owner: ",
            task.owner.clone(),
            " · Phase: ",
            task.status.description(),
        )),
    ];
    if task.expanded {
        detail_views.push(
            text()
                .color(colors::muted())
                .wrap(TextWrap::Word)
                .children(task.notes.clone()),
        );
    }

    let mut action_views = vec![
        button()
            .on_press({
                let tasks = tasks.clone();
                move |_| {
                    update_task(&tasks, task_id, |task| {
                        task.status = task.status.advance();
                    });
                }
            })
            .children(task.status.advance_label()),
        button()
            .on_press({
                let tasks = tasks.clone();
                move |_| {
                    update_task(&tasks, task_id, |task| {
                        task.expanded = !task.expanded;
                    });
                }
            })
            .children(if task.expanded {
                "Hide notes"
            } else {
                "Show notes"
            }),
    ];
    if can_move_up {
        action_views.push(
            button()
                .on_press({
                    let tasks = tasks.clone();
                    move |_| move_task(&tasks, task_id, MoveDirection::Up)
                })
                .children("Move up"),
        );
    }
    if can_move_down {
        action_views.push(
            button()
                .on_press({
                    let tasks = tasks.clone();
                    move |_| move_task(&tasks, task_id, MoveDirection::Down)
                })
                .children("Move down"),
        );
    }

    view! {
        block(
            padding = 14.0,
            gap = 12.0,
            background = task.status.background(),
            border_radius = 12.0,
            border = (1.0, task.status.accent()),
        ) {
            row(gap = 12.0) {
                block(
                    width = 120.0,
                    padding = 10.0,
                    background = surfaces::canvas(),
                    border_radius = 10.0,
                    border = (1.0, task.status.accent()),
                ) {
                    text(weight = FontWeight::Semibold, color = task.status.accent()) {
                        task.status.label()
                    }
                }

                column(flex = 1.0, gap = 8.0) {
                    detail_views
                }
            }

            row(gap = 8.0) {
                action_views
            }
        }
    }
}

#[derive(Clone)]
struct Task {
    id: u64,
    title: String,
    owner: String,
    notes: String,
    status: TaskStatus,
    expanded: bool,
}

#[derive(Clone, Copy)]
enum MoveDirection {
    Up,
    Down,
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum TaskStatus {
    Backlog,
    Doing,
    Done,
}

impl TaskStatus {
    const fn label(self) -> &'static str {
        match self {
            TaskStatus::Backlog => "Backlog",
            TaskStatus::Doing => "Doing",
            TaskStatus::Done => "Done",
        }
    }

    const fn description(self) -> &'static str {
        match self {
            TaskStatus::Backlog => "Waiting for a pass",
            TaskStatus::Doing => "Actively being verified",
            TaskStatus::Done => "Ready to ship",
        }
    }

    const fn advance(self) -> Self {
        match self {
            TaskStatus::Backlog => TaskStatus::Doing,
            TaskStatus::Doing => TaskStatus::Done,
            TaskStatus::Done => TaskStatus::Backlog,
        }
    }

    const fn advance_label(self) -> &'static str {
        match self {
            TaskStatus::Backlog => "Start work",
            TaskStatus::Doing => "Complete",
            TaskStatus::Done => "Reopen",
        }
    }

    const fn accent(self) -> Color {
        match self {
            TaskStatus::Backlog => Color::rgb(0xE3, 0xB5, 0x65),
            TaskStatus::Doing => Color::rgb(0x71, 0xA7, 0xF7),
            TaskStatus::Done => Color::rgb(0x69, 0xC3, 0x7D),
        }
    }

    const fn background(self) -> Color {
        match self {
            TaskStatus::Backlog => Color::rgb(0x38, 0x2B, 0x1C),
            TaskStatus::Doing => Color::rgb(0x1F, 0x2E, 0x49),
            TaskStatus::Done => Color::rgb(0x1E, 0x36, 0x28),
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum TaskFilter {
    All,
    OpenOnly,
    CompletedOnly,
}

impl TaskFilter {
    const fn matches(self, status: TaskStatus) -> bool {
        match self {
            TaskFilter::All => true,
            TaskFilter::OpenOnly => !matches!(status, TaskStatus::Done),
            TaskFilter::CompletedOnly => matches!(status, TaskStatus::Done),
        }
    }

    const fn label(self) -> &'static str {
        match self {
            TaskFilter::All => "All",
            TaskFilter::OpenOnly => "Open only",
            TaskFilter::CompletedOnly => "Completed only",
        }
    }
}

fn update_task(tasks: &Signal<Vec<Task>>, task_id: u64, mutate: impl FnOnce(&mut Task)) {
    tasks.update(|tasks| {
        if let Some(task) = tasks.iter_mut().find(|task| task.id == task_id) {
            mutate(task);
        }
    });
}

fn move_task(tasks: &Signal<Vec<Task>>, task_id: u64, direction: MoveDirection) {
    tasks.update(|tasks| {
        let Some(index) = tasks.iter().position(|task| task.id == task_id) else {
            return;
        };
        let target = match direction {
            MoveDirection::Up if index > 0 => index - 1,
            MoveDirection::Down if index + 1 < tasks.len() => index + 1,
            _ => index,
        };
        if target != index {
            tasks.swap(index, target);
        }
    });
}

fn filter_label(active: bool, label: &str) -> String {
    if active {
        format!("[x] {label}")
    } else {
        format!("[ ] {label}")
    }
}

fn seed_tasks() -> Vec<Task> {
    vec![
        Task {
            id: 1,
            title: "Audit nested clip propagation".to_string(),
            owner: "Renderer".to_string(),
            notes: "Confirm that child rounded clips preserve parent clip intersections, including the empty-intersection case that previously let fully offscreen text reappear.".to_string(),
            status: TaskStatus::Done,
            expanded: false,
        },
        Task {
            id: 2,
            title: "Clamp stale scroll offsets on reflow".to_string(),
            owner: "Layout".to_string(),
            notes: "When wrapped content gets shorter after a resize, the scrollbox should clamp the old offset before positioning children so the viewport never opens up an empty gap.".to_string(),
            status: TaskStatus::Done,
            expanded: false,
        },
        Task {
            id: 3,
            title: "Prototype honest example 03 slice".to_string(),
            owner: "ruin_app".to_string(),
            notes: "Keep the example faithful to the current runtime: task filtering, list reordering, and row mutation should work today even though keyed diffing and layout islands are still future work.".to_string(),
            status: TaskStatus::Doing,
            expanded: true,
        },
        Task {
            id: 4,
            title: "Add vector child composition".to_string(),
            owner: "Macros".to_string(),
            notes: "Allow `Vec<View>`-style composition so examples can build honest dynamic lists without inventing fake `for` syntax before the macro grammar is ready.".to_string(),
            status: TaskStatus::Doing,
            expanded: false,
        },
        Task {
            id: 5,
            title: "Stress wrapped task notes".to_string(),
            owner: "Examples".to_string(),
            notes: "Task notes should be long enough to force wrapping and scroll-box growth so resize/reflow behavior is easy to observe while manually testing the example.".to_string(),
            status: TaskStatus::Backlog,
            expanded: false,
        },
        Task {
            id: 6,
            title: "Decide the keyed-list surface".to_string(),
            owner: "Design".to_string(),
            notes: "Later passes still need a real keyed list story, stable nested component identity, and layout invalidation boundaries rather than whole-tree rerenders.".to_string(),
            status: TaskStatus::Backlog,
            expanded: false,
        },
        Task {
            id: 7,
            title: "Polish button variants".to_string(),
            owner: "Design".to_string(),
            notes: "The current button surface is deliberately minimal. Example 03 would read even better once button kinds and denser inline controls exist.".to_string(),
            status: TaskStatus::Backlog,
            expanded: false,
        },
    ]
}
