#!/usr/bin/env python

# 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 zipfile

import mozprofile
import mozunit
import pytest
from addon_stubs import generate_addon

here = os.path.dirname(os.path.abspath(__file__))


@pytest.fixture
def profile():
    return mozprofile.Profile()


@pytest.fixture
def am(profile):
    return profile.addons


def test_install_multiple_same_source(tmpdir, am):
    path = tmpdir.strpath

    # Generate installer stubs for all possible types of addons
    addon_xpi = generate_addon("test-addon-1@mozilla.org", path=path)
    addon_folder = generate_addon("test-addon-1@mozilla.org", path=path, xpi=False)

    # The same folder should not be installed twice
    am.install([addon_folder, addon_folder])
    assert am.installed_addons == [addon_folder]
    am.clean()

    # The same XPI file should not be installed twice
    am.install([addon_xpi, addon_xpi])
    assert am.installed_addons == [addon_xpi]
    am.clean()

    # Even if it is the same id the add-on should be installed twice, if
    # specified via XPI and folder
    am.install([addon_folder, addon_xpi])
    assert len(am.installed_addons) == 2
    assert addon_folder in am.installed_addons
    assert addon_xpi in am.installed_addons
    am.clean()


def test_install_webextension_from_dir(tmpdir, am):
    tmpdir = tmpdir.strpath

    addon = os.path.join(here, "addons", "apply-css.xpi")
    zipped = zipfile.ZipFile(addon)
    try:
        zipped.extractall(tmpdir)
    finally:
        zipped.close()
    am.install(tmpdir)
    assert len(am.installed_addons) == 1
    assert os.path.isdir(am.installed_addons[0])


def test_install_webextension(am):
    addon = os.path.join(here, "addons", "apply-css.xpi")

    am.install(addon)
    assert len(am.installed_addons) == 1
    assert os.path.isfile(am.installed_addons[0])
    assert "apply-css.xpi" == os.path.basename(am.installed_addons[0])

    details = am.addon_details(am.installed_addons[0])
    assert "test-webext@quality.mozilla.org" == details["id"]


def test_install_webextension_id_via_browser_specific_settings(am):
    # See Bug 1572404
    addon = os.path.join(
        here, "addons", "apply-css-id-via-browser-specific-settings.xpi"
    )
    am.install(addon)
    assert len(am.installed_addons) == 1
    assert os.path.isfile(am.installed_addons[0])
    assert "apply-css-id-via-browser-specific-settings.xpi" == os.path.basename(
        am.installed_addons[0]
    )

    details = am.addon_details(am.installed_addons[0])
    assert "test-webext@quality.mozilla.org" == details["id"]


def test_install_webextension_sans_id(am):
    addon = os.path.join(here, "addons", "apply-css-sans-id.xpi")
    am.install(addon)

    assert len(am.installed_addons) == 1
    assert os.path.isfile(am.installed_addons[0])
    assert "apply-css-sans-id.xpi" == os.path.basename(am.installed_addons[0])

    details = am.addon_details(am.installed_addons[0])
    assert "@temporary-addon" in details["id"]


def test_install_xpi(tmpdir, am):
    tmpdir = tmpdir.strpath

    addons_to_install = []
    addons_installed = []

    # Generate installer stubs and install them
    for ext in ["test-addon-1@mozilla.org", "test-addon-2@mozilla.org"]:
        temp_addon = generate_addon(ext, path=tmpdir)
        addons_to_install.append(am.addon_details(temp_addon)["id"])
        am.install(temp_addon)

    # Generate a list of addons installed in the profile
    addons_installed = [
        str(x[: -len(".xpi")])
        for x in os.listdir(os.path.join(am.profile, "extensions"))
    ]
    assert addons_to_install.sort() == addons_installed.sort()


def test_install_folder(tmpdir, am):
    tmpdir = tmpdir.strpath

    # Generate installer stubs for all possible types of addons
    addons = []
    addons.append(generate_addon("test-addon-1@mozilla.org", path=tmpdir))
    addons.append(generate_addon("test-addon-2@mozilla.org", path=tmpdir, xpi=False))
    addons.append(
        generate_addon("test-addon-3@mozilla.org", path=tmpdir, name="addon-3")
    )
    addons.append(
        generate_addon(
            "test-addon-4@mozilla.org", path=tmpdir, name="addon-4", xpi=False
        )
    )
    addons.sort()

    am.install(tmpdir)

    assert am.installed_addons == addons


def test_install_unpack(tmpdir, am):
    tmpdir = tmpdir.strpath

    # Generate installer stubs for all possible types of addons
    addon_xpi = generate_addon("test-addon-unpack@mozilla.org", path=tmpdir)
    addon_folder = generate_addon(
        "test-addon-unpack@mozilla.org", path=tmpdir, xpi=False
    )
    addon_no_unpack = generate_addon("test-addon-1@mozilla.org", path=tmpdir)

    # Test unpack flag for add-on as XPI
    am.install(addon_xpi)
    assert am.installed_addons == [addon_xpi]
    am.clean()

    # Test unpack flag for add-on as folder
    am.install(addon_folder)
    assert am.installed_addons == [addon_folder]
    am.clean()

    # Test forcing unpack an add-on
    am.install(addon_no_unpack, unpack=True)
    assert am.installed_addons == [addon_no_unpack]
    am.clean()


def test_install_after_reset(tmpdir, profile):
    tmpdir = tmpdir.strpath
    am = profile.addons

    # Installing the same add-on after a reset should not cause a failure
    addon = generate_addon("test-addon-1@mozilla.org", path=tmpdir, xpi=False)

    # We cannot use am because profile.reset() creates a new instance
    am.install(addon)
    profile.reset()

    am.install(addon)
    assert am.installed_addons == [addon]


def test_install_backup(tmpdir, am):
    tmpdir = tmpdir.strpath

    staged_path = os.path.join(am.profile, "extensions")

    # Generate installer stubs for all possible types of addons
    addon_xpi = generate_addon("test-addon-1@mozilla.org", path=tmpdir)
    addon_folder = generate_addon("test-addon-1@mozilla.org", path=tmpdir, xpi=False)
    addon_name = generate_addon(
        "test-addon-1@mozilla.org", path=tmpdir, name="test-addon-1-dupe@mozilla.org"
    )

    # Test backup of xpi files
    am.install(addon_xpi)
    assert am.backup_dir is None

    am.install(addon_xpi)
    assert am.backup_dir is not None
    assert os.listdir(am.backup_dir) == ["test-addon-1@mozilla.org.xpi"]

    am.clean()
    assert os.listdir(staged_path) == ["test-addon-1@mozilla.org.xpi"]
    am.clean()

    # Test backup of folders
    am.install(addon_folder)
    assert am.backup_dir is None

    am.install(addon_folder)
    assert am.backup_dir is not None
    assert os.listdir(am.backup_dir) == ["test-addon-1@mozilla.org"]

    am.clean()
    assert os.listdir(staged_path) == ["test-addon-1@mozilla.org"]
    am.clean()

    # Test backup of xpi files with another file name
    am.install(addon_name)
    assert am.backup_dir is None

    am.install(addon_xpi)
    assert am.backup_dir is not None
    assert os.listdir(am.backup_dir) == ["test-addon-1@mozilla.org.xpi"]

    am.clean()
    assert os.listdir(staged_path) == ["test-addon-1@mozilla.org.xpi"]
    am.clean()


def test_install_invalid_addons(tmpdir, am):
    tmpdir = tmpdir.strpath

    # Generate installer stubs for all possible types of addons
    addons = []
    addons.append(
        generate_addon(
            "test-addon-invalid-no-manifest@mozilla.org", path=tmpdir, xpi=False
        )
    )
    addons.append(generate_addon("test-addon-invalid-no-id@mozilla.org", path=tmpdir))

    am.install(tmpdir)

    assert am.installed_addons == []


@pytest.mark.xfail(reason="feature not implemented as part of AddonManger")
def test_install_error(am):
    """Check install raises an error with an invalid addon"""
    temp_addon = generate_addon("test-addon-invalid-version@mozilla.org")
    # This should raise an error here
    with pytest.raises(Exception):
        am.install(temp_addon)


def test_addon_details(tmpdir, am):
    tmpdir = tmpdir.strpath

    # Generate installer stubs for a valid and invalid add-on manifest
    valid_addon = generate_addon("test-addon-1@mozilla.org", path=tmpdir)
    invalid_addon = generate_addon(
        "test-addon-invalid-not-wellformed@mozilla.org", path=tmpdir
    )

    # Check valid add-on
    details = am.addon_details(valid_addon)
    assert details["id"] == "test-addon-1@mozilla.org"
    assert details["name"] == "Test Add-on 1"
    assert not details["unpack"]
    assert details["version"] == "0.1"

    # Check invalid add-on
    with pytest.raises(mozprofile.addons.AddonFormatError):
        am.addon_details(invalid_addon)

    # Check invalid path
    with pytest.raises(IOError):
        am.addon_details("")

    # Check invalid add-on format
    addon_path = os.path.join(os.path.join(here, "files"), "not_an_addon.txt")
    with pytest.raises(mozprofile.addons.AddonFormatError):
        am.addon_details(addon_path)


def test_clean_addons(am):
    addon_one = generate_addon("test-addon-1@mozilla.org")
    addon_two = generate_addon("test-addon-2@mozilla.org")

    am.install(addon_one)
    installed_addons = [
        str(x[: -len(".xpi")])
        for x in os.listdir(os.path.join(am.profile, "extensions"))
    ]

    # Create a new profile based on an existing profile
    # Install an extra addon in the new profile
    # Cleanup addons
    duplicate_profile = mozprofile.profile.Profile(profile=am.profile, addons=addon_two)
    duplicate_profile.addons.clean()

    addons_after_cleanup = [
        str(x[: -len(".xpi")])
        for x in os.listdir(os.path.join(duplicate_profile.profile, "extensions"))
    ]
    # New addons installed should be removed by clean_addons()
    assert installed_addons == addons_after_cleanup


def test_noclean(tmpdir):
    """test `restore=True/False` functionality"""
    profile = tmpdir.mkdtemp().strpath
    tmpdir = tmpdir.mkdtemp().strpath

    # empty initially
    assert not bool(os.listdir(profile))

    # make an addon
    addons = [
        generate_addon("test-addon-1@mozilla.org", path=tmpdir),
        os.path.join(here, "addons", "empty.xpi"),
    ]

    # install it with a restore=True AddonManager
    am = mozprofile.addons.AddonManager(profile, restore=True)

    for addon in addons:
        am.install(addon)

    # now its there
    assert os.listdir(profile) == ["extensions"]
    staging_folder = os.path.join(profile, "extensions")
    assert os.path.exists(staging_folder)
    assert len(os.listdir(staging_folder)) == 2

    del am

    assert os.listdir(profile) == ["extensions"]
    assert os.path.exists(staging_folder)
    assert os.listdir(staging_folder) == []


def test_remove_addon(tmpdir, am):
    tmpdir = tmpdir.strpath

    addons = []
    addons.append(generate_addon("test-addon-1@mozilla.org", path=tmpdir))
    addons.append(generate_addon("test-addon-2@mozilla.org", path=tmpdir))

    am.install(tmpdir)

    extensions_path = os.path.join(am.profile, "extensions")
    staging_path = os.path.join(extensions_path)

    for addon in am._addons:
        am.remove_addon(addon)

    assert os.listdir(staging_path) == []
    assert os.listdir(extensions_path) == []


if __name__ == "__main__":
    mozunit.main()
