<?php

declare(strict_types=1);

namespace Duplicator\Addons\ProBase;

use Duplicator\Addons\ProBase\ComponentsPlus\ComponentsPlusHooks;
use Duplicator\Addons\ProBase\ComponentsPlus\ProComponentStats;
use Duplicator\Addons\ProBase\License\License;
use Duplicator\Addons\ProBase\Models\LicenseData;
use Duplicator\Core\MigrationMng;
use Duplicator\Installer\Core\InstState;
use Duplicator\Models\TemplateEntity;
use Duplicator\Package\Create\BuildComponents;
use Duplicator\Models\Storages\AbstractStorageEntity;
use Duplicator\Models\Storages\Local\LocalStorage;
use Duplicator\Utils\UsageStatistics\PluginData;

/**
 * ProBase usage statistics contribution
 */
class ProUsageStats
{
    /**
     * Register hooks
     *
     * @return void
     */
    public static function init(): void
    {
        add_filter('duplicator_usage_stats_plugin_identifier', [self::class, 'getPluginIdentifier']);
        add_filter('duplicator_usage_stats_endpoint', [self::class, 'getEndpoint']);
        add_filter('duplicator_usage_stats_license_type', [self::class, 'getLicenseType']);
        add_filter('duplicator_usage_stats_license_status', [self::class, 'getLicenseStatus']);
        add_filter('duplicator_usage_stats_plugin_data', [self::class, 'addProPluginData'], 10, 2);
        add_filter('duplicator_usage_stats_sanitize_rules', [self::class, 'addSanitizeRules']);
    }

    /**
     * Override plugin identifier to dup-pro
     *
     * @return string
     */
    public static function getPluginIdentifier(): string
    {
        return 'dup-pro';
    }

    /**
     * Override endpoint to pro stats
     *
     * @return string
     */
    public static function getEndpoint(): string
    {
        return '/api/ustats/addProStats';
    }

    /**
     * Add sanitize rules for pro-specific fields
     *
     * @param array<string, string> $rules Existing rules
     *
     * @return array<string, string>
     */
    public static function addSanitizeRules(array $rules): array
    {
        $rules['email']   = '?string|max:255';
        $rules['api_key'] = '?string|max:32';

        return $rules;
    }

    /**
     * Return license type string from current license
     *
     * @return string
     */
    public static function getLicenseType(): string
    {
        return self::getLicenseTypeFromNumeric('', License::getType());
    }

    /**
     * Return license status string
     *
     * @return string
     */
    public static function getLicenseStatus(): string
    {
        switch (License::getLicenseStatus()) {
            case LicenseData::STATUS_VALID:
                return 'valid';
            case LicenseData::STATUS_INVALID:
                return 'invalid';
            case LicenseData::STATUS_INACTIVE:
                return 'inactive';
            case LicenseData::STATUS_DISABLED:
                return 'disabled';
            case LicenseData::STATUS_SITE_INACTIVE:
                return 'site-inactive';
            case LicenseData::STATUS_EXPIRED:
                return 'expired';
            case LicenseData::STATUS_UNKNOWN:
            default:
                return 'unknown';
        }
    }

    /**
     * Map numeric license type to string label
     *
     * @param string $default     Default value (from filter)
     * @param int    $numericType Numeric license type
     *
     * @return string
     */
    public static function getLicenseTypeFromNumeric(string $default, int $numericType): string
    {
        switch ($numericType) {
            case License::TYPE_PERSONAL:
            case License::TYPE_PERSONAL_AUTO:
                return 'personal';
            case License::TYPE_FREELANCER:
            case License::TYPE_FREELANCER_AUTO:
                return 'freelancer';
            case License::TYPE_BUSINESS:
            case License::TYPE_BUSINESS_AUTO:
                return 'business';
            case License::TYPE_GOLD:
                return 'gold';
            case License::TYPE_BASIC:
                return 'basic';
            case License::TYPE_PLUS:
                return 'plus';
            case License::TYPE_PRO:
                return 'pro';
            case License::TYPE_ELITE:
                return 'elite';
            case License::TYPE_ENTERPRISE:
                return 'enterprise';
            case License::TYPE_UNLICENSED:
            case License::TYPE_UNKNOWN:
            default:
                return 'unlicensed';
        }
    }

    /**
     * Add all pro-specific data to the plugin stats payload
     *
     * @param array<string, mixed> $data       Current stats data
     * @param PluginData           $pluginData PluginData instance
     *
     * @return array<string, mixed>
     */
    public static function addProPluginData(array $data, PluginData $pluginData): array
    {
        $data = array_merge($data, self::getManualBuildCompInfos($pluginData));
        $data = array_merge($data, self::getStoragesInfos());
        $data = array_merge($data, self::getTemplatesInfos());
        $data = array_merge($data, self::getOtherInfos());

        return array_merge($data, self::getNonAnonymousInfos());
    }

    /**
     * Return manual build component breakdown
     *
     * @param PluginData $pluginData PluginData instance
     *
     * @return array<string, mixed>
     */
    private static function getManualBuildCompInfos(PluginData $pluginData): array
    {
        $compStats = ProComponentStats::getInstance();

        return [
            'packages_build_comp_full_count'               => $pluginData->getPackagesBuildCompFullCount(),
            'packages_build_comp_dbonly_count'             => $pluginData->getPackagesBuildCompDbOnlyCount(),
            'packages_build_comp_mdonly_count'             => $compStats->getMdOnlyCount(),
            'packages_build_comp_custom_count'             => $compStats->getCustomCount(),
            'packages_build_comp_custom_only_active_count' => $compStats->getCustomOnlyActiveCount(),
        ];
    }

    /**
     * Return storages infos
     *
     * @return array<string, mixed>
     */
    private static function getStoragesInfos(): array
    {
        $result = [
            'storages_local_count'         => 0,
            'storages_onedrive_count'      => 0,
            'storages_s3_count'            => 0,
            'storages_s3_compatible_count' => 0,
            'storages_backblaze_count'     => 0,
            'storages_tot_count'           => 0,
        ];

        if (($storages = AbstractStorageEntity::getAll()) === false) {
            $storages = [];
        }

        foreach ($storages as $index => $storage) {
            if ($index === 0) {
                continue;
            }

            if ($storage->getSType() == LocalStorage::getSType()) {
                $result['storages_local_count']++;
            }
        }

        $result = apply_filters('duplicator_usage_stats_storages_infos', $result);

        $total = 0;
        foreach ($result as $value) {
            $total += $value;
        }
        $result['storages_tot_count'] = $total;

        return $result;
    }

    /**
     * Return template infos
     *
     * @return array<string, mixed>
     */
    private static function getTemplatesInfos(): array
    {
        $result = [
            'can_use_adv_components'             => License::can(License::FEATURE_PACKAGE_COMPONENTS_PLUS),
            'package_manual_create_component'    => 'full',
            'templates_full_count'               => 0,
            'templates_dbonly_count'             => 0,
            'templates_monly_count'              => 0,
            'templates_custom_count'             => 0,
            'templates_custom_only_active_count' => 0,
            'templates_tot_count'                => 0,
        ];

        $manualTpl = TemplateEntity::getManualTemplate();
        switch (BuildComponents::getActionFromComponents($manualTpl->components)) {
            case BuildComponents::COMP_ACTION_ALL:
                $result['package_manual_create_component'] = 'full';
                break;
            case BuildComponents::COMP_ACTION_DB:
                $result['package_manual_create_component'] = 'dbonly';
                break;
            case ComponentsPlusHooks::COMP_ACTION_MEDIA:
                $result['package_manual_create_component'] = 'mediaonly';
                break;
            case ComponentsPlusHooks::COMP_ACTION_CUSTOM:
                if (
                    array_intersect(
                        $manualTpl->components,
                        [
                            BuildComponents::COMP_PLUGINS_ACTIVE,
                            BuildComponents::COMP_THEMES_ACTIVE,
                        ]
                    )
                ) {
                    $result['package_manual_create_component'] = 'custom_only_active';
                } else {
                    $result['package_manual_create_component'] = 'custom';
                }
                break;
        }

        if (($templates = TemplateEntity::getAllWithoutManualMode()) === false) {
            $templates = [];
        }

        foreach ($templates as $tpl) {
            switch (BuildComponents::getActionFromComponents($tpl->components)) {
                case BuildComponents::COMP_ACTION_ALL:
                    $result['templates_full_count']++;
                    break;
                case BuildComponents::COMP_ACTION_DB:
                    $result['templates_dbonly_count']++;
                    break;
                case ComponentsPlusHooks::COMP_ACTION_MEDIA:
                    $result['templates_monly_count']++;
                    break;
                case ComponentsPlusHooks::COMP_ACTION_CUSTOM:
                    if (
                        array_intersect(
                            $tpl->components,
                            [
                                BuildComponents::COMP_PLUGINS_ACTIVE,
                                BuildComponents::COMP_THEMES_ACTIVE,
                            ]
                        )
                    ) {
                        $result['templates_custom_only_active_count']++;
                    } else {
                        $result['templates_custom_count']++;
                    }
                    break;
            }
            $result['templates_tot_count']++;
        }

        return $result;
    }

    /**
     * Return other infos (recovery, branding, migration)
     *
     * @return array<string, mixed>
     */
    private static function getOtherInfos(): array
    {
        $migrateData = MigrationMng::getMigrationData();
        return [
            'is_recovery_point_set'     => (apply_filters('duplicator_recovery_package_id', false) !== false),
            'used_recovery_point_count' => PluginData::getInstance()->getUsedRecoveryCount(),
            'branding_count'            => 0,
            'is_recovered_site'         => $migrateData->recoveryMode,
            'is_migrated_site'          => ($migrateData->installType !== InstState::TYPE_NOT_SET),
        ];
    }

    /**
     * Return non-anonymous infos (email + license key)
     *
     * @return array<string, mixed>
     */
    private static function getNonAnonymousInfos(): array
    {
        return [
            'email'   => get_bloginfo('admin_email'),
            'api_key' => (strlen(License::getLicenseKey()) ? License::getLicenseKey() : null),
        ];
    }
}
