<?php

/**
 * Kunena Privacy Plugin
 *
 * @package         Kunena.Plugins
 * @subpackage      Privacy
 *
 * @copyright       Copyright (C) 2023 - 2026 Kunena Team. All rights reserved.
 * @license         https://www.gnu.org/copyleft/gpl.html GNU/GPL
 * @link            https://www.kunena.org
 **/

namespace Kunena\Forum\Plugin\Privacy\Kunena\Extension;

// No direct access
\defined('_JEXEC') or die;

use Joomla\CMS\Event\Privacy\CanRemoveDataEvent;
use Joomla\CMS\Event\Privacy\CollectCapabilitiesEvent;
use Joomla\CMS\Event\Privacy\ExportRequestEvent;
use Joomla\CMS\Event\Privacy\RemoveDataEvent;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\Component\Privacy\Administrator\Export\Domain;
use Joomla\Component\Privacy\Administrator\Plugin\PrivacyPlugin;
use Joomla\Component\Privacy\Administrator\Removal\Status;
use Joomla\Database\DatabaseAwareInterface;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Database\ParameterType;
use Joomla\Event\SubscriberInterface;
use Kunena\Forum\Plugin\Privacy\Kunena\Helper\KunenaHelper;

/**
 * Plug-in to prevent Registering with Disposable Email Addresses
 *
 * @since  6.1.0
 */
class Kunena extends PrivacyPlugin implements SubscriberInterface, DatabaseAwareInterface
{
    use DatabaseAwareTrait;

    /**
     * Returns an array of events this subscriber will listen to.
     *
     * The array keys are event names and the value can be:
     *
     *  - The method name to call (priority defaults to 0)
     *  - An array composed of the method name to call and the priority
     *
     * @return  array
     * @since   Kunena 7.0.0
     */
    public static function getSubscribedEvents(): array
    {
        $app     = Factory::getApplication();
        $mapping = [];

        if ($app->isClient('site') || $app->isClient('administrator')) {
            $mapping['onPrivacyCanRemoveData']            = 'onPrivacyCanRemoveData';
            $mapping['onPrivacyExportRequest']            = 'onPrivacyExportRequest';
            $mapping['onPrivacyRemoveData']               = 'onPrivacyRemoveData';
            $mapping['onPrivacyCollectAdminCapabilities'] = 'onPrivacyCollectAdminCapabilities';

            if ($app->isClient('site')) {
                // Only allowed in the frontend
            } elseif ($app->isClient('administrator')) {
                // Only allowed in the backend
            }
        }

        return $mapping;
    }

    /**
     * Performs validation to determine if the data associated with a remove information request can be processed
     *
     * This event will not allow a super user account to be removed
     *
     * @param   CanRemoveDataEvent  $event  The request event
     *
     * @return  void
     *
     * @since   3.9.0
     */
    public function onPrivacyCanRemoveData(CanRemoveDataEvent $event)
    {
        $user   = $event->getUser();
        $status = new Status();

        if (!$user) {
            $event->addResult($status);
            return;
        }

        if ($user->authorise('core.admin')) {
            $status->canRemove = false;
            $status->reason    = Text::_('PLG_PRIVACY_USER_ERROR_CANNOT_REMOVE_SUPER_USER');
        }

        $event->addResult($status);
    }

    /**
     * Processes an export request for Kunena user data
     *
     * This event will collect data for the following Kunena tables:
     *
     * - #__kunena_announcement
     * - #__kunena_attachments
     * - #__kunena_logs
     * - #__kunena_messages > #__kunena_messages_text
     * - #__kunena_polls_users > #__kunena_polls
     * - #__kunena_private
     * - #__kunena_rate
     * - #__kunena_thankyou
     * - #__kunena_topics
     * - #__kunena_users
     * - #__kunena_user_banned
     *
     * @param   ExportRequestEvent  $event  The request event
     *
     * @return  void
     *
     * @since   3.9.0
     */
    public function onPrivacyExportRequest(ExportRequestEvent $event)
    {
        $user = $event->getUser();

        if (!$user) {
            return;
        }

        $domains = [];
        $domains[] = $this->createKunenaUserDomain($user->id);
        $domains[] = $this->createKunenaAnnouncementDomain($user->id);
        $domains[] = $this->createKunenaAttachmentsDomain($user->id);
        $domains[] = $this->createKunenaLogsDomain($user->id);
        $domains[] = $this->createKunenaMessagesDomain($user->id);
        $domains[] = $this->createKunenaPollsDomain($user->id);
        $domains[] = $this->createKunenaPrivateDomain($user->id);
        $domains[] = $this->createKunenaRateDomain($user->id);
        $domains[] = $this->createKunenaThankyouDomain($user->id);
        $domains[] = $this->createKunenaTopicsDomain($user->id);
        $domains[] = $this->createKunenaUserbannedDomain($user->id);

        $event->addResult($domains);
    }

    /**
     * Removes the data associated with a remove information request
     *
     * This event will pseudoanonymise the user data
     *
     * @param   RemoveDataEvent  $event  The remove data event
     *
     * @return  void
     *
     * @since   3.9.0
     */
    public function onPrivacyRemoveData(RemoveDataEvent $event)
    {
        $user = $event->getUser();

        // This plugin only processes data for registered user accounts
        if (!$user) {
            return;
        }

        $anonymizedname = 'User ID ' . $user->id;

        // Anomynize User name in messages
        KunenaHelper::anomynizeUserData('#__kunena_messages', 'userid', $user->id, 'name', $anonymizedname);
        KunenaHelper::anomynizeUserData('#__kunena_messages', 'userid', $user->id, 'ip', '');

        // Anomynize User name in topics
        KunenaHelper::anomynizeUserData('#__kunena_topics', 'first_post_userid', $user->id, 'first_post_guest_name', $anonymizedname);
        KunenaHelper::anomynizeUserData('#__kunena_topics', 'last_post_userid', $user->id, 'last_post_guest_name', $anonymizedname);

        // Delete Kunena User Profile
        KunenaHelper::deleteUserData('#__kunena_users', 'userid', $user->id);

        // Delete User Category / Topic subscriptions, User Topic / Message Read status
        KunenaHelper::deleteUserData('#__kunena_user_categories', 'user_id', $user->id);
        KunenaHelper::deleteUserData('#__kunena_user_topics', 'user_id', $user->id);
        KunenaHelper::deleteUserData('#__kunena_user_read', 'user_id', $user->id);

        // Delete miscelanious
        KunenaHelper::deleteUserData('#__kunena_logs', 'user_id', $user->id);
        KunenaHelper::deleteUserData('#__kunena_logs', 'target_user', $user->id);
        KunenaHelper::deleteUserData('#__kunena_rate', 'userid', $user->id);
        KunenaHelper::deleteUserData('#__kunena_thankyou', 'userid', $user->id);
        KunenaHelper::deleteUserData('#__kunena_thankyou', 'targetuserid', $user->id);
        KunenaHelper::deleteUserData('#__kunena_users_banned', 'userid', $user->id);
    }

    /**
     * Adds the Kunena Privacy Information to Joomla Privacy plugin.
     * 
     * @param   CollectCapabilitiesEvent  $event  Event instance
     *
     * @return  void
     *
     * @since   6.1.0
     */
    public function onPrivacyCollectAdminCapabilities(CollectCapabilitiesEvent $event): void
    {
        $event->addResult(
            [
                'Kunena Privacy' => [
                    Text::_('PLG_PRIVACY_KUNENA_CAPABILITY_EMAIL'),
                    Text::_('PLG_PRIVACY_KUNENA_CAPABILITY_IP_ADDRESS'),
                    Text::_('PLG_PRIVACY_KUNENA_CAPABILITY_USERPROFILE'),
                    Text::_('PLG_PRIVACY_KUNENA_CAPABILITY_POSTS'),
                    Text::_('PLG_PRIVACY_KUNENA_CAPABILITY_RATINGS'),
                    Text::_('PLG_PRIVACY_KUNENA_CAPABILITY_STATISTICS'),
                    Text::_('PLG_PRIVACY_KUNENA_CAPABILITY_COOKIES'),
                    Text::_('PLG_PRIVACY_KUNENA_CAPABILITY_LOGS'),
                    Text::_('PLG_PRIVACY_KUNENA_CAPABILITY_SOCIAL'),
                ],
            ]
        );
    }

    /**
     * Create the domain for the user data
     *
     * @param   int  $userid  The User ID
     *
     * @return  Domain
     *
     * @since   6.1.0
     */
    private function createKunenaUserDomain(int $userid)
    {
        $domain   = $this->createDomain('kunena_user', 'kunena_user_data');
        $redacted = [];
        $excluded = [];
        $item     = KunenaHelper::getUserItems('#__kunena_users', 'userid', $userid, true);
        $data     = KunenaHelper::processUserData($item, $excluded, $redacted);

        $domain->addItem($this->createItemFromArray($data, $userid));

        return $domain;
    }

    /**
     * Create the domain for the user announcement data
     *
     * @param   int  $userid  The User ID
     *
     * @return  Domain
     *
     * @since   6.1.0
     */
    private function createKunenaAnnouncementDomain(int $userid)
    {
        $domain   = $this->createDomain('kunena_user_announcement', 'kunena_user_announcement_data');
        $redacted = [];
        $excluded = [];
        $items    = KunenaHelper::getUserItems('#__kunena_announcement', 'created_by', $userid);

        foreach ($items as $item) {
            $data = KunenaHelper::processUserData($item, $excluded, $redacted);

            $domain->addItem($this->createItemFromArray($data, $data['id']));
        }

        return $domain;
    }

    /**
     * Create the domain for the user attachments data
     *
     * @param   int  $userid  The User ID
     *
     * @return  Domain
     *
     * @since   6.1.0
     */
    private function createKunenaAttachmentsDomain(int $userid)
    {
        $domain   = $this->createDomain('kunena_user_attachments', 'kunena_user_attachments_data');
        $redacted = [];
        $excluded = [];
        $items    = KunenaHelper::getUserItems('#__kunena_attachments', 'userid', $userid);

        foreach ($items as $item) {
            $data = KunenaHelper::processUserData($item, $excluded, $redacted);

            $domain->addItem($this->createItemFromArray($data, $data['id']));
        }

        return $domain;
    }

    /**
     * Create the domain for the user logs data
     *
     * @param   int  $userid  The User ID
     *
     * @return  Domain
     *
     * @since   6.1.0
     */
    private function createKunenaLogsDomain(int $userid)
    {
        $domain   = $this->createDomain('kunena_user_logs', 'kunena_user_logs_data');
        $redacted = [];
        $excluded = [];
        $items    = KunenaHelper::getUserItems('#__kunena_logs', 'user_id', $userid);

        foreach ($items as $item) {
            $data = KunenaHelper::processUserData($item, $excluded, $redacted);

            $domain->addItem($this->createItemFromArray($data, $data['id']));
        }

        return $domain;
    }

    /**
     * Create the domain for the user messages data
     *
     * @param   int  $userid  The User ID
     *
     * @return  Domain
     *
     * @since   6.1.0
     */
    private function createKunenaMessagesDomain(int $userid)
    {
        $domain   = $this->createDomain('kunena_user_messages', 'kunena_user_messages_data');
        $db       = $this->getDatabase();
        $redacted = [];
        $excluded = [];

        $query = $db->createQuery()
            ->select(['m.*', 't.message'])
            ->from($db->quoteName('#__kunena_messages', 'm'))
            ->join('LEFT', $db->quoteName('#__kunena_messages_text', 't') . ' ON ' . $db->quoteName('m.id') . ' = ' . $db->quoteName('t.mesid'))
            ->where($db->quoteName('m.userid') . ' = :userid')
            ->bind(':userid', $userid, ParameterType::INTEGER);

        $items = $db->setQuery($query)->loadAssocList();

        foreach ($items as $item) {
            $data = KunenaHelper::processUserData($item, $excluded, $redacted);

            $domain->addItem($this->createItemFromArray($data, $data['id']));
        }

        return $domain;
    }

    /**
     * Create the domain for the user polls data
     *
     * @param   int  $userid  The User ID
     *
     * @return  Domain
     *
     * @since   6.1.0
     */
    private function createKunenaPollsDomain(int $userid)
    {
        $domain   = $this->createDomain('kunena_user_polls', 'kunena_user_polls_data');
        $db       = $this->getDatabase();
        $redacted = [];
        $excluded = [];

        $query = $db->createQuery()
            ->select(['u.*', 'p.title'])
            ->from($db->quoteName('#__kunena_polls_users', 'u'))
            ->join('LEFT', $db->quoteName('#__kunena_polls', 'p') . ' ON ' . $db->quoteName('u.pollid') . ' = ' . $db->quoteName('p.id'))
            ->where($db->quoteName('u.userid') . ' = :userid')
            ->bind(':userid', $userid, ParameterType::INTEGER);

        $items = $db->setQuery($query)->loadAssocList();

        foreach ($items as $item) {
            $data = KunenaHelper::processUserData($item, $excluded, $redacted);

            $domain->addItem($this->createItemFromArray($data, $data['id']));
        }

        return $domain;
    }

    /**
     * Create the domain for the user private data
     *
     * @param   int  $userid  The User ID
     *
     * @return  Domain
     *
     * @since   6.1.0
     */
    private function createKunenaPrivateDomain(int $userid)
    {
        $domain   = $this->createDomain('kunena_user_private', 'kunena_user_private_data');
        $redacted = [];
        $excluded = ['params'];
        $items    = KunenaHelper::getUserItems('#__kunena_private', 'author_id', $userid);

        foreach ($items as $item) {
            $data = KunenaHelper::processUserData($item, $excluded, $redacted);

            $domain->addItem($this->createItemFromArray($data, $data['id']));
        }

        return $domain;
    }

    /**
     * Create the domain for the user rate data
     *
     * @param   int  $userid  The User ID
     *
     * @return  Domain
     *
     * @since   6.1.0
     */
    private function createKunenaRateDomain(int $userid)
    {
        $domain   = $this->createDomain('kunena_user_rate', 'kunena_user_rate_data');
        $redacted = [];
        $excluded = [];
        $items    = KunenaHelper::getUserItems('#__kunena_rate', 'userid', $userid);

        foreach ($items as $item) {
            $data = KunenaHelper::processUserData($item, $excluded, $redacted);

            $domain->addItem($this->createItemFromArray($data, $data['id']));
        }

        return $domain;
    }

    /**
     * Create the domain for the user thankyou data
     *
     * @param   int  $userid  The User ID
     *
     * @return  Domain
     *
     * @since   6.1.0
     */
    private function createKunenaThankyouDomain(int $userid)
    {
        $domain   = $this->createDomain('kunena_user_thankyou', 'kunena_user_thankyou_data');
        $redacted = [];
        $excluded = ['targetuserid'];
        $items    = KunenaHelper::getUserItems('#__kunena_thankyou', 'userid', $userid);

        foreach ($items as $item) {
            $data = KunenaHelper::processUserData($item, $excluded, $redacted);

            $domain->addItem($this->createItemFromArray($data, $data['postid']));
        }

        return $domain;
    }

    /**
     * Create the domain for the user topics data
     *
     * @param   int  $userid  The User ID
     *
     * @return  Domain
     *
     * @since   6.1.0
     */
    private function createKunenaTopicsDomain(int $userid)
    {
        $domain   = $this->createDomain('kunena_user_topics', 'kunena_user_topics_data');
        $redacted = [];
        $excluded = ['first_post_message', 'last_post_message', 'last_post_userid', 'last_post_guest_name', 'params'];
        $items    = KunenaHelper::getUserItems('#__kunena_topics', 'first_post_userid', $userid);

        foreach ($items as $item) {
            $data = KunenaHelper::processUserData($item, $excluded, $redacted);

            $domain->addItem($this->createItemFromArray($data, $data['id']));
        }

        return $domain;
    }

    /**
     * Create the domain for the user banned data
     *
     * @param   int  $userid  The User ID
     *
     * @return  Domain
     *
     * @since   6.1.0
     */
    private function createKunenaUserbannedDomain(int $userid)
    {
        $domain   = $this->createDomain('kunena_user_banned', 'kunena_user_banned_data');
        $redacted = [];
        $excluded = ['created_by', 'reason_private', 'modified_by', 'modified_time', 'params'];
        $items    = KunenaHelper::getUserItems('#__kunena_users_banned', 'userid', $userid);

        foreach ($items as $item) {
            $data = KunenaHelper::processUserData($item, $excluded, $redacted);

            $domain->addItem($this->createItemFromArray($data, $data['id']));
        }

        return $domain;
    }
}
