/*
 * Copyright (C) 2020-2026 Savoir-faire Linux Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Enums 1.1
import net.jami.Helpers 1.1
import net.jami.Constants 1.1
import "mainview"
import "mainview/components"
import "wizardview"
import "commoncomponents"
import QWindowKit

Window {
    id: appWindow

    // Start hidden; LayoutManager.restoreWindowSettings() will set the
    // correct visibility once geometry has been applied.
    visibility: Window.Hidden

    readonly property bool useFrameless: JamiQmlUtils.isMacOS26OrLater || UtilsAdapter.getAppValue(Settings.Key.UseFramelessWindow)
    property bool isRTL: UtilsAdapter.isRTL
    LayoutMirroring.enabled: isRTL
    LayoutMirroring.childrenInherit: isRTL
    property var raiseWhenCalled: AppSettingsManager.getValue(Settings.RaiseWhenCalled)

    // DEBUG Button on top left to force switch RTL (toggle between AR and EN
    Button {
        id: rtlToggleButton
        text: UtilsAdapter.isRTL ? "RTL" : "LTR"
        // To debug RTL:
        // - use `QML_PROPERTY(bool, isRTL) instead of QML_RO_PROPERTY(bool, isRTL)` in utilsadapter.h
        // - comment the following line
        visible: false
        anchors.bottom: parent.bottom
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.margins: 10
        z: 999
        onClicked: UtilsAdapter.isRTL = !UtilsAdapter.isRTL
    }

    onActiveFocusItemChanged: {
        focusOverlay.margin = -5;
        if (activeFocusItem) {
            const goodReasonToChangeFocus = activeFocusItem instanceof ItemDelegate || ((activeFocusItem.focusReason === Qt.TabFocusReason) || (activeFocusItem.focusReason === Qt.BacktabFocusReason));
            if (goodReasonToChangeFocus) {
                if (activeFocusItem.focusOnChild) {
                    focusOverlay.parent = activeFocusItem.parent;
                } else if (activeFocusItem.dontShowFocusState) {
                    focusOverlay.parent = null;
                } else {
                    if (activeFocusItem.showFocusMargin)
                        focusOverlay.margin = 0;
                    focusOverlay.parent = activeFocusItem;
                }
            } else {
                focusOverlay.parent = null;
            }
        } else {
            focusOverlay.parent = null;
        }
    }

    Loader {
        id: errorLoader
        active: true
        anchors {
            bottom: parent.bottom
            left: parent.left
            right: parent.right
        }
        sourceComponent: GenericErrorsRow {
            id: genericError
            text: CurrentAccount.enabled ? JamiStrings.noNetworkConnectivity : JamiStrings.disabledAccount
            height: visible ? JamiTheme.qwkTitleBarHeight : 0
        }
    }

    Loader {
        id: macTitleBarLoader
        active: JamiQmlUtils.isMacOS26OrLater
        height: 35
        anchors {
            top: parent.top
            left: parent.left
            right: parent.right
        }
        z: 10
        sourceComponent: Rectangle {
            id: macTitleBar
            anchors.fill: parent
            color: "transparent"
            MouseArea {
                anchors.fill: parent
                onPressed: m => {
                    MainApplication.startSystemMove(appWindow);
                    m.accepted = false;
                }
            }
        }
    }

    Rectangle {
        id: focusOverlay
        objectName: "focusOverlay"
        property real margin: -5
        z: -2
        anchors.fill: parent
        anchors.margins: margin
        visible: true
        color: "transparent"
        radius: parent ? parent.radius ? parent.radius : 0 : 0
        border.width: 2
        border.color: JamiTheme.tintedBlue
    }

    // Used to manage full screen mode and save/restore window geometry.
    LayoutManager {
        id: layoutManager
        appContainer: fullscreenContainer
    }
    // Used to manage dynamic view loading and unloading.
    property ViewManager viewManager: ViewManager {
        id: appViewManager
    }
    // Used to manage the view stack and the current view.
    property ViewCoordinator viewCoordinator: ViewCoordinator {
        viewManager: appViewManager
        appContext: appWindow.appContext
    }

    readonly property var _appContextWindow: appWindow
    readonly property var _appContextLayoutManager: layoutManager
    property QtObject appContext: QtObject {
        property var appWindow: _appContextWindow
        property ViewManager viewManager: appWindow.viewManager
        property ViewCoordinator viewCoordinator: appWindow.viewCoordinator
        property LayoutManager layoutManager: _appContextLayoutManager
        readonly property bool useFrameless: appWindow.useFrameless
        readonly property bool isInSinglePaneMode: viewCoordinator.isInSinglePaneMode
        readonly property string currentViewName: viewCoordinator.currentViewName
    }

    // Tracks whether the main view has finished loading.
    readonly property bool mainViewReady: mainViewLoader.status === Loader.Ready

    // ── Auto PiP pop-out ──────────────────────────────────────────────────────
    // Track the previous conversation so we can pop it out if it had a call.
    property string _prevConvForAutoPip: ""
    property string _prevAccountForAutoPip: ""

    onVisibilityChanged: function(visibility) {
        if (visibility === Window.Minimized
                && AppSettingsManager.getValue(Settings.AutoPipCallOnNavAway)
                && !CallPipWindowManager.isPipActive
                && CurrentConversation.hasCall) {
            CallPipWindowManager.popOutCall(CurrentConversation.id, CurrentAccount.id);
        }
    }

    function cleanupMainView() {
        // Save the main view window size if loading anything else.
        layoutManager.saveWindowSettings();

        // Unload any created views used by the main view.
        viewCoordinator.deinit();
    }

    function closeOrMinimize(force = false) {
        // If we're in the onboarding wizard or 'MinimizeOnClose'
        // is set, then we can quit
        var minimizeToTray = UtilsAdapter.getAppValue(Settings.MinimizeOnClose) && UtilsAdapter.isSystemTrayIconVisible();
        if (force || !minimizeToTray || !UtilsAdapter.getAccountListSize()) {
            Qt.quit();
        } else {
            layoutManager.closeToTray();
        }
    }

    title: JamiStrings.appTitle

    Connections {
        id: connectionMigrationEnded

        target: CurrentAccountToMigrate

        function onAccountNeedsMigration(accountId) {
            viewCoordinator.present("AccountMigrationView");
        }

        function onAllMigrationsFinished() {
            viewCoordinator.dismiss("AccountMigrationView");
            viewCoordinator.present("WelcomePage");
        }
    }

    function initMainView(view) {
        console.info("Initializing main view");

        // Main window, load any valid app settings, and allow the
        // layoutManager to handle as much as possible.
        layoutManager.restoreWindowSettings();

        // QWK: setup
        if (useFrameless && !JamiQmlUtils.isMacOS26OrLater) {
            windowAgent.setTitleBar(titleBar);
            // Now register the system buttons (non-macOS).
            if (sysBtnsLoader.item) {
                const sysBtns = sysBtnsLoader.item;
                windowAgent.setSystemButton(WindowAgent.Minimize, sysBtns.minButton);
                windowAgent.setSystemButton(WindowAgent.Maximize, sysBtns.maxButton);
                windowAgent.setSystemButton(WindowAgent.Close, sysBtns.closeButton);
            }
        }

        // Set up the event filter for macOS.
        if (Qt.platform.os.toString() === "osx") {
            MainApplication.setEventFilter();
            if (JamiQmlUtils.isMacOS26OrLater) {
                MainApplication.setToolBar(appWindow);
            }
        }

        // Set the viewCoordinator's root item.
        viewCoordinator.init(view);

        // Navigate to something.
        if (UtilsAdapter.getAccountListSize() > 0) {
            // Already have an account.
            if (CurrentAccountToMigrate.accountToMigrateListSize > 0)
                // Do we need to migrate any accounts?
                viewCoordinator.present("AccountMigrationView");
            else
                // Okay now just start the client normally.
                viewCoordinator.present("WelcomePage");

            // Preload ConversationView so the first conversation selection is instant.
            // WelcomePage already creates SidePanel; this only instantiates ChatView and
            // MessageListView, separating QML tree creation from message data loading.
            Qt.callLater(function () {
                viewCoordinator.preload("ConversationView");
            });
        } else {
            // No account, so start the wizard.
            viewCoordinator.present("WizardView");
        }

        // Quiet check for updates on start if set to.
        if (Qt.platform.os.toString() === "windows") {
            if (UtilsAdapter.getAppValue(Settings.AutoUpdate)) {
                AppVersionManager.checkForUpdates(true);
                AppVersionManager.setAutoUpdateCheck(true);
            }
        }

        // Handle a start URI if set as start option.
        MainApplication.handleUriAction();
    }

    Component.onCompleted: {
        // QWK: setup
        if (useFrameless) {
            windowAgent.setup(appWindow);
        }

        mainViewLoader.active = true;

        // Dbus error handler for Linux.
        if (Qt.platform.os.toString() !== "windows" && Qt.platform.os.toString() !== "osx")
            DBusErrorHandler.setActive(true);

        // Handle potential crash recovery.
        var crashedLastRun = crashReporter.getHasPendingReport();
        if (crashedLastRun) {
            // A crash was detected during the last session. We need to inform the user and offer to send a crash report.
            var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/ConfirmDialog.qml", {
                "title": JamiStrings.crashReportTitle,
                "textLabel": JamiStrings.crashReportMessage + "\n\n" + JamiStrings.crashReportMessageExtra,
                "confirmLabel": JamiStrings.send,
                "rejectLabel": JamiStrings.dontSend,
                "textHAlign": Text.AlignLeft,
                "textMaxWidth": 400
            });
            dlg.accepted.connect(function () {
                crashReporter.uploadLastReport();
            });
            dlg.rejected.connect(function () {
                crashReporter.clearReports();
            });
        }
    }

    // Background properties (moved from MainView so they render on startup)
    property color currentConversationColor: CurrentConversation.color
    property color tintColor: Qt.rgba(currentConversationColor.r, currentConversationColor.g, currentConversationColor.b, JamiTheme.chatViewBackgroundTintOpacity)
    property real tintOpacity: CurrentConversation.id === "" ? 0.0 : 1.0
    property color baseColor: Qt.tint(JamiTheme.globalBackgroundColor, tintColor)

    // WelcomePage background properties
    property variant uiCustomization: CurrentAccount.uiCustomization
    onUiCustomizationChanged: updateWelcomeBackgroundFlags()
    property bool hasCustomUi: false
    property bool hasCustomBgImage: false
    property string customBgUrl: ""
    property bool hasCustomBgColor: false
    property string customBgColor: ""

    Connections {
        target: CurrentAccount
        function onIdChanged() {
            updateWelcomeBackgroundFlags();
        }
    }

    Connections {
        target: JamiTheme
        function onDarkThemeChanged() {
            customBgUrl = hasCustomBgImage ? customBgUrl : JamiTheme.welcomeBg;
        }
    }

    function updateWelcomeBackgroundFlags() {
        hasCustomUi = Object.keys(uiCustomization).length > 0;
        hasCustomBgImage = (hasCustomUi && uiCustomization.backgroundType === "image");
        customBgUrl = hasCustomBgImage ? (CurrentAccount.managerUri + uiCustomization.backgroundColorOrUrl) : "";
        hasCustomBgColor = (hasCustomUi && uiCustomization.backgroundType === "color");
        customBgColor = hasCustomBgColor ? uiCustomization.backgroundColorOrUrl : "";
    }

    // JAMS-specific background colour
    Rectangle {
        id: welcomeBgRect
        anchors.fill: parent
        color: hasCustomBgColor ? customBgColor : "transparent"
        visible: hasCustomBgColor
        z: -1
    }

    // Background colour overlay
    // When NOT in a conversation, this will be transparent (i.e. the welcome page image)
    // When IN a conversation, this will be the conversation's assigned colour
    Rectangle {
        anchors.fill: parent
        color: baseColor
        opacity: tintOpacity
    }

    // Will cause a fade between background colours when switching conversations
    Behavior on baseColor {
        ColorAnimation {
            duration: JamiTheme.chatViewFadeDuration
            easing.type: Easing.InOutQuad
        }
    }

    // Will cause fade-in/out effect when transitioning between the welcome page and a conversation
    Behavior on tintOpacity {
        NumberAnimation {
            duration: JamiTheme.longFadeDuration
            easing.type: Easing.InOutQuad
        }
    }

    // The global background image that can be seen when NOT in a conversation
    // Three cases (based on priority):
    // 1. The background image set by the user in the Appearance settings
    // 2. The background set by a JAMS administrator (if applicable)
    // 3. The default light/dark mode background
    CachedImage {
        id: welcomeCachedImgLogo
        downloadUrl: (AccountSettingsManager.accountSettingsPropertyMap.backgroundUri === undefined || AccountSettingsManager.accountSettingsPropertyMap.backgroundUri === "") ? (hasCustomBgImage ? customBgUrl : JamiTheme.welcomeBg) : AccountSettingsManager.accountSettingsPropertyMap.backgroundUri
        visible: !hasCustomBgColor
        anchors.fill: parent
        localPath: UtilsAdapter.getCachePath() + "/" + CurrentAccount.id + "/welcomeview/" + UtilsAdapter.base64Encode(downloadUrl) + fileExtension
        imageFillMode: Image.PreserveAspectCrop
        z: -1
    }

    // A blur effect applied to the background image (can be toggled in the Appearance settings)
    FastBlur {
        anchors.fill: welcomeCachedImgLogo
        source: welcomeCachedImgLogo
        radius: JamiTheme.welcomePageFastBlurRadius
        z: -1
        visible: AccountSettingsManager.accountSettingsPropertyMap.backgroundBlurEnabled && welcomeCachedImgLogo.visible
    }

    // A theme-based colour overlay applied to the background image (can be toggled in the Appearance settings)
    ColorOverlay {
        anchors.fill: welcomeCachedImgLogo
        source: welcomeCachedImgLogo
        color: JamiTheme.globalBackgroundColor
        opacity: JamiTheme.welcomePageColorOverlayOpacity
        z: -1
        visible: AccountSettingsManager.accountSettingsPropertyMap.backgroundScrimEnabled && welcomeCachedImgLogo.visible
    }

    Rectangle {
        anchors.fill: parent

        color: JamiTheme.secondaryBackgroundColor
        opacity: mainViewReady ? 0.0 : 1.0

        visible: opacity > 0

        Image {
            id: jamiLogo

            anchors.centerIn: parent

            width: 100
            height: 100

            source: JamiResources.net_jami_jami_svg
        }

        Behavior on opacity {
            NumberAnimation {
                duration: JamiTheme.shortFadeDuration
                easing.type: Easing.InOutQuad
            }
        }
    }

    Loader {
        id: mainViewLoader
        active: false
        source: "qrc:/mainview/MainView.qml"
        anchors {
            top: parent.top
            left: parent.left
            right: parent.right
            bottom: errorLoader.top
        }
        opacity: mainViewReady ? 1 : 0

        Behavior on opacity {
            NumberAnimation {
                duration: 350
                easing.type: Easing.InOutQuad
            }
        }

        onLoaded: {
            item.appContext = appContext;
            initMainView(item);
        }
    }

    // Use this as a parent for fullscreen items.
    Item {
        id: fullscreenContainer
        anchors.fill: parent
    }

    // QWK: Window Title bar
    Item {
        id: titleBar
        height: JamiTheme.qwkTitleBarHeight
        anchors {
            top: parent.top
            right: parent.right
            left: parent.left
        }

        // On Windows and Linux, use custom system buttons.
        Loader {
            id: sysBtnsLoader
            active: Qt.platform.os.toString() !== "osx" && useFrameless
            height: titleBar.height
            anchors {
                top: parent.top
                right: parent.right
                // Note: leave these margins, they prevent image scaling artifacts
                topMargin: 1
                rightMargin: 1
            }
            source: "qrc:/commoncomponents/QWKSystemButtonGroup.qml"
        }
    }

    // QWK: Main interop component.
    WindowAgent {
        id: windowAgent
    }

    Connections {
        target: LRCInstance

        function onSelectedConvUidChanged() {
            const newConvId = LRCInstance.selectedConvUid;
            if (AppSettingsManager.getValue(Settings.AutoPipCallOnNavAway)
                    && appWindow._prevConvForAutoPip.length > 0
                    && appWindow._prevConvForAutoPip !== newConvId
                    && !CallPipWindowManager.isPipActive
                    && CallPipWindowManager.convHasActiveCall(appWindow._prevConvForAutoPip,
                                                              appWindow._prevAccountForAutoPip)) {
                CallPipWindowManager.popOutCall(appWindow._prevConvForAutoPip,
                                                appWindow._prevAccountForAutoPip);
            }
            appWindow._prevConvForAutoPip = newConvId;
            appWindow._prevAccountForAutoPip = LRCInstance.currentAccountId;
        }

        function onRestoreAppRequested() {
            requestActivate();
            layoutManager.restoreApp();
        }

        function onNotificationClicked() {
            requestActivate();
            raise();
            layoutManager.restoreApp();
        }

        function onCurrentAccountRemoved() {
            if (UtilsAdapter.getAccountListSize() === 0) {
                viewCoordinator.present("WizardView");
            }
        }
    }

    Connections {
        target: UtilsAdapter
        function onRaiseWhenCalledChanged() {
            raiseWhenCalled = AppSettingsManager.getValue(Settings.RaiseWhenCalled);
        }
    }

    Connections {
        target: CallAdapter

        function onCallStatusChanged(index, accountId, convUid) {
            //If we are starting a call with raiseWhenCalled activated
            if (raiseWhenCalled && index === Call.Status.INCOMING_RINGING) {
                appWindow.raise();
                appWindow.requestActivate();
                layoutManager.restoreApp();
            }
        }
    }

    Connections {
        target: MainApplication

        function onAboutToQuit() {
            // Save minor pane width on app exit
            if (JamiQmlUtils.currentMinorPaneWidth) {
                UtilsAdapter.setAppValue("minorPaneWidth", JamiQmlUtils.currentMinorPaneWidth);
            }
            cleanupMainView();
        }

        function onCloseRequested() {
            closeOrMinimize(true);
        }

        function onSearchAndSelect(request) {
            ConversationsAdapter.setFilterAndSelect(request);
        }
    }

    Connections {
        target: {
            if (Qt.platform.os.toString() !== "windows" && Qt.platform.os.toString() !== "osx")
                return DBusErrorHandler;
            return null;
        }
        ignoreUnknownSignals: true

        function onShowDaemonReconnectPopup(visible) {
            if (visible) {
                viewCoordinator.presentDialog(appWindow, "commoncomponents/DaemonReconnectPopup.qml");
            }
        }
    }

    function presentUpdateInfoDialog(infoText) {
        return viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
            "title": JamiStrings.updateDialogTitle,
            "infoText": infoText,
            "buttonTitles": [JamiStrings.optionOk],
            "buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue],
            "buttonCallBacks": [],
            "buttonRoles": [DialogButtonBox.AcceptRole]
        });
    }

    function presentUpdateConfirmInstallDialog(switchToBeta = false) {
        return viewCoordinator.presentDialog(appWindow, "commoncomponents/SimpleMessageDialog.qml", {
            "title": JamiStrings.updateDialogTitle,
            "infoText": switchToBeta ? JamiStrings.confirmBeta : JamiStrings.updateFound,
            "buttonTitles": [JamiStrings.optionUpgrade, JamiStrings.optionLater],
            "buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlue],
            "buttonCallBacks": [function () {
                    AppVersionManager.applyUpdates(switchToBeta);
                }],
            "buttonRoles": [DialogButtonBox.AcceptRole, DialogButtonBox.RejectRole]
        });
    }

    function translateErrorToString(error) {
        switch (error) {
        case NetworkManager.DISCONNECTED:
            return JamiStrings.networkDisconnected;
        case NetworkManager.CONTENT_NOT_FOUND:
            return JamiStrings.contentNotFoundError;
        case NetworkManager.ACCESS_DENIED:
            return JamiStrings.accessError;
        case NetworkManager.SSL_ERROR:
            return JamiStrings.updateSSLError;
        case NetworkManager.CANCELED:
            return JamiStrings.updateDownloadCanceled;
        case NetworkManager.NETWORK_ERROR:
        default:
            return JamiStrings.updateNetworkError;
        }
    }

    Connections {
        target: AppVersionManager

        function onDownloadStarted() {
            viewCoordinator.presentDialog(appWindow, "settingsview/components/UpdateDownloadDialog.qml", {
                "title": JamiStrings.updateDialogTitle
            });
        }

        function onUpdateCheckReplyReceived(ok, found) {
            if (!ok) {
                // Show an error dialog describing that an update check failed.
                presentUpdateInfoDialog(JamiStrings.updateCheckError);
                return;
            }
            if (!found) {
                // Show a dialog describing that no update was found.
                presentUpdateInfoDialog(JamiStrings.updateNotFound);
            } else {
                // Show a dialog describing that an update were found, and offering to install it.
                presentUpdateConfirmInstallDialog();
            }
        }

        function onNetworkErrorOccurred(error) {
            var errorStr = translateErrorToString(error);
            presentUpdateInfoDialog(errorStr);
        }
    }

    // QtCreator may falsely report this with:
    // "Invalid property name". See QTCREATORBUG-13347
    onClosing: function(event) {
        event.accepted = false;

        if (UtilsAdapter.getAppValue(Settings.MinimizeOnClose)) {
            // Pop out any active call into PiP so it survives the minimize.
            // popOutFirstActiveCall() is a no-op if PiP is already active or there is no call.
            CallPipWindowManager.popOutFirstActiveCall();
        } else {
            CallPipWindowManager.closeAll();
        }

        closeOrMinimize();
    }

    // Capture the inputs to the main window while the File Dialog is open
    // This is used to mitigate modality issues on Ubuntu 22.04 systems that use wayland.
    Loader {
        active: JamiQmlUtils.openFileDialogCount > 0
        sourceComponent: Popup {
            modal: true
            visible: true
            closePolicy: Popup.NoAutoClose
            width: appWindow.width
            height: appWindow.height
            background: Rectangle {
                color: "#80808080"  // Semi-transparent grey
            }
        }
    }
}
