# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import os
import sys
import tempfile
from pathlib import Path

import mozfile

sys.path.append(os.fspath(Path(__file__).parents[0]))
from backup_test_base import BackupTestBase


class BackupReplaceCurrentProfileTest(BackupTestBase):
    """
    Tests for recovering backups with replaceCurrentProfile=true.

    This includes:
    1. Verifying the old profile is deleted from the database
    2. Verifying metadata (name, avatar, theme) is copied when recovering
       a legacy backup into a selectable profile environment
    """

    def test_replace_current_profile(self):
        """
        Tests that recovering with replaceCurrentProfile=true deletes the
        current profile from the database.
        """
        self.logger.info("=== Test: Replace Current Profile ===")

        self.logger.info("Step 1: Setting up source selectable profile")
        profile_name = self.register_profile_and_restart()
        self._cleanups.append({"profile_name": profile_name})
        self.logger.info(f"Registered toolkit profile: {profile_name}")

        selectable_info = self.setup_selectable_profile()
        original_profile_id = selectable_info["id"]
        original_profile_path = selectable_info["path"]
        self.logger.info(
            f"Source profile: id={original_profile_id}, path={original_profile_path}"
        )

        self.marionette.restart(clean=False, in_app=True)

        self.logger.info("Step 2: Creating backup")
        self._archive_path = self.create_backup()
        self._cleanups.append({"path": self._archive_path})
        self.assertTrue(
            os.path.exists(self._archive_path), "Backup archive should exist"
        )
        self.logger.info(f"Backup created at: {self._archive_path}")

        self.logger.info("Step 3: Getting profiles before recovery")
        profiles_before = self.get_all_profiles()
        self.logger.info(f"Profiles before recovery: {profiles_before}")

        self.logger.info("Step 4: Recovering backup with replaceCurrentProfile=true")
        self._recovery_path = os.path.join(
            tempfile.gettempdir(), "replace-profile-recovery"
        )
        mozfile.remove(self._recovery_path)
        self._cleanups.append({"path": self._recovery_path})

        new_profile_root = self.run_async(
            """
            let newProfileRoot = await IOUtils.createUniqueDirectory(
                PathUtils.tempDir, "replaceProfileTest"
            );
            return newProfileRoot;
            """
        )
        self._cleanups.append({"path": new_profile_root})
        self.logger.info(f"Created profile root for recovery: {new_profile_root}")

        self.run_async(
            """
            const { BackupService } = ChromeUtils.importESModule(
                "resource:///modules/backup/BackupService.sys.mjs"
            );
            let [archivePath, recoveryPath, profileRoot] = arguments;
            let bs = BackupService.get();
            bs.deleteAndQuitCurrentSelectableProfile = async () => null;
            await bs.recoverFromBackupArchive(
                archivePath, null, false, recoveryPath, profileRoot, true
            );
            """,
            script_args=[self._archive_path, self._recovery_path, new_profile_root],
        )

        self.logger.info("Step 5: Launching recovered profile to verify it works")
        recovered_profile_dirs = os.listdir(new_profile_root)
        self.assertEqual(
            len(recovered_profile_dirs), 1, "Should have exactly one recovered profile"
        )
        recovered_profile_path = os.path.join(
            new_profile_root, recovered_profile_dirs[0]
        )
        self.logger.info(f"Recovered profile path: {recovered_profile_path}")
        self._new_profile_path = recovered_profile_path

        self.marionette.quit()
        self.marionette.instance.profile = recovered_profile_path
        self.marionette.start_session()
        self.marionette.set_context("chrome")

        self.wait_for_post_recovery()
        self.logger.info("Post-recovery complete")

        self.init_selectable_profile_service()
        self.assert_profile_source("backup")

        self.logger.info("Step 6: Cleaning up")
        self.cleanup_selectable_profiles()
        self.logger.info("=== Test: Replace Current Profile PASSED ===")

    def test_replace_current_profile_legacy_backup_copies_metadata(self):
        """
        Tests that recovering a legacy backup with replaceCurrentProfile=true
        copies the current profile's metadata (name, avatar, theme) to the
        new profile.
        """
        self.logger.info(
            "=== Test: Replace Current Profile - Legacy Backup Copies Metadata ==="
        )

        self.logger.info("Step 1: Creating legacy backup (no selectable profiles)")
        profile_name = self.register_profile_and_restart()
        self._cleanups.append({"profile_name": profile_name})

        self.set_prefs({
            "browser.profiles.enabled": False,
            "browser.profiles.created": False,
        })

        has_selectable = self.has_selectable_profiles()
        self.assertFalse(has_selectable, "Should be a legacy profile")
        self.logger.info("Verified profile is legacy")

        self._archive_path = self.create_backup()
        self._cleanups.append({"path": self._archive_path})
        self.assertTrue(
            os.path.exists(self._archive_path), "Backup archive should exist"
        )
        self.logger.info(f"Legacy backup created at: {self._archive_path}")

        self.logger.info(
            "Step 2: Setting up destination selectable profile with custom metadata"
        )
        self.marionette.quit()
        self.marionette.instance.switch_profile()
        self.marionette.start_session()
        self.marionette.set_context("chrome")

        recovery_profile_name = self.register_profile_and_restart()
        self._cleanups.append({"profile_name": recovery_profile_name})

        self.setup_selectable_profile()

        custom_name = "My Custom Profile"
        custom_avatar = "book"
        custom_theme = {
            "themeId": "default-theme@mozilla.org",
            "themeBg": "#ff5500",
            "themeFg": "#ffffff",
        }
        self.run_async(
            """
            const { SelectableProfileService } = ChromeUtils.importESModule(
                "resource:///modules/profiles/SelectableProfileService.sys.mjs"
            );
            let profile = SelectableProfileService.currentProfile;
            profile.name = arguments[0];
            profile.setAvatar(arguments[1]);
            profile.theme = arguments[2];
            """,
            script_args=[custom_name, custom_avatar, custom_theme],
        )
        self.logger.info(
            f"Set custom metadata: name={custom_name}, avatar={custom_avatar}"
        )

        self.logger.info(
            "Step 3: Recovering legacy backup with replaceCurrentProfile=true"
        )
        self._recovery_path = os.path.join(
            tempfile.gettempdir(), "legacy-replace-profile-recovery"
        )
        mozfile.remove(self._recovery_path)
        self._cleanups.append({"path": self._recovery_path})

        new_profile_root = self.run_async(
            """
            let newProfileRoot = await IOUtils.createUniqueDirectory(
                PathUtils.tempDir, "legacyReplaceTest"
            );
            return newProfileRoot;
            """
        )
        self._cleanups.append({"path": new_profile_root})
        self.logger.info(f"Created profile root for recovery: {new_profile_root}")

        self.run_async(
            """
            const { BackupService } = ChromeUtils.importESModule(
                "resource:///modules/backup/BackupService.sys.mjs"
            );
            let [archivePath, recoveryPath, profileRoot] = arguments;
            let bs = BackupService.get();
            bs.deleteAndQuitCurrentSelectableProfile = async () => null;
            await bs.recoverFromBackupArchive(
                archivePath, null, false, recoveryPath, profileRoot, true
            );
            """,
            script_args=[self._archive_path, self._recovery_path, new_profile_root],
        )

        self.logger.info("Step 4: Launching recovered profile to verify metadata")
        recovered_profile_dirs = os.listdir(new_profile_root)
        self.assertEqual(
            len(recovered_profile_dirs), 1, "Should have exactly one recovered profile"
        )
        recovered_profile_path = os.path.join(
            new_profile_root, recovered_profile_dirs[0]
        )
        self.logger.info(f"Recovered profile path: {recovered_profile_path}")
        self._new_profile_path = recovered_profile_path

        self.marionette.quit()
        self.marionette.instance.profile = recovered_profile_path
        self.marionette.start_session()
        self.marionette.set_context("chrome")

        self.wait_for_post_recovery()
        self.logger.info("Post-recovery complete")

        self.logger.info("Step 5: Verifying recovered profile has copied metadata")
        self.init_selectable_profile_service()
        self.assert_profile_source("backup")

        recovered_profile_metadata = self.run_async(
            """
            const { SelectableProfileService } = ChromeUtils.importESModule(
                "resource:///modules/profiles/SelectableProfileService.sys.mjs"
            );
            const { FileUtils } = ChromeUtils.importESModule(
                "resource://gre/modules/FileUtils.sys.mjs"
            );

            let filePath = new FileUtils.File(arguments[0]);
            let profile = await SelectableProfileService.getProfileByPath(filePath);
            if (!profile) return null;
            return {
                id: profile.id,
                name: profile.name,
                avatar: profile.avatar,
                theme: profile.theme
            };
            """,
            script_args=[recovered_profile_path],
        )

        self.assertIsNotNone(
            recovered_profile_metadata, "Should have recovered profile metadata"
        )
        self.assertEqual(
            recovered_profile_metadata["name"],
            custom_name,
            "Recovered profile should have the old profile's name",
        )
        self.assertEqual(
            recovered_profile_metadata["avatar"],
            custom_avatar,
            "Recovered profile should have the old profile's avatar",
        )
        self.logger.info(
            f"Verified metadata was copied: name={recovered_profile_metadata['name']}, "
            f"avatar={recovered_profile_metadata['avatar']}"
        )

        self.logger.info("Step 6: Cleaning up")
        self.cleanup_selectable_profiles()
        self.logger.info(
            "=== Test: Replace Current Profile - Legacy Backup Copies Metadata PASSED ==="
        )
