use ruin_app::prelude::*;

// This is the aspirational author-facing source we eventually want to support:
//
// ```ignore
// #[ruin_runtime::async_main]
// async fn main() -> ruin::Result<()> {
//     App::new()
//         .window(
//             Window::new()
//                 .title("RUIN Counter")
//                 .app_id("dev.ruin.counter")
//                 .size(960.0, 640.0),
//         )
//         .mount(view! { CounterApp(title = "RUIN Counter") {} })
//         .run()
//         .await
// }
//
// #[component]
// fn CounterApp(title: &'static str) -> impl View {
//     let count = use_signal(|| 0);
//     let doubled = use_memo(move || count.get() * 2);
//
//     use_window_title(move || format!("{title} ({})", count.get()));
//
//     view! {
//         column(gap = 16, padding = 24) {
//             text(role = TextRole::Heading(1), size = 32, weight = FontWeight::Semibold) {
//                 title
//             }
//
//             row(gap = 8) {
//                 button(on_press = move |_| count.update(|value| *value -= 1)) { "-1" }
//                 button(on_press = move |_| count.set(0)) { "Reset" }
//                 button(on_press = move |_| count.update(|value| *value += 1)) { "+1" }
//             }
//
//             block(padding = 16, gap = 8, background = surfaces::raised()) {
//                 text(size = 18) { "count = "; count }
//                 text(color = colors::muted()) { "double = "; doubled }
//             }
//         }
//     }
// }
// ```
//
// The hand-written code below is the kind of lower-level shape the proc macro should expand to.
//
// Dev notes:
// - `mount` should accept any mountable root, including component instances and already-built
//   views.
// - The component render function runs as-authored. Hook storage and event dispatch belong to the
//   app runtime, not to ad hoc wrapper structs per component.
// - `view!` should lower component and primitive calls to builder invocations, with `children(...)`
//   as the finalizing step.
// - The macro cannot ask rustc whether an arbitrary token names a primitive or a component. It has
//   to decide syntactically. The workable rule is lowercase/intrinsic names (`row`, `text`,
//   `button`) lower to primitive builders, while `UpperCamelCase` paths lower to component
//   builders.

#[ruin_runtime::async_main]
async fn main() -> ruin_app::Result<()> {
    let title = "RUIN Counter";
    App::new()
        .window(
            Window::new()
                .title(title)
                .app_id("dev.ruin.counter")
                .size(960.0, 640.0),
        )
        .mount(CounterApp::builder().title(title).children(()))
        .run()
        .await
}

struct CounterApp {
    title: &'static str,
}

struct __CounterAppTitleMissing;
struct __CounterAppTitlePresent(&'static str);

struct __CounterAppBuilder<TitleState> {
    title: TitleState,
}

impl __CounterAppBuilder<__CounterAppTitleMissing> {
    fn title(self, title: &'static str) -> __CounterAppBuilder<__CounterAppTitlePresent> {
        __CounterAppBuilder {
            title: __CounterAppTitlePresent(title),
        }
    }
}

impl __CounterAppBuilder<__CounterAppTitlePresent> {
    fn children(self, _children: ()) -> CounterApp {
        CounterApp::from_builder(self)
    }
}

impl CounterApp {
    fn from_builder(builder: __CounterAppBuilder<__CounterAppTitlePresent>) -> Self {
        Self {
            title: builder.title.0,
        }
    }
}

impl Component for CounterApp {
    type Builder = __CounterAppBuilder<__CounterAppTitleMissing>;

    fn builder() -> Self::Builder {
        __CounterAppBuilder {
            title: __CounterAppTitleMissing,
        }
    }

    fn render(&self) -> View {
        __render_CounterApp(self.title)
    }
}

struct CounterActions {
    count: Signal<i32>,
}

struct __CounterActionsCountMissing;
struct __CounterActionsCountPresent(Signal<i32>);

struct __CounterActionsBuilder<CountState> {
    count: CountState,
}

impl __CounterActionsBuilder<__CounterActionsCountMissing> {
    fn count(self, count: Signal<i32>) -> __CounterActionsBuilder<__CounterActionsCountPresent> {
        __CounterActionsBuilder {
            count: __CounterActionsCountPresent(count),
        }
    }
}

impl __CounterActionsBuilder<__CounterActionsCountPresent> {
    fn children(self, _children: ()) -> CounterActions {
        CounterActions::from_builder(self)
    }
}

impl CounterActions {
    fn from_builder(builder: __CounterActionsBuilder<__CounterActionsCountPresent>) -> Self {
        Self {
            count: builder.count.0,
        }
    }
}

impl Component for CounterActions {
    type Builder = __CounterActionsBuilder<__CounterActionsCountMissing>;

    fn builder() -> Self::Builder {
        __CounterActionsBuilder {
            count: __CounterActionsCountMissing,
        }
    }

    fn render(&self) -> View {
        __render_CounterActions(self.count.clone())
    }
}

#[allow(non_snake_case)]
fn __render_CounterApp(title: &'static str) -> View {
    let count = use_signal(|| 0_i32);
    let doubled = use_memo({
        let count = count.clone();
        move || count.get() * 2
    });

    use_window_title({
        let count = count.clone();
        move || format!("{title} ({})", count.get())
    });

    column().gap(16.0).padding(24.0).children((
        text()
            .role(TextRole::Heading(1))
            .size(32.0)
            .weight(FontWeight::Semibold)
            .children(title),
        // `CounterActions(...) {}` in `view!` would lower to a component builder invocation, not a
        // primitive builder invocation. The resulting component instance is then rendered as a
        // child view by the runtime.
        CounterActions::builder().count(count.clone()).children(()),
        block()
            .padding(16.0)
            .gap(8.0)
            .background(surfaces::raised())
            .border_radius(12.0)
            .children((
                text().size(18.0).children(("count = ", count.clone())),
                text()
                    .color(colors::muted())
                    .children(("double = ", doubled.clone())),
            )),
    ))
}

#[allow(non_snake_case)]
fn __render_CounterActions(count: Signal<i32>) -> View {
    row().gap(8.0).children((
        button()
            .on_press({
                let count = count.clone();
                move |_| {
                    count.update(|value| *value -= 1);
                }
            })
            .children("-1"),
        button()
            .on_press({
                let count = count.clone();
                move |_| {
                    let _ = count.set(0);
                }
            })
            .children("Reset"),
        button()
            .on_press({
                let count = count.clone();
                move |_| {
                    count.update(|value| *value += 1);
                }
            })
            .children("+1"),
    ))
}
