<?php
/**
 * @author 1902 Software
 * @copyright Copyright © 2023 1902 Software (https://1902software.com/magento/)
 * @package WriteTextAI_WriteTextAI
 */

namespace WriteTextAI\WriteTextAI\Setup;

use Magento\Framework\Setup\UpgradeDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Ui\Model\BookmarkFactory;
use Magento\Authorization\Model\RoleFactory;
use Magento\Authorization\Model\RulesFactory;
use Magento\Authorization\Model\UserContextInterface;
use Magento\Authorization\Model\Acl\Role\Group as RoleGroup;
use Magento\Authorization\Model\ResourceModel\Role\CollectionFactory as RoleCollectionFactory;

class UpgradeData implements UpgradeDataInterface
{
    public const CATEGORY_GRID_LISTING = 'wtai_categories_grid_listing';

    public const PRODUCT_GRID_LISTING = 'wtai_products_grid_listing';

    public const ADMIN_ROLE = 'WriteText.ai Administrator';

    public const EDITOR_ROLE = 'WriteText.ai Editor';

    public const CONTRIBUTOR_ROLE = 'WriteText.ai Contributor';

    public const VIEWER_ROLE = 'WriteText.ai Viewer';

    /**
     * @var BookmarkFactory
     */
    private $bookmarkFactory;

    /**
     * @var RoleFactory
     */
    private $roleFactory;

    /**
     * @var RulesFactory
     */
    private $rulesFactory;

    /**
     * @var RoleCollectionFactory
     */
    private $roleCollectionFactory;

    /**
     * Constructor
     *
     * @param BookmarkFactory $bookmarkFactory
     * @param RoleFactory $roleFactory
     * @param RulesFactory $rulesFactory
     * @param RoleCollectionFactory $roleCollectionFactory
     */
    public function __construct(
        BookmarkFactory $bookmarkFactory,
        RoleFactory $roleFactory,
        RulesFactory $rulesFactory,
        RoleCollectionFactory $roleCollectionFactory
    ) {
        $this->bookmarkFactory = $bookmarkFactory;
        $this->roleFactory = $roleFactory;
        $this->rulesFactory = $rulesFactory;
        $this->roleCollectionFactory = $roleCollectionFactory;
    }

    /**
     * Upgrade
     *
     * @param ModuleDataSetupInterface $setup
     * @param ModuleContextInterface $context
     *
     * @return void
     */
    public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();

        $this->resetListingFilters();
        $this->forceExpireUserWebTokens($setup);
        $this->refetchApiSettings($setup);
        $this->manageUserRoles($setup);

        $setup->endSetup();
    }

    /**
     * Reset listing filters for grids
     *
     * @return void
     */
    private function resetListingFilters()
    {
        $bookmarkCollection = $this->bookmarkFactory->create()->getCollection();
        $bookmarkCollection->addFieldToFilter('namespace', ['in' => [
            self::CATEGORY_GRID_LISTING,
            self::PRODUCT_GRID_LISTING
        ]]);
        $bookmarkCollection->walk('delete');
    }

    /**
     * Force expiration of all user web tokens
     *
     * @param ModuleDataSetupInterface $setup
     * @return void
     */
    private function forceExpireUserWebTokens(ModuleDataSetupInterface $setup)
    {
        $setup->getConnection()->truncateTable(
            $setup->getTable('writetextai_writetextai_user_web_token')
        );
    }

    /**
     * Refetch API settings by clearing the settings table
     *
     * @param ModuleDataSetupInterface $setup
     * @return void
     */
    private function refetchApiSettings(ModuleDataSetupInterface $setup)
    {
        $setup->getConnection()->truncateTable(
            $setup->getTable('writetextai_writetextai_settings')
        );
    }

    /**
     * Manage WriteTextAI user roles - create, update, and cleanup
     *
     * @param ModuleDataSetupInterface $setup
     * @return void
     */
    private function manageUserRoles(ModuleDataSetupInterface $setup)
    {
        $this->createWriteTextAdmin($setup);
        $this->createWriteTextEditor($setup);
        $this->createWriteTextContributor($setup);
        $this->createWriteTextViewer($setup);
        $this->ensureCorrectRoleRules($setup);
        $this->removeDuplicateRoles($setup);
    }

    /**
     * Check if role exists by name
     *
     * @param string $roleName
     * @return bool
     */
    private function roleExists(string $roleName): bool
    {
        $roleCollection = $this->roleCollectionFactory->create();
        $roleCollection->addFieldToFilter('role_name', $roleName);
        return $roleCollection->getSize() > 0;
    }

    /**
     * Create WriteText.ai Administrator Role
     *
     * @param ModuleDataSetupInterface $setup
     * @return void
     */
    private function createWriteTextAdmin(ModuleDataSetupInterface $setup)
    {
        if ($this->roleExists(self::ADMIN_ROLE)) {
            return;
        }

        $role = $this->roleFactory->create();
        $role->setName(self::ADMIN_ROLE)
            ->setPid(0)
            ->setRoleType(RoleGroup::ROLE_TYPE)
            ->setUserType(UserContextInterface::USER_TYPE_ADMIN);
        $role->save();

        $rolePermissions = $this->getRolePermissions();
        $resource = $rolePermissions[self::ADMIN_ROLE];

        $this->rulesFactory->create()
            ->setRoleId($role->getId())
            ->setResources($resource)
            ->saveRel();
    }

    /**
     * Create WriteText.ai Editor Role
     *
     * @param ModuleDataSetupInterface $setup
     * @return void
     */
    private function createWriteTextEditor(ModuleDataSetupInterface $setup)
    {
        if ($this->roleExists(self::EDITOR_ROLE)) {
            return;
        }

        $role = $this->roleFactory->create();
        $role->setName(self::EDITOR_ROLE)
            ->setPid(0)
            ->setRoleType(RoleGroup::ROLE_TYPE)
            ->setUserType(UserContextInterface::USER_TYPE_ADMIN);
        $role->save();

        $rolePermissions = $this->getRolePermissions();
        $resource = $rolePermissions[self::EDITOR_ROLE];

        $this->rulesFactory->create()
            ->setRoleId($role->getId())
            ->setResources($resource)
            ->saveRel();
    }

    /**
     * Create WriteText.ai Contributor Role
     *
     * @param ModuleDataSetupInterface $setup
     * @return void
     */
    private function createWriteTextContributor(ModuleDataSetupInterface $setup)
    {
        if ($this->roleExists(self::CONTRIBUTOR_ROLE)) {
            return;
        }

        $role = $this->roleFactory->create();
        $role->setName(self::CONTRIBUTOR_ROLE)
            ->setPid(0)
            ->setRoleType(RoleGroup::ROLE_TYPE)
            ->setUserType(UserContextInterface::USER_TYPE_ADMIN);
        $role->save();

        $rolePermissions = $this->getRolePermissions();
        $resource = $rolePermissions[self::CONTRIBUTOR_ROLE];

        $this->rulesFactory->create()
            ->setRoleId($role->getId())
            ->setResources($resource)
            ->saveRel();
    }

    /**
     * Create WriteText.ai Viewer Role
     *
     * @param ModuleDataSetupInterface $setup
     * @return void
     */
    private function createWriteTextViewer(ModuleDataSetupInterface $setup)
    {
        if ($this->roleExists(self::VIEWER_ROLE)) {
            return;
        }

        $role = $this->roleFactory->create();
        $role->setName(self::VIEWER_ROLE)
            ->setPid(0)
            ->setRoleType(RoleGroup::ROLE_TYPE)
            ->setUserType(UserContextInterface::USER_TYPE_ADMIN);
        $role->save();

        $rolePermissions = $this->getRolePermissions();
        $resource = $rolePermissions[self::VIEWER_ROLE];

        $this->rulesFactory->create()
            ->setRoleId($role->getId())
            ->setResources($resource)
            ->saveRel();
    }

    /**
     * Ensure all WriteTextAI roles have the correct rules/permissions
     *
     * @param ModuleDataSetupInterface $setup
     * @return void
     */
    private function ensureCorrectRoleRules(ModuleDataSetupInterface $setup)
    {
        $rolePermissions = $this->getRolePermissions();

        foreach ($rolePermissions as $roleName => $expectedResources) {
            $roleCollection = $this->roleCollectionFactory->create();
            $roleCollection->addFieldToFilter('role_name', $roleName);
            
            $role = $roleCollection->getFirstItem();
            if ($role && $role->getId()) {
                // Check if current rules match expected rules
                if (!$this->doRulesMatch($role->getId(), $expectedResources, $setup)) {
                    // Only delete and recreate if rules don't match
                    $connection = $setup->getConnection();
                    $connection->delete(
                        $setup->getTable('authorization_rule'),
                        ['role_id = ?' => $role->getId()]
                    );
                    
                    // Create new rules with correct permissions
                    $this->rulesFactory->create()
                        ->setRoleId($role->getId())
                        ->setResources($expectedResources)
                        ->saveRel();
                }
            }
        }
    }

    /**
     * Check if existing rules match expected resources
     *
     * @param int $roleId
     * @param array $expectedResources
     * @param ModuleDataSetupInterface $setup
     * @return bool
     */
    private function doRulesMatch($roleId, $expectedResources, ModuleDataSetupInterface $setup)
    {
        $connection = $setup->getConnection();
        $select = $connection->select()
            ->from($setup->getTable('authorization_rule'), ['resource_id'])
            ->where('role_id = ?', $roleId);
        
        $currentResources = $connection->fetchCol($select);
        
        // Sort both arrays for comparison
        sort($currentResources);
        sort($expectedResources);
        
        // Compare arrays
        return $currentResources === $expectedResources;
    }

    /**
     * Get role permissions mapping
     *
     * @return array
     */
    private function getRolePermissions()
    {
        return [
            self::ADMIN_ROLE => [
                'WriteTextAI_WriteTextAI::menu',
                'WriteTextAI_WriteTextAI::transfer',
                'WriteTextAI_WriteTextAI::review',
                'WriteTextAI_WriteTextAI::edit',
                'WriteTextAI_WriteTextAI::product_details',
                'WriteTextAI_WriteTextAI::modify_defaults',
                'WriteTextAI_WriteTextAI::modify_attributes',
                'WriteTextAI_WriteTextAI::modify_tone_style',
                'WriteTextAI_WriteTextAI::modify_words',
                'WriteTextAI_WriteTextAI::generate',
                'WriteTextAI_WriteTextAI::keyword_analysis'
            ],
            self::EDITOR_ROLE => [
                'WriteTextAI_WriteTextAI::menu',
                'WriteTextAI_WriteTextAI::transfer',
                'WriteTextAI_WriteTextAI::review',
                'WriteTextAI_WriteTextAI::edit',
                'WriteTextAI_WriteTextAI::product_details',
                'WriteTextAI_WriteTextAI::modify_attributes',
                'WriteTextAI_WriteTextAI::modify_tone_style',
                'WriteTextAI_WriteTextAI::modify_words',
                'WriteTextAI_WriteTextAI::generate',
                'WriteTextAI_WriteTextAI::keyword_analysis'
            ],
            self::CONTRIBUTOR_ROLE => [
                'WriteTextAI_WriteTextAI::menu',
                'WriteTextAI_WriteTextAI::edit',
                'WriteTextAI_WriteTextAI::product_details',
                'WriteTextAI_WriteTextAI::modify_attributes',
                'WriteTextAI_WriteTextAI::modify_tone_style',
                'WriteTextAI_WriteTextAI::modify_words',
                'WriteTextAI_WriteTextAI::generate',
                'WriteTextAI_WriteTextAI::keyword_analysis'
            ],
            self::VIEWER_ROLE => [
                'WriteTextAI_WriteTextAI::menu'
            ]
        ];
    }

    /**
     * Remove duplicate roles by keeping only the most recent one for each role name
     *
     * @param ModuleDataSetupInterface $setup
     * @return void
     */
    private function removeDuplicateRoles(ModuleDataSetupInterface $setup)
    {
        $roleNames = [
            self::ADMIN_ROLE,
            self::EDITOR_ROLE,
            self::CONTRIBUTOR_ROLE,
            self::VIEWER_ROLE
        ];

        foreach ($roleNames as $roleName) {
            $roleCollection = $this->roleCollectionFactory->create();
            $roleCollection->addFieldToFilter('role_name', $roleName);
            $roleCollection->setOrder('role_id', 'DESC'); // Most recent first

            $roles = $roleCollection->getItems();
            
            if (count($roles) > 1) {
                // Keep the first (most recent) role and delete the rest
                $keepRole = array_shift($roles);
                
                foreach ($roles as $duplicateRole) {
                    try {
                        // Delete the rules associated with this role first
                        $connection = $setup->getConnection();
                        $connection->delete(
                            $setup->getTable('authorization_rule'),
                            ['role_id = ?' => $duplicateRole->getId()]
                        );
                        
                        // Delete the duplicate role
                        $duplicateRole->delete();
                    } catch (\Exception $e) {
                        // Log error but continue with other duplicates
                        continue;
                    }
                }
            }
        }
    }
}
