// browser-auth.el -- El-compiled auth flow using Supabase
//
// Compile: elc --target=js --bundle examples/browser-auth.el > auth.js
// (requires el_runtime.js in the same directory as browser-auth.el)
//
// Demonstrates:
//   - extern fn for declaring Supabase client constructor
//   - anonymous function literals for callbacks
//   - method call syntax on Any-typed values (client.auth.signInWithOtp)
//   - try/catch for error handling
//   - @async functions with DOM interaction
//   - DOM bridge: dom_get_element, dom_get_value, dom_set_text, dom_add_class
//     dom_remove_class, dom_show, dom_hide, dom_is_null
//   - window_set to expose El functions to the browser global scope
//   - local_storage_set/get for session hints
//   - set_timeout for transient UI state
//   - state_set/get for component state
//
// Expected HTML elements:
//   #acct-email-input   -- email text input
//   #send-link-btn      -- submit button
//   #auth-message       -- status message container
//   #auth-form          -- the form to hide after success
//
// The Supabase JS SDK is loaded from CDN via a <script> tag before auth.js.
// supabase_create_client is declared extern: the runtime provides it via
// the global supabase.createClient function exposed by the CDN bundle.

// ── External declarations ─────────────────────────────────────────────────
//
// These functions are provided by the JS environment (CDN script tags).
// No body is emitted -- the compiler just records the names.

extern fn supabase_create_client(url: String, key: String) -> Any

// ── UI helpers ─────────────────────────────────────────────────────────────

fn show_message(text: String, is_error: Bool) -> Void {
    let msg_el = dom_get_element("auth-message")
    if !dom_is_null(msg_el) {
        dom_set_text(msg_el, text)
        dom_remove_class(msg_el, "hidden")
        if is_error {
            dom_add_class(msg_el, "error")
            dom_remove_class(msg_el, "success")
        } else {
            dom_add_class(msg_el, "success")
            dom_remove_class(msg_el, "error")
        }
    }
}

fn set_button_loading(loading: Bool) -> Void {
    let btn = dom_get_element("send-link-btn")
    if !dom_is_null(btn) {
        if loading {
            dom_set_text(btn, "Sending...")
            dom_set_attr(btn, "disabled", "true")
        } else {
            dom_set_text(btn, "Send Magic Link")
            dom_remove_attr(btn, "disabled")
        }
    }
}

fn clear_message() -> Void {
    let msg_el = dom_get_element("auth-message")
    if !dom_is_null(msg_el) {
        dom_add_class(msg_el, "hidden")
        dom_set_text(msg_el, "")
    }
}

// ── Email validation ───────────────────────────────────────────────────────

fn is_valid_email(email: String) -> Bool {
    let trimmed: String = str_trim(email)
    if str_len(trimmed) < 5 { return false }
    let at_pos: Int = str_index_of(trimmed, "@")
    if at_pos < 1 { return false }
    let dot_pos: Int = str_index_of(trimmed, ".")
    if dot_pos < at_pos + 2 { return false }
    return true
}

// ── Supabase client construction ──────────────────────────────────────────
//
// Build a Supabase client from config injected into the page as NEURON_CFG.
// The extern fn supabase_create_client maps to supabase.createClient on
// the global object exposed by the CDN bundle.

fn get_supabase_client() -> Any {
    let cfg = window_get("NEURON_CFG")
    if dom_is_null(cfg) {
        return null
    }
    let url: String = cfg["supabaseUrl"]
    let key: String = cfg["supabaseAnonKey"]
    supabase_create_client(url, key)
}

// ── Auth flow ──────────────────────────────────────────────────────────────

@async
fn send_magic_link() -> Void {
    let email_el = dom_get_element("acct-email-input")
    if dom_is_null(email_el) {
        show_message("Could not find email input", true)
        return null
    }

    let email: String = str_trim(dom_get_value(email_el))

    if !is_valid_email(email) {
        show_message("Please enter a valid email address", true)
        return null
    }

    clear_message()
    set_button_loading(true)
    state_set("auth_email", email)

    // Build the Supabase client and call auth.signInWithOtp directly.
    // Method call syntax on Any-typed values: client.auth.signInWithOtp(opts)
    // No native_js_call required.
    let client = get_supabase_client()
    if dom_is_null(client) {
        show_message("Auth service not configured", true)
        set_button_loading(false)
        return null
    }

    try {
        let opts: Map<String, Any> = { "email": email }
        // client is Any-typed; .auth returns the auth sub-client (also Any).
        // .signInWithOtp(opts) returns a Promise. @async + await handles it.
        let resp = client.auth.signInWithOtp(opts)
        let err = resp["error"]
        if !dom_is_null(err) {
            let msg: String = err["message"]
            show_message("Error: " + msg, true)
        } else {
            local_storage_set("auth_pending_email", email)
            show_message("Magic link sent! Check your inbox for " + email, false)
            let form = dom_get_element("auth-form")
            if !dom_is_null(form) {
                dom_hide(form)
            }
        }
    } catch (err: Any) {
        show_message("Unexpected error. Please try again.", true)
    }

    set_button_loading(false)
}

// ── Keyboard support ───────────────────────────────────────────────────────

fn handle_email_keydown(event: Any) -> Void {
    let key: String = dom_get_prop(event, "key")
    if str_eq(key, "Enter") {
        send_magic_link()
    }
}

// ── Initialization ─────────────────────────────────────────────────────────

fn init_auth() -> Void {
    let email_el = dom_get_element("acct-email-input")
    if !dom_is_null(email_el) {
        // Pre-fill from local storage if a pending send was interrupted.
        let pending: String = local_storage_get("auth_pending_email")
        if !str_eq(pending, "") {
            dom_set_value(email_el, pending)
        }
        // Anonymous function literal for inline event handler.
        dom_listen(email_el, "keydown", fn(event: Any) -> Void {
            let key: String = dom_get_prop(event, "key")
            if str_eq(key, "Enter") {
                send_magic_link()
            }
        })
    }
    let btn = dom_get_element("send-link-btn")
    if !dom_is_null(btn) {
        dom_listen(btn, "click", fn(event: Any) -> Void {
            send_magic_link()
        })
    }
    state_set("auth_initialized", "true")
}

fn main() -> Void {
    // Expose send_magic_link globally so inline event handlers can call it.
    window_set("sendMagicLink", send_magic_link)
    window_set("initAuth", init_auth)

    // Run init when DOM is ready.
    window_on_load(init_auth)
}
