import pytest
from sqlalchemy import delete, func, select

from core.db.session_factory import session_factory
from models import Tenant
from models.account import TenantPluginAutoUpgradeStrategy, TenantPluginPermission
from services.plugin.plugin_auto_upgrade_service import PluginAutoUpgradeService
from services.plugin.plugin_permission_service import PluginPermissionService


@pytest.fixture
def tenant(flask_req_ctx):
    with session_factory.create_session() as session:
        t = Tenant(name="plugin_it_tenant")
        session.add(t)
        session.commit()
        tenant_id = t.id

    yield tenant_id

    with session_factory.create_session() as session:
        session.execute(delete(TenantPluginPermission).where(TenantPluginPermission.tenant_id == tenant_id))
        session.execute(
            delete(TenantPluginAutoUpgradeStrategy).where(TenantPluginAutoUpgradeStrategy.tenant_id == tenant_id)
        )
        session.execute(delete(Tenant).where(Tenant.id == tenant_id))
        session.commit()


class TestPluginPermissionLifecycle:
    def test_get_returns_none_for_new_tenant(self, tenant):
        assert PluginPermissionService.get_permission(tenant) is None

    def test_change_creates_row(self, tenant):
        result = PluginPermissionService.change_permission(
            tenant,
            TenantPluginPermission.InstallPermission.ADMINS,
            TenantPluginPermission.DebugPermission.EVERYONE,
        )
        assert result is True

        perm = PluginPermissionService.get_permission(tenant)
        assert perm is not None
        assert perm.install_permission == TenantPluginPermission.InstallPermission.ADMINS
        assert perm.debug_permission == TenantPluginPermission.DebugPermission.EVERYONE

    def test_change_updates_existing_row(self, tenant):
        PluginPermissionService.change_permission(
            tenant,
            TenantPluginPermission.InstallPermission.ADMINS,
            TenantPluginPermission.DebugPermission.NOBODY,
        )
        PluginPermissionService.change_permission(
            tenant,
            TenantPluginPermission.InstallPermission.EVERYONE,
            TenantPluginPermission.DebugPermission.ADMINS,
        )
        perm = PluginPermissionService.get_permission(tenant)
        assert perm is not None
        assert perm.install_permission == TenantPluginPermission.InstallPermission.EVERYONE
        assert perm.debug_permission == TenantPluginPermission.DebugPermission.ADMINS

        with session_factory.create_session() as session:
            count = session.scalar(
                select(func.count())
                .select_from(TenantPluginPermission)
                .where(TenantPluginPermission.tenant_id == tenant)
            )
        assert count == 1


class TestPluginAutoUpgradeLifecycle:
    def test_get_returns_none_for_new_tenant(self, tenant):
        assert PluginAutoUpgradeService.get_strategy(tenant) is None

    def test_change_creates_row(self, tenant):
        result = PluginAutoUpgradeService.change_strategy(
            tenant,
            strategy_setting=TenantPluginAutoUpgradeStrategy.StrategySetting.LATEST,
            upgrade_time_of_day=3,
            upgrade_mode=TenantPluginAutoUpgradeStrategy.UpgradeMode.ALL,
            exclude_plugins=[],
            include_plugins=[],
        )
        assert result is True

        strategy = PluginAutoUpgradeService.get_strategy(tenant)
        assert strategy is not None
        assert strategy.strategy_setting == TenantPluginAutoUpgradeStrategy.StrategySetting.LATEST
        assert strategy.upgrade_time_of_day == 3

    def test_change_updates_existing_row(self, tenant):
        PluginAutoUpgradeService.change_strategy(
            tenant,
            strategy_setting=TenantPluginAutoUpgradeStrategy.StrategySetting.FIX_ONLY,
            upgrade_time_of_day=0,
            upgrade_mode=TenantPluginAutoUpgradeStrategy.UpgradeMode.ALL,
            exclude_plugins=[],
            include_plugins=[],
        )
        PluginAutoUpgradeService.change_strategy(
            tenant,
            strategy_setting=TenantPluginAutoUpgradeStrategy.StrategySetting.LATEST,
            upgrade_time_of_day=12,
            upgrade_mode=TenantPluginAutoUpgradeStrategy.UpgradeMode.PARTIAL,
            exclude_plugins=[],
            include_plugins=["plugin-a"],
        )

        strategy = PluginAutoUpgradeService.get_strategy(tenant)
        assert strategy is not None
        assert strategy.strategy_setting == TenantPluginAutoUpgradeStrategy.StrategySetting.LATEST
        assert strategy.upgrade_time_of_day == 12
        assert strategy.upgrade_mode == TenantPluginAutoUpgradeStrategy.UpgradeMode.PARTIAL
        assert strategy.include_plugins == ["plugin-a"]

    def test_exclude_plugin_creates_strategy_when_none_exists(self, tenant):
        PluginAutoUpgradeService.exclude_plugin(tenant, "my-plugin")

        strategy = PluginAutoUpgradeService.get_strategy(tenant)
        assert strategy is not None
        assert strategy.upgrade_mode == TenantPluginAutoUpgradeStrategy.UpgradeMode.EXCLUDE
        assert "my-plugin" in strategy.exclude_plugins

    def test_exclude_plugin_appends_in_exclude_mode(self, tenant):
        PluginAutoUpgradeService.change_strategy(
            tenant,
            strategy_setting=TenantPluginAutoUpgradeStrategy.StrategySetting.FIX_ONLY,
            upgrade_time_of_day=0,
            upgrade_mode=TenantPluginAutoUpgradeStrategy.UpgradeMode.EXCLUDE,
            exclude_plugins=["existing"],
            include_plugins=[],
        )
        PluginAutoUpgradeService.exclude_plugin(tenant, "new-plugin")

        strategy = PluginAutoUpgradeService.get_strategy(tenant)
        assert strategy is not None
        assert "existing" in strategy.exclude_plugins
        assert "new-plugin" in strategy.exclude_plugins

    def test_exclude_plugin_dedup_in_exclude_mode(self, tenant):
        PluginAutoUpgradeService.change_strategy(
            tenant,
            strategy_setting=TenantPluginAutoUpgradeStrategy.StrategySetting.FIX_ONLY,
            upgrade_time_of_day=0,
            upgrade_mode=TenantPluginAutoUpgradeStrategy.UpgradeMode.EXCLUDE,
            exclude_plugins=["same-plugin"],
            include_plugins=[],
        )
        PluginAutoUpgradeService.exclude_plugin(tenant, "same-plugin")

        strategy = PluginAutoUpgradeService.get_strategy(tenant)
        assert strategy is not None
        assert strategy.exclude_plugins.count("same-plugin") == 1

    def test_exclude_from_partial_mode_removes_from_include(self, tenant):
        PluginAutoUpgradeService.change_strategy(
            tenant,
            strategy_setting=TenantPluginAutoUpgradeStrategy.StrategySetting.FIX_ONLY,
            upgrade_time_of_day=0,
            upgrade_mode=TenantPluginAutoUpgradeStrategy.UpgradeMode.PARTIAL,
            exclude_plugins=[],
            include_plugins=["p1", "p2"],
        )
        PluginAutoUpgradeService.exclude_plugin(tenant, "p1")

        strategy = PluginAutoUpgradeService.get_strategy(tenant)
        assert strategy is not None
        assert "p1" not in strategy.include_plugins
        assert "p2" in strategy.include_plugins

    def test_exclude_from_all_mode_switches_to_exclude(self, tenant):
        PluginAutoUpgradeService.change_strategy(
            tenant,
            strategy_setting=TenantPluginAutoUpgradeStrategy.StrategySetting.LATEST,
            upgrade_time_of_day=0,
            upgrade_mode=TenantPluginAutoUpgradeStrategy.UpgradeMode.ALL,
            exclude_plugins=[],
            include_plugins=[],
        )
        PluginAutoUpgradeService.exclude_plugin(tenant, "excluded-plugin")

        strategy = PluginAutoUpgradeService.get_strategy(tenant)
        assert strategy is not None
        assert strategy.upgrade_mode == TenantPluginAutoUpgradeStrategy.UpgradeMode.EXCLUDE
        assert "excluded-plugin" in strategy.exclude_plugins
