import QtQuick
import qs.config
import qs.services

SettingsPage {
    id: root

    // Only one application is expanded at a time; the point is a card you
    // can read, not twenty open at once.
    property string expandedApp: ""

    // Which focus mode is open for editing. One at a time, like the app rules.
    property string expandedMode: ""

    // Which mode's exception list is open. Separate from expandedMode so the
    // list does not unfold every time a mode is opened to change a switch.
    property string expandedAllow: ""

    function toggleAllowed(mode: var, appId: string, allowed: bool): void {
        const current = Array.isArray(mode.allow) ? mode.allow.map(String) : [];
        const at = current.indexOf(appId);
        if (allowed && at < 0)
            current.push(appId);
        else if (!allowed && at >= 0)
            current.splice(at, 1);
        FocusModes.update(String(mode.id), { allow: current });
    }

    // Schedule edits go through the mode's trigger list rather than replacing
    // it, so a mode that also has a game or fullscreen trigger keeps it.
    function reschedule(mode: var, changes: var): void {
        FocusModes.update(String(mode.id), {
            triggers: (mode.triggers ?? []).map(trigger =>
                trigger.kind === "schedule" ? Object.assign({}, trigger, changes) : trigger)
        });
    }

    function toggleDay(mode: var, day: int): void {
        const schedule = (mode.triggers ?? []).find(trigger => trigger.kind === "schedule");
        if (!schedule)
            return;
        const days = Array.isArray(schedule.days) ? schedule.days.slice() : [];
        const at = days.indexOf(day);
        if (at >= 0)
            days.splice(at, 1);
        else
            days.push(day);
        days.sort((a, b) => a - b);
        root.reschedule(mode, { days: days });
    }

    title: "Notifications & Focus"
    lede: "Control interruptions without losing useful history."

    SettingsCard {
        title: "Notifications"

        SettingRow {
            label: "Do Not Disturb"
            detail: "Keep notifications in the center but suppress banners"
            controlWidth: 48

            SettingsToggle {
                anchors.right: parent.right
                anchors.verticalCenter: parent.verticalCenter
                checked: Notifs.doNotDisturb
                onToggled: value => Notifs.doNotDisturb = value
            }
        }

        TextRow {
            label: "Notification history"
            detail: "Live notifications retained by the shell"
            value: Notifs.history.length === 1 ? "1 item" : `${Notifs.history.length} items`
        }

        ActionRow {
            label: "Clear notification history"
            detail: "Dismiss every item currently in the notification center"
            divider: false
            action: "Clear all"
            enabled: Notifs.history.length > 0
            onTriggered: Notifs.dismissAll()
        }
    }

    SettingsCard {
        title: "Banner behavior"

        SliderRow { setting: "notificationTimeoutMs" }
        SliderRow {
            setting: "notificationTimeoutCriticalMs"
            zeroLabel: "Never"
        }
        SliderRow { setting: "notificationHistoryLimit" }
        SliderRow { setting: "maxVisibleToasts"; divider: false }
    }

    SettingsCard {
        title: "Application rules"
        subtitle: "Apps appear here after they send a notification."

        TextRow {
            visible: Notifs.applications.length === 0
            label: "No applications remembered yet"
            detail: "Application controls will appear after the first notification arrives."
            divider: false
        }

        Repeater {
            model: Notifs.applications

            Column {
                id: appEntry

                required property var modelData

                readonly property var app: appEntry.modelData
                readonly property var rule: Notifs.appRule(appEntry.app.id)
                readonly property bool open: root.expandedApp === String(appEntry.app.id)

                // What the two lock-screen switches add up to, so the common
                // case -- reading rather than changing -- needs no interaction.
                // Every app repeated both switch labels verbatim before this,
                // which made twenty apps sixty rows of identical sentences.
                readonly property string summary: {
                    if (!appEntry.rule.enabled)
                        return "Notifications off";
                    if (!appEntry.rule.showOnLockScreen)
                        return "On · hidden on the lock screen";
                    return appEntry.rule.showContentOnLockScreen
                        ? "On · lock screen shows content"
                        : "On · lock screen shows the sender only";
                }

                width: parent.width

                SettingRow {
                    width: parent.width
                    label: appEntry.app.name
                    // The identifier is only worth the space while the app is
                    // open, which is the only time it disambiguates anything.
                    detail: appEntry.open ? String(appEntry.app.id) : appEntry.summary
                    activatable: true
                    divider: !appEntry.open
                    controlWidth: 92
                    onActivated: root.expandedApp = appEntry.open ? "" : String(appEntry.app.id)

                    Row {
                        anchors.right: parent.right
                        anchors.verticalCenter: parent.verticalCenter
                        spacing: 11

                        SettingsToggle {
                            anchors.verticalCenter: parent.verticalCenter
                            checked: appEntry.rule.enabled
                            onToggled: value => Notifs.setAppRule(appEntry.app.id, { enabled: value })
                        }

                        Text {
                            anchors.verticalCenter: parent.verticalCenter
                            text: appEntry.open ? "\u25B4" : "\u25BE"
                            color: Theme.fgMuted
                            font.family: Theme.fontFamily
                            font.pixelSize: Theme.fontSizeSmall
                        }
                    }
                }

                SettingRow {
                    width: parent.width
                    visible: appEntry.open
                    label: "Show on lock screen"
                    detail: "Allow this app's notifications on the lock screen"
                    controlWidth: 48

                    SettingsToggle {
                        anchors.right: parent.right
                        anchors.verticalCenter: parent.verticalCenter
                        checked: appEntry.rule.showOnLockScreen
                        onToggled: value => Notifs.setAppRule(appEntry.app.id, { showOnLockScreen: value })
                    }
                }

                SettingRow {
                    width: parent.width
                    visible: appEntry.open
                    label: "Show content on lock screen"
                    detail: "Show message details when this app is visible there"
                    divider: false
                    controlWidth: 48
                    // Meaningless unless the app reaches the lock screen at all.
                    opacity: appEntry.rule.showOnLockScreen ? 1 : 0.45

                    SettingsToggle {
                        anchors.right: parent.right
                        anchors.verticalCenter: parent.verticalCenter
                        enabled: appEntry.rule.showOnLockScreen
                        checked: appEntry.rule.showContentOnLockScreen
                        onToggled: value => Notifs.setAppRule(appEntry.app.id, { showContentOnLockScreen: value })
                    }
                }
            }
        }
    }

    SettingsCard {
        title: "Focus modes"
        subtitle: FocusModes.active
            ? FocusModes.activeName + " is on because " + FocusModes.activeReason + "."
            : "What quiets this machine, and what turns it on. The first mode whose condition is true wins, so order is priority."

        Repeater {
            model: FocusModes.modes

            delegate: Column {
                id: modeEntry

                required property var modelData

                readonly property var mode: modeEntry.modelData
                readonly property string modeId: String(modeEntry.mode.id ?? "")
                readonly property bool open: root.expandedMode === modeEntry.modeId
                readonly property bool running: FocusModes.activeMode?.id === modeEntry.modeId
                readonly property var schedule: (modeEntry.mode.triggers ?? [])
                    .find(trigger => trigger.kind === "schedule") ?? null

                width: parent.width

                SettingRow {
                    width: parent.width
                    label: String(modeEntry.mode.name ?? "")
                    detail: modeEntry.running
                        ? "On now — " + FocusModes.activeReason
                        : FocusModes.summary(modeEntry.mode)
                    activatable: true
                    divider: !modeEntry.open
                    controlWidth: 92
                    onActivated: root.expandedMode = modeEntry.open ? "" : modeEntry.modeId

                    Row {
                        anchors.right: parent.right
                        anchors.verticalCenter: parent.verticalCenter
                        spacing: 11

                        SettingsToggle {
                            anchors.verticalCenter: parent.verticalCenter
                            checked: modeEntry.mode.enabled === true
                            onToggled: value => FocusModes.setEnabled(modeEntry.modeId, value)
                        }

                        Text {
                            anchors.verticalCenter: parent.verticalCenter
                            text: modeEntry.open ? "\u25B4" : "\u25BE"
                            color: Theme.fgMuted
                            font.family: Theme.fontFamily
                            font.pixelSize: Theme.fontSizeSmall
                        }
                    }
                }

                SettingRow {
                    width: parent.width
                    visible: modeEntry.open
                    label: "Silence notifications"
                    detail: "Banners are held until the mode ends"
                    controlWidth: 48

                    SettingsToggle {
                        anchors.right: parent.right
                        anchors.verticalCenter: parent.verticalCenter
                        checked: modeEntry.mode.silence === true
                        onToggled: value => FocusModes.update(modeEntry.modeId, { silence: value })
                    }
                }

                SettingRow {
                    width: parent.width
                    visible: modeEntry.open
                    label: "Keep the screen awake"
                    detail: "Caffeine, for as long as the mode is on"
                    controlWidth: 48
                    divider: modeEntry.schedule !== null

                    SettingsToggle {
                        anchors.right: parent.right
                        anchors.verticalCenter: parent.verticalCenter
                        checked: modeEntry.mode.keepAwake === true
                        onToggled: value => FocusModes.update(modeEntry.modeId, { keepAwake: value })
                    }
                }

                // Only modes that actually have a schedule get the schedule
                // controls; a game trigger has no times to set.
                TextFieldRow {
                    width: parent.width
                    visible: modeEntry.open && modeEntry.schedule !== null
                    label: "From"
                    detail: "24-hour, such as 23:30"
                    text: String(modeEntry.schedule?.start ?? "")
                    placeholder: "23:30"
                    onAccepted: value => root.reschedule(modeEntry.mode, { start: value })
                }

                TextFieldRow {
                    width: parent.width
                    visible: modeEntry.open && modeEntry.schedule !== null
                    label: "Until"
                    detail: "A time earlier than the start means the window crosses midnight"
                    text: String(modeEntry.schedule?.end ?? "")
                    placeholder: "07:00"
                    onAccepted: value => root.reschedule(modeEntry.mode, { end: value })
                }

                SettingRow {
                    width: parent.width
                    visible: modeEntry.open && modeEntry.mode.silence === true
                    label: "May interrupt"
                    detail: (modeEntry.mode.allow ?? []).length === 0
                        ? "Nothing gets through while this mode is on"
                        : "Everything else is held until the mode ends"
                    activatable: true
                    controlWidth: 150
                    onActivated: root.expandedAllow =
                        root.expandedAllow === modeEntry.modeId ? "" : modeEntry.modeId

                    Row {
                        anchors.right: parent.right
                        anchors.verticalCenter: parent.verticalCenter
                        spacing: 9

                        Text {
                            anchors.verticalCenter: parent.verticalCenter
                            text: (modeEntry.mode.allow ?? []).length === 0
                                ? "Nothing"
                                : (modeEntry.mode.allow ?? []).length + " apps"
                            color: Theme.fgDim
                            font.family: Theme.fontFamily
                            font.pixelSize: Theme.fontSize
                        }

                        Text {
                            anchors.verticalCenter: parent.verticalCenter
                            text: root.expandedAllow === modeEntry.modeId ? "\u25B4" : "\u25BE"
                            color: Theme.fgMuted
                            font.family: Theme.fontFamily
                            font.pixelSize: Theme.fontSizeSmall
                        }
                    }
                }

                // Drawn from the applications that have actually sent a
                // notification, which is the same list the rules above use --
                // an exception for something that never notifies is not a
                // choice worth offering.
                Repeater {
                    model: (modeEntry.open && root.expandedAllow === modeEntry.modeId)
                        ? Notifs.applications
                        : []

                    delegate: SettingRow {
                        required property var modelData
                        width: parent.width
                        label: String(modelData.name ?? "")
                        detail: String(modelData.id ?? "")
                        controlWidth: 48

                        SettingsToggle {
                            anchors.right: parent.right
                            anchors.verticalCenter: parent.verticalCenter
                            checked: (modeEntry.mode.allow ?? []).indexOf(String(modelData.id)) >= 0
                            onToggled: value => root.toggleAllowed(modeEntry.mode, String(modelData.id), value)
                        }
                    }
                }

                TextRow {
                    width: parent.width
                    visible: modeEntry.open && root.expandedAllow === modeEntry.modeId
                        && Notifs.applications.length === 0
                    label: "No applications yet"
                    detail: "They appear here once they have sent a notification."
                    value: ""
                }

                SettingRow {
                    width: parent.width
                    visible: modeEntry.open && modeEntry.schedule !== null
                    label: "On these days"
                    detail: "A window that crosses midnight belongs to the day it starts on"
                    controlWidth: 250
                    divider: false

                    Row {
                        anchors.right: parent.right
                        anchors.verticalCenter: parent.verticalCenter
                        spacing: 5

                        Repeater {
                            model: [
                                { value: 1, label: "M" }, { value: 2, label: "T" },
                                { value: 3, label: "W" }, { value: 4, label: "T" },
                                { value: 5, label: "F" }, { value: 6, label: "S" },
                                { value: 0, label: "S" }
                            ]

                            delegate: Rectangle {
                                id: dayPill

                                required property var modelData

                                readonly property bool on:
                                    (modeEntry.schedule?.days ?? []).indexOf(dayPill.modelData.value) >= 0

                                width: 30
                                height: 28
                                radius: 8
                                color: dayPill.on ? Theme.alpha(Theme.accent, 0.22)
                                                  : Theme.alpha(Theme.fg, 0.06)
                                border.width: dayPill.on ? 1 : 0
                                border.color: Theme.alpha(Theme.accent, 0.5)

                                Text {
                                    anchors.centerIn: parent
                                    text: String(dayPill.modelData.label)
                                    color: dayPill.on ? Theme.fg : Theme.fgDim
                                    font.family: Theme.fontFamily
                                    font.pixelSize: Theme.fontSizeSmall
                                    font.weight: dayPill.on ? Font.DemiBold : Font.Medium
                                }

                                HoverHandler { cursorShape: Qt.PointingHandCursor }
                                TapHandler {
                                    onTapped: root.toggleDay(modeEntry.mode, dayPill.modelData.value)
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    SettingsCard {
        title: "Focus sessions"
        subtitle: "A focus session binds quiet mode and Caffeine to the current workspace."

        SettingRow {
            label: "Keep the screen awake"
            detail: Caffeine.enabled
                ? "The display will not blank or lock while this is on"
                : "Idle timings on Power & Lock apply normally"
            controlWidth: 48

            SettingsToggle {
                anchors.right: parent.right
                anchors.verticalCenter: parent.verticalCenter
                checked: Caffeine.enabled
                onToggled: value => Caffeine.enabled = value
            }
        }

        SettingRow {
            id: durationRow

            label: "Default duration"
            detail: "Used by Super+Shift+F and Quick Settings"
            controlWidth: 264

            Row {
                anchors.right: parent.right
                anchors.verticalCenter: parent.verticalCenter
                spacing: 6

                Repeater {
                    model: [25, 45, 60, 90]

                    SettingsButton {
                        required property int modelData

                        text: `${modelData}m`
                        tone: DesktopPreferences.get("focusDurationMinutes") === modelData ? "accent" : "normal"
                        onClicked: SystemSettings.commitPreference("focusDurationMinutes", modelData)
                    }
                }
            }
        }

        SettingRow {
            label: FocusSession.active ? `Active on ${FocusSession.workspaceLabel}` : "No active focus session"
            detail: FocusSession.active ? `${FocusSession.remainingText} remaining` : "Start one without leaving Settings"
            divider: false
            controlWidth: 120

            SettingsButton {
                anchors.right: parent.right
                anchors.verticalCenter: parent.verticalCenter
                text: FocusSession.active ? "Show controls" : "Start focus"
                tone: FocusSession.active ? "normal" : "accent"
                onClicked: FocusSession.reveal()
            }
        }
    }
}
