-- System Shell for Mosis
-- Provides persistent navigation, notifications, toasts, and dialogs

local shell_document = nil
local app_container = nil

-- Navigation state (persistent in shell)
local nav_history = {}
local current_app = "home"
local current_app_path = nil

-- Dialog callback
local dialog_callback = nil

-- Notification state
local notifications_expanded = false

-- ===== INITIALIZATION =====

function initShell(doc)
    print("[Shell] Initializing system shell...")
    shell_document = doc
    app_container = doc:GetElementById("app-container")

    if not app_container then
        print("[Shell] ERROR: app-container not found!")
        return
    end

    -- Update time display
    updateTime()

    -- Load home screen by default
    loadAppContent_internal("home", "apps/home/home_content.rml")

    print("[Shell] Shell initialized")
end

-- Update status bar time
function updateTime()
    local time_elem = shell_document:GetElementById("shell-time")
    if time_elem then
        time_elem.inner_rml = "12:30"
    end
end

-- ===== APP LOADING =====

-- Internal function to load app content
function loadAppContent_internal(app_id, app_path)
    if not app_container then
        print("[Shell] ERROR: No app container")
        return false
    end

    print("[Shell] Loading app: " .. app_id .. " from " .. app_path)

    -- Show loading overlay
    showLoading(true)

    -- Load app content using C++ function
    if loadAppContent then
        local success = loadAppContent(app_container, app_path)
        showLoading(false)

        if success then
            current_app = app_id
            current_app_path = app_path
            print("[Shell] App loaded: " .. app_id)

            -- If home was loaded, populate apps dynamically
            if app_id == "home" then
                populateHomeApps()
            end

            return true
        else
            print("[Shell] Failed to load app: " .. app_id)
            showToast("Failed to load app", "error")
            return false
        end
    else
        showLoading(false)
        print("[Shell] ERROR: loadAppContent not available, using fallback")

        -- Fallback: try to load via inner_rml if we can read the file
        -- This won't work without C++ support, but shows the intent
        return false
    end
end

-- Navigate to a system app by ID
function shellNavigateTo(app_id)
    local app_paths = {
        home = "apps/home/home_content.rml",
        dialer = "apps/dialer/dialer_content.rml",
        calling = "apps/dialer/calling_content.rml",
        contacts = "apps/contacts/contacts_content.rml",
        contact_detail = "apps/contacts/contact_detail_content.rml",
        messages = "apps/messages/messages_content.rml",
        chat = "apps/messages/chat_content.rml",
        settings = "apps/settings/settings_content.rml",
        browser = "apps/browser/browser_content.rml",
        store = "apps/store/store_content.rml",
        camera = "apps/camera/camera_content.rml",
        music = "apps/music/music_content.rml",
        ["sandbox-test"] = "apps/sandbox-test/main_content.rml"
    }

    local path = app_paths[app_id]
    if path then
        -- Push current app to history
        if current_app and current_app ~= app_id then
            table.insert(nav_history, {
                app_id = current_app,
                app_path = current_app_path
            })
            print("[Shell] Pushed to history: " .. current_app .. " (depth: " .. #nav_history .. ")")
        end
        return loadAppContent_internal(app_id, path)
    else
        print("[Shell] Unknown app: " .. tostring(app_id))
        return false
    end
end

-- ===== NAVIGATION =====

-- Go back to previous app
function shellGoBack()
    print("[Shell] goBack called (history depth: " .. #nav_history .. ")")

    -- Hide notifications if open
    if notifications_expanded then
        toggleNotifications()
    end

    if #nav_history > 0 then
        local previous = table.remove(nav_history)
        print("[Shell] Going back to: " .. previous.app_id)

        -- Don't push to history when going back
        local temp_app = current_app
        current_app = nil
        current_app_path = nil

        local success = loadAppContent_internal(previous.app_id, previous.app_path)
        if not success then
            -- Restore state on failure
            current_app = temp_app
        end
        return success
    else
        print("[Shell] No history - already at root")
        if current_app ~= "home" then
            shellGoHome()
        end
    end
    return false
end

-- Go to home screen (clear history)
function shellGoHome()
    print("[Shell] goHome called")

    -- Hide notifications if open
    if notifications_expanded then
        toggleNotifications()
    end

    -- Clear history
    nav_history = {}

    -- Load home
    current_app = nil
    current_app_path = nil
    loadAppContent_internal("home", "apps/home/home_content.rml")
end

-- Show recents (placeholder)
function shellShowRecents()
    print("[Shell] showRecents called")
    showToast("Recent apps not implemented", "warning")
end

-- Launch external app (from base-apps)
function shellLaunchApp(package_id, app_path, entry_point)
    print("[Shell] Launching external app: " .. package_id)

    -- Push current to history
    if current_app then
        table.insert(nav_history, {
            app_id = current_app,
            app_path = current_app_path
        })
    end

    -- For external apps, we need content version of the entry
    -- Convert "app.rml" to "app_content.rml" or load directly
    local content_path = app_path .. "/" .. entry_point

    -- Switch sandbox context if available
    if switchAppSandbox then
        switchAppSandbox(package_id, app_path)
    end

    return loadAppContent_internal(package_id, content_path)
end

-- ===== TOASTS =====

function showToast(message, type)
    type = type or "default"

    local container = shell_document:GetElementById("toast-container")
    if not container then
        print("[Shell] Toast container not found")
        return
    end

    local class = "toast"
    if type == "success" then
        class = "toast toast-success"
    elseif type == "error" then
        class = "toast toast-error"
    elseif type == "warning" then
        class = "toast toast-warning"
    end

    -- Clear existing toasts and add new one
    local toast_html = '<div class="' .. class .. '"><span>' .. message .. '</span></div>'
    container.inner_rml = toast_html

    -- Auto-remove after 3 seconds
    if setTimeout then
        setTimeout(function()
            container.inner_rml = ""
        end, 3000)
    end

    print("[Shell] Toast: " .. message .. " (" .. type .. ")")
end

-- ===== DIALOGS =====

function showDialog(title, message, on_confirm, on_cancel)
    local overlay = shell_document:GetElementById("dialog-overlay")
    local title_elem = shell_document:GetElementById("dialog-title")
    local message_elem = shell_document:GetElementById("dialog-message")

    if overlay and title_elem and message_elem then
        title_elem.inner_rml = title
        message_elem.inner_rml = message
        overlay:SetClass("visible", true)

        dialog_callback = {
            confirm = on_confirm,
            cancel = on_cancel
        }

        print("[Shell] Dialog shown: " .. title)
    end
end

function dialogConfirm()
    local overlay = shell_document:GetElementById("dialog-overlay")
    if overlay then
        overlay:SetClass("visible", false)
    end

    if dialog_callback and dialog_callback.confirm then
        dialog_callback.confirm()
    end
    dialog_callback = nil
end

function dialogCancel()
    local overlay = shell_document:GetElementById("dialog-overlay")
    if overlay then
        overlay:SetClass("visible", false)
    end

    if dialog_callback and dialog_callback.cancel then
        dialog_callback.cancel()
    end
    dialog_callback = nil
end

-- ===== NOTIFICATIONS =====

function toggleNotifications()
    local panel = shell_document:GetElementById("notification-panel")
    if panel then
        notifications_expanded = not notifications_expanded
        panel:SetClass("expanded", notifications_expanded)
        print("[Shell] Notifications " .. (notifications_expanded and "expanded" or "collapsed"))
    end
end

function addNotification(title, text, icon, app_id)
    local list = shell_document:GetElementById("notification-list")
    if not list then return end

    icon = icon or "../../icons/message.tga"
    local notification_html = [[
        <div class="notification-item" onclick="onNotificationClick(']] .. (app_id or "") .. [[')">
            <div class="notification-icon">
                <img src="]] .. icon .. [["/>
            </div>
            <div class="notification-content">
                <div class="notification-title">]] .. title .. [[</div>
                <div class="notification-text">]] .. text .. [[</div>
            </div>
            <span class="notification-time">now</span>
        </div>
    ]]

    list.inner_rml = notification_html .. list.inner_rml
    print("[Shell] Notification added: " .. title)
end

function onNotificationClick(app_id)
    toggleNotifications()
    if app_id and app_id ~= "" then
        shellNavigateTo(app_id)
    end
end

function clearNotifications()
    local list = shell_document:GetElementById("notification-list")
    if list then
        list.inner_rml = ""
        print("[Shell] Notifications cleared")
    end
end

-- ===== LOADING OVERLAY =====

function showLoading(visible)
    local overlay = shell_document:GetElementById("loading-overlay")
    if overlay then
        overlay:SetClass("visible", visible)
    end
end

-- ===== UTILITY =====

function shellGetCurrentApp()
    return current_app
end

function shellCanGoBack()
    return #nav_history > 0
end

-- ===== DYNAMIC APP DISCOVERY =====

-- Default colors for apps without custom colors
local app_colors = {
    ["com.mosis.browser"] = "#F44336",
    ["com.mosis.camera"] = "#9C27B0",
    ["com.mosis.contacts"] = "#FF9800",
    ["com.mosis.dialer"] = "#4CAF50",
    ["com.mosis.messages"] = "#2196F3",
    ["com.mosis.music"] = "#E91E63",
    ["com.mosis.settings"] = "#607D8B",
    ["com.mosis.store"] = "#3F51B5",
}

-- Default color for unknown apps
local default_colors = {
    "#FF5722", "#009688", "#795548", "#673AB7",
    "#3F51B5", "#00BCD4", "#8BC34A", "#CDDC39"
}
local color_index = 1

function getAppColor(package_id)
    if app_colors[package_id] then
        return app_colors[package_id]
    end
    -- Assign a default color
    local color = default_colors[color_index]
    color_index = (color_index % #default_colors) + 1
    app_colors[package_id] = color
    return color
end

-- Built-in system apps (fallback when mosis.apps not available or empty)
local builtin_apps = {
    {package_id = "com.mosis.browser", name = "Browser", icon = "../../icons/browser.tga"},
    {package_id = "com.mosis.camera", name = "Camera", icon = "../../icons/camera.tga"},
    {package_id = "com.mosis.contacts", name = "Contacts", icon = "../../icons/contacts.tga"},
    {package_id = "com.mosis.dialer", name = "Phone", icon = "../../icons/phone.tga"},
    {package_id = "com.mosis.messages", name = "Messages", icon = "../../icons/message.tga"},
    {package_id = "com.mosis.music", name = "Music", icon = "../../icons/music.tga"},
    {package_id = "com.mosis.settings", name = "Settings", icon = "../../icons/settings.tga"},
    {package_id = "com.mosis.store", name = "Store", icon = "../../icons/store.tga"},
}

-- Populate home screen with discovered apps
function populateHomeApps()
    local apps = nil

    -- Try to get installed apps from mosis.apps API
    if mosis and mosis.apps then
        apps = mosis.apps.getInstalled()
    end

    -- Fall back to built-in apps if none discovered
    if not apps or #apps == 0 then
        print("[Shell] Using built-in system apps")
        apps = builtin_apps
    end

    print("[Shell] Populating home with " .. #apps .. " apps")

    local grid_container = shell_document:GetElementById("installed-apps")
    if not grid_container then
        print("[Shell] installed-apps container not found")
        return
    end

    local apps_html = ""
    local dock_apps = {}

    for _, app in ipairs(apps) do
        -- Skip home app itself
        if app.package_id ~= "com.mosis.home" then
            local color = getAppColor(app.package_id)
            local icon = app.icon or "../../icons/app.tga"
            local name = app.name or app.package_id

            -- Build app icon HTML
            local app_html = [[
                <div class="app-icon">
                    <div class="app-icon-image" style="background-color: ]] .. color .. [[;"
                         onclick="launchDiscoveredApp(']] .. app.package_id .. [[')">
                        <img src="]] .. icon .. [["/>
                    </div>
                    <span class="app-icon-label">]] .. name .. [[</span>
                </div>
            ]]
            apps_html = apps_html .. app_html

            -- Track dock apps (dialer, messages, contacts, browser)
            if app.package_id == "com.mosis.dialer" then
                dock_apps.dialer = {icon = icon, color = color, id = app.package_id}
            elseif app.package_id == "com.mosis.messages" then
                dock_apps.messages = {icon = icon, color = color, id = app.package_id}
            elseif app.package_id == "com.mosis.contacts" then
                dock_apps.contacts = {icon = icon, color = color, id = app.package_id}
            elseif app.package_id == "com.mosis.browser" then
                dock_apps.browser = {icon = icon, color = color, id = app.package_id}
            end
        end
    end

    -- Set grid content
    grid_container.inner_rml = apps_html

    -- Populate dock
    local dock_container = shell_document:GetElementById("home-dock")
    if dock_container and (dock_apps.dialer or dock_apps.messages or dock_apps.contacts or dock_apps.browser) then
        local dock_html = ""
        local dock_order = {"dialer", "messages", "contacts", "browser"}
        for _, key in ipairs(dock_order) do
            local app = dock_apps[key]
            if app then
                dock_html = dock_html .. [[
                    <div class="dock-item" style="background-color: ]] .. app.color .. [[;"
                         onclick="launchDiscoveredApp(']] .. app.id .. [[')">
                        <img src="]] .. app.icon .. [["/>
                    </div>
                ]]
            end
        end
        dock_container.inner_rml = dock_html
    end

    print("[Shell] Home populated with " .. #apps - 1 .. " apps")
end

-- Launch a discovered app by package_id
function launchDiscoveredApp(package_id)
    print("[Shell] Launching app: " .. package_id)

    -- Check if it's a built-in system app
    local app_id = package_id:gsub("com.mosis.", "")
    local builtin_ids = {
        browser = true, camera = true, contacts = true, dialer = true,
        messages = true, music = true, settings = true, store = true,
        ["sandbox-test"] = true
    }

    if builtin_ids[app_id] then
        -- Use shellNavigateTo for built-in apps
        return shellNavigateTo(app_id)
    end

    -- Try to find in installed apps
    if mosis and mosis.apps then
        local apps = mosis.apps.getInstalled()
        for _, app in ipairs(apps) do
            if app.package_id == package_id then
                print("[Shell] Launching installed app: " .. package_id)

                -- Push current to history
                if current_app then
                    table.insert(nav_history, {
                        app_id = current_app,
                        app_path = current_app_path
                    })
                    print("[Shell] Pushed to history: " .. current_app .. " (depth: " .. #nav_history .. ")")
                end

                -- Build content path - convert entry.rml to entry_content.rml
                local entry = app.entry_point or "main.rml"
                local content_entry = entry:gsub("%.rml$", "_content.rml")
                local content_path = "apps/" .. package_id:gsub("com.mosis.", "") .. "/" .. content_entry

                -- Switch sandbox if available
                if switchAppSandbox then
                    switchAppSandbox(package_id, app.install_path)
                end

                return loadAppContent_internal(package_id, content_path)
            end
        end
    end

    print("[Shell] App not found: " .. package_id)
    showToast("App not found", "error")
    return false
end

-- ===== SANDBOX TEST FUNCTIONS =====

function testSandboxTimer()
    local status = shell_document:GetElementById("timer-status")
    local results = shell_document:GetElementById("sandbox-results")

    if not setTimeout then
        if status then status.inner_rml = "Timer API not available" end
        return
    end

    if status then status.inner_rml = "Timer started..." end

    setTimeout(function()
        if status then status.inner_rml = "Timer completed!" end
        if results then results.inner_rml = results.inner_rml .. "\nTimer: PASSED (1 second delay)" end
        showToast("Timer test passed!", "success")
    end, 1000)
end

function testSandboxJSON()
    local status = shell_document:GetElementById("json-status")
    local results = shell_document:GetElementById("sandbox-results")

    if not json then
        if status then status.inner_rml = "JSON API not available" end
        return
    end

    local test_data = {name = "test", value = 42, nested = {a = 1, b = 2}}
    local encoded = json.encode(test_data)
    local decoded = json.decode(encoded)

    if decoded and decoded.name == "test" and decoded.value == 42 then
        if status then status.inner_rml = "JSON encode/decode: PASSED" end
        if results then results.inner_rml = results.inner_rml .. "\nJSON: PASSED" end
        showToast("JSON test passed!", "success")
    else
        if status then status.inner_rml = "JSON test: FAILED" end
        showToast("JSON test failed!", "error")
    end
end

function testSandboxCrypto()
    local status = shell_document:GetElementById("crypto-status")
    local results = shell_document:GetElementById("sandbox-results")

    if not crypto then
        if status then status.inner_rml = "Crypto API not available" end
        return
    end

    local hash = crypto.sha256("test")
    if hash and #hash > 0 then
        if status then status.inner_rml = "SHA256: " .. hash:sub(1, 16) .. "..." end
        if results then results.inner_rml = results.inner_rml .. "\nCrypto: PASSED" end
        showToast("Crypto test passed!", "success")
    else
        if status then status.inner_rml = "Crypto test: FAILED" end
        showToast("Crypto test failed!", "error")
    end
end

function testSandboxStorage()
    local status = shell_document:GetElementById("storage-status")
    local results = shell_document:GetElementById("sandbox-results")

    if not fs then
        if status then status.inner_rml = "Storage API not available" end
        return
    end

    local test_content = "Hello from sandbox test!"
    local write_ok, write_err = fs.write("/data/test.txt", test_content)

    if write_ok then
        local read_content, read_err = fs.read("/data/test.txt")
        if read_content == test_content then
            if status then status.inner_rml = "Storage read/write: PASSED" end
            if results then results.inner_rml = results.inner_rml .. "\nStorage: PASSED" end
            showToast("Storage test passed!", "success")
        else
            local err_msg = read_err or "content mismatch"
            if status then status.inner_rml = "Storage read: " .. err_msg end
            showToast("Storage read failed!", "error")
        end
    else
        local err_msg = write_err or "unknown error"
        if status then status.inner_rml = "Storage write: " .. err_msg end
        showToast("Storage write failed!", "error")
    end
end

-- Make shell functions globally available for apps
_G.showToast = showToast
_G.showDialog = showDialog
_G.addNotification = addNotification
_G.shellNavigateTo = shellNavigateTo
_G.shellLaunchApp = shellLaunchApp
_G.launchDiscoveredApp = launchDiscoveredApp
_G.populateHomeApps = populateHomeApps
_G.testSandboxTimer = testSandboxTimer
_G.testSandboxJSON = testSandboxJSON
_G.testSandboxCrypto = testSandboxCrypto
_G.testSandboxStorage = testSandboxStorage

print("[Shell] Shell script loaded")
