#!/usr/bin/env bash

set -euo pipefail

project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
phone_controls="$project_root/config/dot/quickshell/modules/quicksettings/PhoneControls.qml"
phone_actions="$project_root/config/dot/quickshell/modules/quicksettings/PhoneActions.qml"
quicksettings_qmldir="$project_root/config/dot/quickshell/modules/quicksettings/qmldir"
system_settings="$project_root/config/dot/quickshell/services/SystemSettings.qml"
harness="$project_root/config/dot/quickshell/phone-controls-harness.qml"
kde_fixture="$project_root/tests/quickshell/fixtures/PhoneControlsKdeConnect.qml"
settings_fixture="$project_root/tests/quickshell/fixtures/PhoneControlsSystemSettings.qml"
state_home="$(mktemp -d /tmp/panama-phone-controls.XXXXXX)"
config_path="$state_home/quickshell"

fail() {
    printf 'Phone Messages contract: %s\n' "$1" >&2
    exit 1
}

[[ -f "$phone_controls" ]] || fail 'Phone controls component is missing'
[[ -f "$phone_actions" ]] || fail 'Nonvisual Phone actions component is missing'
[[ -f "$system_settings" ]] || fail 'System settings service is missing'
[[ -f "$harness" ]] || fail 'Phone controls runtime harness is missing'
[[ -f "$kde_fixture" ]] || fail 'Phone controls KDE fixture is missing'
[[ -f "$settings_fixture" ]] || fail 'Phone controls System Settings fixture is missing'

rg -Fq 'PhoneActions 1.0 PhoneActions.qml' "$quicksettings_qmldir" \
    || fail 'Phone actions component is not registered'
rg -Fq 'PhoneActions {' "$phone_controls" \
    || fail 'Phone controls do not consume the shared action model'
rg -Fq 'readonly property var actionModels: phoneActions.actionModels' "$phone_controls" \
    || fail 'Phone controls do not render the shared action models'
rg -Fq 'PhoneActions {' "$harness" \
    || fail 'Runtime harness does not use the nonvisual action model'
if rg -Fq 'PhoneControls {' "$harness"; then
    fail 'Runtime harness still instantiates the visual Phone controls tree'
fi

cleanup() {
    XDG_STATE_HOME="$state_home" QS_DISABLE_CRASH_HANDLER=1 \
        qs -p "$config_path/phone-controls-harness.qml" kill >/dev/null 2>&1 || true
    rm -rf "$state_home"
}
trap cleanup EXIT

qs_for_harness() {
    XDG_STATE_HOME="$state_home" QS_DISABLE_CRASH_HANDLER=1 \
        qs -p "$config_path/phone-controls-harness.qml" "$@"
}

start_harness() {
    cp -a "$project_root/config/dot/quickshell" "$config_path"
    cp "$kde_fixture" "$config_path/services/KdeConnect.qml"
    cp "$settings_fixture" "$config_path/services/SystemSettings.qml"
    qs_for_harness --daemonize >/dev/null
    for _ in $(seq 1 40); do
        if qs_for_harness ipc show 2>/dev/null | rg -q '^target phone-controls-test$'; then
            return
        fi
        sleep 0.1
    done
    fail 'isolated Phone controls harness did not start'
}

assert_fixture_state() {
    local fixture="$1"
    local expected="$2"

    qs_for_harness ipc call phone-controls-test fixture "$fixture" >/dev/null
    local actual
    actual="$(qs_for_harness ipc call phone-controls-test status)"
    [[ -n "$actual" ]] || fail 'Phone controls do not expose runtime action enablement'
    jq -e --argjson expected "$expected" '
        .bluebubblesAvailable == true and .appLaunches == 0 and .phoneActions == 0 and
        (.actions | length == 4) and
        ([.actions[] | select(.id == "messages") | .enabled] == [true]) and
        ([.actions[] | select(.id != "messages") | .enabled] == $expected)
    ' <<<"$actual" >/dev/null \
        || fail "unexpected $fixture action state: $actual"
}

# These source checks do not evaluate the launch branch. The isolated runtime
# harness below uses fixture singletons and only reads action-model status.

rg -Fq '"bluebubbles": ["flatpak", "run", "app.bluebubbles.BlueBubbles"]' "$system_settings" \
    || fail 'BlueBubbles does not use the fixed Flatpak argument vector'
rg -Fq 'readonly property bool bluebubblesAvailable: root.bluebubblesDetected' "$system_settings" \
    || fail 'BlueBubbles availability is not exposed independently'
rg -Fq 'command: ["flatpak", "info", "app.bluebubbles.BlueBubbles"]' "$system_settings" \
    || fail 'BlueBubbles installed-state probe is missing'

for action in share clipboard ring messages; do
    rg -Fq "id: \"$action\"" "$phone_actions" \
        || fail "Phone actions do not include $action"
done

if rg -Fq '.filter(' "$phone_actions"; then
    fail 'Phone actions are filtered instead of keeping four stable columns'
fi
rg -Fq 'columns: 4' "$phone_controls" \
    || fail 'Phone actions do not use four equal columns'
rg -Fq 'root.actionModels.length - 1' "$phone_controls" \
    || fail 'Phone action widths are not calculated from the fixed model count'
rg -Fq 'SystemSettings.bluebubblesAvailable' "$phone_controls" \
    || fail 'Messages enablement does not read BlueBubbles availability'
rg -Fq 'KdeConnect.phoneReachable' "$phone_controls" \
    || fail 'KDE action reachability semantics are missing'
rg -Fq 'function actionEnabled(action: string): bool' "$phone_controls" \
    || fail 'Phone controls do not expose action enablement'
rg -Fq 'KdeConnect.supports(action)' "$phone_actions" \
    || fail 'KDE action capability semantics are missing'
rg -Fq 'else if (action === "messages")' "$phone_controls" \
    || fail 'Messages has no independent invocation branch'
rg -Fq 'SystemSettings.openApplication("bluebubbles")' "$phone_controls" \
    || fail 'Messages does not use the allow-listed BlueBubbles launcher'
rg -Fq 'BlueBubbles is not installed' "$phone_controls" \
    || fail 'Missing BlueBubbles has no quiet explanatory row'
rg -Fq 'KDE Connect actions are unavailable until the iPhone reconnects' "$phone_controls" \
    || fail 'Offline copy does not scope unavailability to KDE Connect actions'
rg -Fq 'border.width: actionMouse.activeFocus ? 2 : 0' "$phone_controls" \
    || fail 'Phone actions do not visibly indicate keyboard focus'
rg -Fq 'Accessible.role: Accessible.Button' "$phone_controls" \
    || fail 'Phone actions do not expose an Accessible button role'
rg -Fq 'Accessible.name: actionButton.modelData.label' "$phone_controls" \
    || fail 'Phone actions do not expose their accessible name'
rg -Fq 'Accessible.description: root.actionAccessibleDescription(actionButton.modelData.id)' "$phone_controls" \
    || fail 'Phone actions do not expose their availability state'
rg -Fq 'Accessible.focusable: actionMouse.enabled' "$phone_controls" \
    || fail 'Phone action accessibility does not respect disabled state'
rg -Fq 'Accessible.onPressAction:' "$phone_controls" \
    || fail 'Phone actions do not provide an accessible press handler'

# A static negative guard keeps the Messages branch from accidentally inheriting
# KDE Connect reachability, transfer, or plugin conditions.
messages_branch="$(sed -n '/else if (action === "messages")/,/^    }/p' "$phone_controls")"
printf '%s\n' "$messages_branch" | rg -Fq 'KdeConnect.' \
    && fail 'Messages is coupled to KDE Connect'

start_harness
assert_fixture_state offline '[false, false, false]'
assert_fixture_state unsupported '[true, false, false]'
assert_fixture_state transfer '[false, false, false]'

printf 'Phone Messages contract: PASS\n'
