Current File : /home/digitaw/www/wp-content/plugins/fluentform/app/Services/Report/ReportHelper.php
<?php

namespace FluentForm\App\Services\Report;

use FluentForm\App\Helpers\Helper;
use FluentForm\App\Models\EntryDetails;
use FluentForm\App\Models\Form;
use FluentForm\App\Models\FormAnalytics;
use FluentForm\App\Models\Log;
use FluentForm\App\Models\Submission;
use FluentForm\App\Modules\Form\FormFieldsParser;
use FluentForm\App\Modules\Payments\PaymentHelper;
use FluentForm\App\Services\Submission\SubmissionService;
use FluentForm\Framework\Helpers\ArrayHelper as Arr;

class ReportHelper
{
    public static function generateReport($form, $statuses = ['read', 'unread', 'unapproved', 'approved', 'declined', 'unconfirmed', 'confirmed'])
    {
        $formInputs = FormFieldsParser::getEntryInputs($form, ['admin_label', 'element', 'options']);
        $inputLabels = FormFieldsParser::getAdminLabels($form, $formInputs);

        $elements = [];
        foreach ($formInputs as $inputName => $input) {
            $elements[$inputName] = $input['element'];
            if ('select_country' == $input['element']) {
                $formInputs[$inputName]['options'] = getFluentFormCountryList();
            }
        }

        $reportableInputs = Helper::getReportableInputs();
        $formReportableInputs = array_intersect($reportableInputs, array_values($elements));
        $reportableInputs = Helper::getSubFieldReportableInputs();
        $formSubFieldInputs = array_intersect($reportableInputs, array_values($elements));


        if (!$formReportableInputs && !$formSubFieldInputs) {
            return [
                'report_items'  => (object)[],
                'total_entries' => static::getEntryCounts($form->id, $statuses),
                'browsers'      => static::getBrowserCounts($form->id, $statuses),
                'devices'       => static::getDeviceCounts($form->id, $statuses),
            ];
        }

        $inputs = [];
        $subfieldInputs = [];
        foreach ($elements as $elementKey => $element) {
            if (in_array($element, $formReportableInputs)) {
                $inputs[$elementKey] = $element;
            }
            if (in_array($element, $formSubFieldInputs)) {
                $subfieldInputs[$elementKey] = $element;
            }
        }

        $reports = static::getInputReport($form->id, array_keys($inputs), $statuses);

        $subFieldReports = static::getSubFieldInputReport($form->id, array_keys($subfieldInputs), $statuses);
        $reports = array_merge($reports, $subFieldReports);
        foreach ($reports as $reportKey => $report) {
            $reports[$reportKey]['label'] = $inputLabels[$reportKey];
            $reports[$reportKey]['element'] = Arr::get($inputs, $reportKey, []);
            $reports[$reportKey]['options'] = $formInputs[$reportKey]['options'];
        }

        return [
            'report_items'  => $reports,
            'total_entries' => static::getEntryCounts($form->id, $statuses),
            'browsers'      => static::getBrowserCounts($form->id, $statuses),
            'devices'       => static::getDeviceCounts($form->id, $statuses),
        ];
    }

    public static function getInputReport(
        $formId,
        $fieldNames,
        $statuses = ['read', 'unread', 'unapproved', 'approved', 'declined', 'unconfirmed', 'confirmed']
    ) {
        if (!$fieldNames) {
            return [];
        }

        $reports = EntryDetails::select(['field_name', 'sub_field_name', 'field_value'])
            ->where('form_id', $formId)
            ->whereIn('field_name', $fieldNames)
            ->when(
                is_array($statuses) && (count($statuses) > 0),
                function ($q) use ($statuses) {
                    return $q->whereHas('submission', function ($q) use ($statuses) {
                        return $q->whereIn('status', $statuses);
                    });
                })
            ->selectRaw('COUNT(field_name) AS total_count')
            ->groupBy(['field_name', 'field_value'])
            ->get();

        $formattedReports = [];
        foreach ($reports as $report) {
            $formattedReports[$report->field_name]['reports'][] = [
                'value'     => Helper::safeUnserialize($report->field_value),
                'count'     => $report->total_count,
                'sub_field' => $report->sub_field_name,
            ];

            $formattedReports[$report->field_name]['total_entry'] = static::getEntryTotal($report->field_name, $formId,
                $statuses);
        }
        if ($formattedReports) {
            //sync with form field order
            $formattedReports = array_replace(array_intersect_key(array_flip($fieldNames), $formattedReports),
                $formattedReports);
        }
        return $formattedReports;
    }

    public static function getSubFieldInputReport($formId, $fieldNames, $statuses)
    {
        if (!$fieldNames) {
            return [];
        }

        $reports = EntryDetails::select(['field_name', 'sub_field_name', 'field_value'])
            ->selectRaw('COUNT(field_name) AS total_count')
            ->where('form_id', $formId)
            ->whereIn('field_name', $fieldNames)
            ->when(
                is_array($statuses) && (count($statuses) > 0),
                function ($q) use ($statuses) {
                    return $q->whereHas('submission', function ($q) use ($statuses) {
                        return $q->whereIn('status', $statuses);
                    });
                })
            ->groupBy(['field_name', 'field_value', 'sub_field_name'])
            ->get()->toArray();
        return static::getFormattedReportsForSubInputs($reports, $formId, $statuses);
    }

    protected static function getFormattedReportsForSubInputs($reports, $formId, $statuses)
    {
        if (!count($reports)) {
            return [];
        }
        $formattedReports = [];
        foreach ($reports as $report) {
            static::setReportForSubInput((array)$report, $formattedReports);
        }
        foreach ($formattedReports as $fieldName => $val) {
            $formattedReports[$fieldName]['total_entry'] = static::getEntryTotal(
                Arr::get($report, 'field_name'),
                $formId,
                $statuses
            );
            $formattedReports[$fieldName]['reports'] = array_values(
                $formattedReports[$fieldName]['reports']
            );
        }
        return $formattedReports;
    }

    protected static function setReportForSubInput($report, &$formattedReports)
    {
        $filedValue = Helper::safeUnserialize(Arr::get($report, 'field_value'));

        if (is_array($filedValue)) {
            foreach ($filedValue as $fVal) {
                static::setReportForSubInput(
                    array_merge($report, ['field_value' => $fVal]),
                    $formattedReports
                );
            }
        } else {
            $value = Arr::get($report, 'sub_field_name') . ' : ' . $filedValue;
            $count = Arr::get($formattedReports, $report['field_name'] . '.reports.' . $value . '.count');
            $count = $count ? $count + Arr::get($report, 'total_count') : Arr::get($report, 'total_count');

            $formattedReports[$report['field_name']]['reports'][$value] = [
                'value'     => $value,
                'count'     => $count,
                'sub_field' => $report['sub_field_name'],
            ];
        }
    }

    public static function getEntryTotal($fieldName, $formId, $statuses = false)
    {
        return EntryDetails::select('id')->where('form_id', $formId)
            ->where('field_name', $fieldName)
            ->when(
                is_array($statuses) && (count($statuses) > 0),
                function ($q) use ($statuses) {
                    return $q->whereHas('submission', function ($q) use ($statuses) {
                        return $q->whereIn('status', $statuses);
                    });
                }
            )
            ->distinct(['field_name', 'submission_id'])
            ->count();
    }

    private static function getEntryCounts($formId, $statuses = false)
    {
        return Submission::where('form_id', $formId)
            ->when(
                is_array($statuses) && (count($statuses) > 0),
                function ($q) use ($statuses) {
                    return $q->whereIn('status', $statuses);
                })
            ->when(!$statuses, function ($q) {
                return $q->where('status', '!=', 'trashed');
            })->count();
    }

    public static function getBrowserCounts($formId, $statuses = false)
    {
        return static::getCounts($formId, 'browser', $statuses);
    }

    public static function getDeviceCounts($formId, $statuses = false)
    {
        return static::getCounts($formId, 'device', $statuses);
    }

    private static function getCounts($formId, $for, $statuses)
    {
        $deviceCounts = Submission::select([
            "$for",
        ])
            ->selectRaw('COUNT(id) as total_count')
            ->where('form_id', $formId)
            ->when(
                is_array($statuses) && (count($statuses) > 0),
                function ($q) use ($statuses) {
                    return $q->whereIn('status', $statuses);
                })
            ->when(!$statuses, function ($q) {
                return $q->where('status', '!=', 'trashed');
            })
            ->groupBy("$for")->get();

        $formattedData = [];
        foreach ($deviceCounts as $deviceCount) {
            $formattedData[$deviceCount->{$for}] = $deviceCount->total_count;
        }
        return $formattedData;
    }

    public static function maybeMigrateData($formId)
    {
        // We have to check if we need to migrate the data
        if ('yes' == Helper::getFormMeta($formId, 'report_data_migrated')) {
            return true;
        }
        // let's migrate the data
        $unmigratedData = Submission::select(['id', 'response'])
            ->where('form_id', $formId)
            ->doesntHave('entryDetails')
            ->get();

        if (!$unmigratedData) {
            return Helper::setFormMeta($formId, 'report_data_migrated', 'yes');
        }
        $submissionService = new SubmissionService();
        foreach ($unmigratedData as $datum) {
            $value = json_decode($datum->response, true);
            $submissionService->recordEntryDetails($datum->id, $formId, $value);
        }
        return true;
    }

    /**
     * Get overview chart data
     */
    public static function getOverviewChartData($startDate, $endDate, $formId, $view)
    {
        // Process and fix date ranges if needed
        list($startDate, $endDate) = self::processDateRange($startDate, $endDate);

        // Calculate date difference to determine grouping
        $startDateTime = new \DateTime($startDate);
        $endDateTime = new \DateTime($endDate);
        $interval = $startDateTime->diff($endDateTime);
        $daysInterval = $interval->days + 1;

        // Determine grouping mode based on date range
        $groupingMode = self::getGroupingMode($daysInterval);
        $data = self::getAggregatedData($startDate, $endDate, $groupingMode, $view, $formId);
        // Get date labels based on grouping mode
        $dateLabels = self::getDateLabels($startDateTime, $endDateTime, $groupingMode);
        // Format the data for the chart
        $chartData = self::formatDataForChart($dateLabels, $data, $formId);
        // Get views data based on the ip
        $views = self::getFormViews($startDate, $endDate, $groupingMode, $formId);
        if ($views) {
            $chartData['values']['views'] = array_values(self::fillMissingData($dateLabels['dates'], $views));
        }
        return $chartData;
    }





    public static function getFormStats($startDate, $endDate, $formId)
    {
        // Process and fix date ranges if needed
        list($startDate, $endDate) = self::processDateRange($startDate, $endDate);

        // Calculate the date range duration to determine previous period
        $startDateTime = new \DateTime($startDate);
        $endDateTime = new \DateTime($endDate);
        $interval = $startDateTime->diff($endDateTime);
        $daysDifference = $interval->days;

        // Calculate previous period dates (same duration, shifted back)
        $previousEndDateTime = clone $startDateTime;
        $previousEndDateTime->modify('-1 day');
        $previousStartDateTime = clone $previousEndDateTime;
        $previousStartDateTime->modify("-{$daysDifference} days");

        $previousStartDate = $previousStartDateTime->format('Y-m-d H:i:s');
        $previousEndDate = $previousEndDateTime->format('Y-m-d H:i:s');

        // Get submission counts
        $periodSubmissions = Submission::whereBetween('created_at', [$startDate, $endDate])
            ->when($formId, function ($q) use ($formId) {
                return $q->where('form_id', $formId);
            })->count();
        $previousPeriodSubmissions = Submission::whereBetween('created_at',
            [$previousStartDate, $previousEndDate])
            ->when($formId, function ($q) use ($formId) {
                return $q->where('form_id', $formId);
            })->count();

        // Get submission status counts (grouped)
        $statusCounts = Submission::whereBetween('created_at', [$startDate, $endDate])
            ->selectRaw('status, COUNT(*) as count')
            ->when($formId, function ($q) use ($formId) {
                return $q->where('form_id', $formId);
            })
            ->groupBy('status')
            ->pluck('count', 'status');

        $unreadSubmissions = intval(Arr::get($statusCounts, 'unread', 0));
        $readSubmissions = intval(Arr::get($statusCounts, 'read', 0));
        $periodSpamSubmissions = intval(Arr::get($statusCounts, 'spam', 0));


        $previousStatusCounts = Submission::whereBetween('created_at', [$previousStartDate, $previousEndDate])
            ->selectRaw('status, COUNT(*) as count')
            ->when($formId, function ($q) use ($formId) {
                return $q->where('form_id', $formId);
            })
            ->groupBy('status')
            ->pluck('count', 'status');

        $previousSpamSubmissions = intval(Arr::get($previousStatusCounts, 'spam', 0));

        // Get active integrations count from wp_options
        $modulesStatus = get_option('fluentform_global_modules_status');
        $activeIntegrations = count(array_filter($modulesStatus, function ($status) {
            return $status === 'yes' || $status == 1 || $status == 'true';
        }));

        // Calculate period growth percentage
        $growthPercentage = 0;
        if ($previousPeriodSubmissions > 0) {
            $growthPercentage = round((($periodSubmissions - $previousPeriodSubmissions) / $previousPeriodSubmissions) * 100,
                1);
        } elseif ($periodSubmissions > 0) {
            $growthPercentage = 100;
        }

        $growthText = $growthPercentage > 0 ? '+' . $growthPercentage . '%' : $growthPercentage . '%';
        $growthType = $growthPercentage > 0 ? 'up' : ($growthPercentage < 0 ? 'down' : 'neutral');

        // calculate spam percentage
        $spamPercentage = 0;
        if ($previousSpamSubmissions > 0) {
            $spamPercentage = round((($periodSpamSubmissions - $previousSpamSubmissions) / $previousSpamSubmissions) * 100,
                1);
        } elseif ($periodSpamSubmissions > 0) {
            $spamPercentage = 100;
        }
        $spamText = $spamPercentage > 0 ? '+' . $spamPercentage . '%' : $spamPercentage . '%';
        $spamType = $spamPercentage > 0 ? 'down' : ($spamPercentage < 0 ? 'up' : 'neutral'); // Refunds going up is bad

        // Active forms
        $periodActiveFormsCount = Form::where('status', 'published')->whereBetween('created_at',
            [$startDate, $endDate])->count();
        $previousActiveFormsCount = Form::where('status', 'published')->whereBetween('created_at',
            [$previousStartDate, $previousEndDate])->count();
        $activeFormsPercentage = 0;
        if ($previousActiveFormsCount > 0) {
            $activeFormsPercentage = round((($periodActiveFormsCount - $previousActiveFormsCount) / $previousActiveFormsCount) * 100,
                1);
        } elseif ($periodActiveFormsCount > 0) {
            $activeFormsPercentage = 100;
        }
        $activeFormsText = $activeFormsPercentage > 0 ? '+' . $activeFormsPercentage . '%' : $activeFormsPercentage . '%';
        $activeFormsType = $activeFormsPercentage > 0 ? 'up' : ($activeFormsPercentage < 0 ? 'down' : 'neutral');

        $readRate = $periodSubmissions > 0 ? round(($readSubmissions / $periodSubmissions) * 100, 1) : 0;

        $stats = [
            'period'               => $daysDifference . ' days',
            'total_submissions'    => [
                'value'        => $periodSubmissions,
                'period_value' => $periodSubmissions,
                'change'       => $growthText,
                'change_type'  => $growthType
            ],
            'spam_submissions'     => [
                'value'        => $periodSpamSubmissions,
                'period_value' => $previousSpamSubmissions,
                'change'       => $spamText,
                'change_type'  => $spamType
            ],
            'active_integrations'  => [
                'value' => $activeIntegrations,
            ],
            'unread_submissions'   => [
                'value' => $unreadSubmissions,
            ],
            'read_submissions'     => [
                'value' => $readSubmissions,
            ],
            'active_forms'         => [
                'value'       => $periodActiveFormsCount,
                'change'      => $activeFormsText,
                'change_type' => $activeFormsType
            ],
            'read_submission_rate' => [
                'value' => $readRate,
            ]
        ];

        // Add payment statistics if payment module is enabled
        $paymentSettings = get_option('__fluentform_payment_module_settings');
        if ($paymentSettings && Arr::get($paymentSettings, 'status') === 'yes') {
            // Get payment statistics
            $paymentStats = self::getPaymentStats($startDate, $endDate, $previousStartDate, $previousEndDate, $formId);
            $stats = array_merge($stats, $paymentStats);
        }

        return $stats;
    }


    /**
     * Initialize heatmap data structure based on aggregation type
     */
    protected static function initializeHeatmapData($aggregationType)
    {
        if ($aggregationType === 'day_of_week') {
            $dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
            $heatmapData = [];
            foreach ($dayNames as $day) {
                $heatmapData[$day] = array_fill(0, 24, 0); // 24 time slots (0-23 hours), all initialized to 0
            }
            return $heatmapData;
        }

        return [];
    }

    /**
     * Get submission data for heatmap with appropriate grouping
     * Optimized version with better query performance
     */
    protected static function getHeatmapSubmissionData($startDate, $endDate, $formId, $aggregationType)
    {
        if ($aggregationType === 'day_of_week') {
            $query = Submission::selectRaw('
                DAYOFWEEK(created_at) as day_of_week,
                HOUR(created_at) as submission_hour,
                COUNT(*) as count
            ')
                ->whereBetween('created_at', [$startDate, $endDate])
                ->when($formId, function ($q) use ($formId) {
                    return $q->where('form_id', $formId);
                })
                ->whereNotIn('status', ['trashed', 'spam'])
                ->groupBy('day_of_week', 'submission_hour')
                ->orderBy('day_of_week')
                ->orderBy('submission_hour');

            return $query->get();
        }

        return collect([]);
    }


    public static function getApiLogs($startDate, $endDate, $formId = null)
    {
        // Process date range
        list($startDate, $endDate) = self::processDateRange($startDate, $endDate);

        // Calculate date difference to determine grouping
        $startDateTime = new \DateTime($startDate);
        $endDateTime = new \DateTime($endDate);
        $interval = $startDateTime->diff($endDateTime);
        $daysInterval = $interval->days + 1;

        // Determine grouping mode based on date range
        $groupingMode = self::getGroupingMode($daysInterval);

        // Define the date format based on grouping mode
        if ($groupingMode === 'day') {
            $dateFormat = "DATE(created_at)";
        } elseif ($groupingMode === '3days') {
            $dateFormat = "DATE(created_at)";
        } elseif ($groupingMode === 'week') {
            $dateFormat = "DATE(DATE_ADD(created_at, INTERVAL(-WEEKDAY(created_at)) DAY))";
        } else { // month
            $dateFormat = "DATE_FORMAT(created_at, '%Y-%m-01')";
        }

        // Components to exclude
        $excludedComponents = [
            'postFeeds',
            'AdminApproval',
            'Payment',
            'EntryEditor',
            'DoubleOptin',
            'Subscription',
            'UserRegistration',
            'Akismet Integration',
            'CleanTalk API Integration'
        ];

        // Get logs grouped by date and status using Eloquent, excluding specific components
        $logsQuery = Log::whereBetween('created_at', [$startDate, $endDate]);

        // Exclude components - handle both NULL and specific values
        $logsQuery->where(function ($query) use ($excludedComponents) {
            $query->whereNull('component')
                ->orWhereNotIn('component', $excludedComponents);
        });

        if ($formId) {
            $logsQuery->where('parent_source_id', $formId);
        }

        $results = $logsQuery->selectRaw($dateFormat . ' as log_date')
            ->selectRaw('status')
            ->selectRaw('COUNT(*) as count')
            ->groupBy('log_date', 'status')
            ->orderBy('log_date')
            ->get();

        // Get total counts by status (also excluding the specific components)
        $totalsQuery = Log::whereBetween('created_at', [$startDate, $endDate]);

        $totalsQuery->where(function ($query) use ($excludedComponents) {
            $query->whereNull('component')
                ->orWhereNotIn('component', $excludedComponents);
        });

        $totalsResults = $totalsQuery->selectRaw('status')
            ->selectRaw('COUNT(*) as count')
            ->groupBy('status')
            ->get();

        $totals = [
            'success' => 0,
            'pending' => 0,
            'failed'  => 0
        ];

        foreach ($totalsResults as $total) {
            $status = strtolower($total->status);
            if (isset($totals[$status])) {
                $totals[$status] = (int)$total->count;
            }
        }

        // Get date labels and prepare data
        $dateLabels = self::getDateLabels($startDateTime, $endDateTime, $groupingMode);
        $dates = $dateLabels['dates'];
        $formattedLabels = $dateLabels['labels'];

        // Initialize data structure - always with all dates, even if no data exists
        $seriesData = [
            'success' => array_fill_keys($dates, 0),
            'pending' => array_fill_keys($dates, 0),
            'failed'  => array_fill_keys($dates, 0)
        ];

        // Fill in data from results when available
        foreach ($results as $row) {
            $date = $row->log_date;
            $status = strtolower($row->status);
            $count = (int)$row->count;

            // Map status to our categories
            if ($status === 'success' || $status === 'pending' || $status === 'failed') {
                if (isset($seriesData[$status][$date])) {
                    $seriesData[$status][$date] = $count;
                }
            }
        }

        return [
            'logs_data'  => [
                'categories' => $formattedLabels,
                'series'     => $seriesData
            ],
            'totals'     => $totals,
            'start_date' => $startDate,
            'end_date'   => $endDate
        ];
    }

    /**
     * Get top performing forms by entries, views, or payments
     */
    public static function getTopPerformingForms($startDate, $endDate, $metric = 'entries')
    {
        list($startDate, $endDate) = self::processDateRange($startDate, $endDate);
        global $wpdb;
        $prefix = $wpdb->prefix;
        $formResults = [];
        $disableMessage = '';

        switch ($metric) {
            case 'entries':
                // Use Form model with Submission relationship
                $results = Form::select(['id', 'title'])
                    ->withCount([
                        'submissions' => function ($q) use ($startDate, $endDate) {
                            $q->whereBetween('created_at', [$startDate, $endDate]);
                            $q->whereNotIn('status', ['trashed', 'spam']);
                        }
                    ])
                    ->orderBy('submissions_count', 'DESC')
                    ->limit(5)
                    ->get();

                // Map the results to standard format
                foreach ($results as $form) {
                    $formResults[] = (object)[
                        'id' => $form->id,
                        'title' => $form->title,
                        'value' => $form->submissions_count
                    ];
                }
                break;

            case 'payments':
                // Check if payment module is enabled
                $paymentSettings = get_option('__fluentform_payment_module_settings');
                if ($paymentSettings && Arr::get($paymentSettings, 'status')) {
                    $results = wpFluent()->table('fluentform_forms')
                        ->select([
                            'fluentform_forms.id',
                            'fluentform_forms.title',
                            wpFluent()->raw("COALESCE(SUM({$prefix}fluentform_transactions.payment_total), 0) as raw_value")
                        ])
                        ->leftJoin('fluentform_transactions', 'fluentform_forms.id', '=',
                            'fluentform_transactions.form_id')
                        ->whereBetween('fluentform_transactions.created_at', [$startDate, $endDate])
                        ->where('fluentform_transactions.status', 'paid')
                        ->groupBy('fluentform_forms.id')
                        ->orderBy('raw_value', 'DESC')
                        ->limit(5)
                        ->get();

                    // Convert cents to dollars in PHP for better precision
                    foreach ($results as $form) {
                        $form->value = round((float)$form->raw_value / 100, 2);
                    }

                    $formResults = $results;
                } else {
                    $disableMessage = __('Payment module is disabled. Please enable it to view top performing form by payments.', 'fluentform');
                }
                break;

            case 'views':
                // Count unique views by IP from analytics table if analytics enabled
                if (!apply_filters('fluentform/disabled_analytics', true)) {
                    $results = wpFluent()->table('fluentform_forms')
                        ->select([
                            'fluentform_forms.id',
                            'fluentform_forms.title',
                            wpFluent()->raw("COUNT(DISTINCT {$prefix}fluentform_form_analytics.ip) as value")
                        ])
                        ->leftJoin('fluentform_form_analytics', 'fluentform_forms.id', '=',
                            'fluentform_form_analytics.form_id')
                        ->whereBetween('fluentform_form_analytics.created_at', [$startDate, $endDate])
                        ->groupBy('fluentform_forms.id')
                        ->orderBy('value', 'DESC')
                        ->limit(5)
                        ->get();

                    $formResults = $results;
                } else {
                    $disableMessage = __('Analytics is disabled. Please enable it to view top performing form by views.', 'fluentform');
                }
                break;
        }

        // Common formatting for all results
        $topForms = [];
        foreach ($formResults as $form) {
            if ((float)$form->value > 0) {
                $topForms[] = [
                    'id'    => $form->id,
                    'title' => $form->title ?: 'Untitled Form',
                    'value' => (float)$form->value
                ];
            }
        }
        return [
            'disable_message' => $disableMessage,
            'data' => array_reverse($topForms)
        ];
    }

    /**
     * Get form views date chunks
     */
    private static function getFormViews($startDate, $endDate, $groupingMode, $formId)
    {
        if (apply_filters('fluentform/disabled_analytics', true)) {
            return [];
        }

        // 1. Get UNIQUE VIEWS by IP address
        $viewsQuery = FormAnalytics::whereBetween('created_at', [$startDate, $endDate])
            ->whereNotNull('ip');

        if ($formId) {
            $viewsQuery->where('form_id', $formId);
        }

        // Group by date and IP to count unique visitors
        if ($groupingMode === 'day') {
            $viewsQuery->selectRaw('DATE(created_at) as date_group, COUNT(DISTINCT ip) as unique_count');
        } elseif ($groupingMode === '3days') {
            // Get min date for reference
            $minDateRecord = FormAnalytics::whereBetween('created_at', [$startDate, $endDate])
                ->selectRaw('MIN(DATE(created_at)) as min_date')
                ->first();

            if ($minDateRecord && $minDateRecord->min_date) {
                $minDate = $minDateRecord->min_date;
                $viewsQuery->selectRaw("FLOOR(DATEDIFF(DATE(created_at), ?) / 3) as group_num", [$minDate])
                    ->selectRaw('MIN(DATE(created_at)) as date_group')
                    ->selectRaw('COUNT(DISTINCT ip) as unique_count')
                    ->groupBy('group_num');
            } else {
                $viewsQuery->selectRaw('DATE(created_at) as date_group, COUNT(DISTINCT ip) as unique_count')
                    ->groupBy('date_group');
            }
        } elseif ($groupingMode === 'week') {
            $viewsQuery->selectRaw("DATE(DATE_ADD(created_at, INTERVAL(-WEEKDAY(created_at)) DAY)) as date_group, COUNT(DISTINCT ip) as unique_count");
        } else { // month
            $viewsQuery->selectRaw("DATE_FORMAT(created_at, '%Y-%m-01') as date_group, COUNT(DISTINCT ip) as unique_count");
        }

        if ($groupingMode !== '3days' || !(isset($minDateRecord) && $minDateRecord->min_date)) {
            $viewsQuery->groupBy('date_group');
        }

        $results = $viewsQuery->orderBy('date_group')->get();
        $views = [];
        foreach ($results as $result) {
            $views[$result->date_group] = $result->unique_count;
        }
        return $views;
    }

    /**
     * Process date range
     */
    public static function processDateRange($startDate, $endDate)
    {
        // Validate date formats
        if (!strtotime($startDate) || !strtotime($endDate)) {
            return [];
        }

        // Sanity check - ensure start date is before end date
        $startDateTime = new \DateTime($startDate);
        $endDateTime = new \DateTime($endDate);

        // If start date is after end date, swap them
        if ($startDateTime > $endDateTime) {
            $temp = $startDate;
            $startDate = $endDate;
            $endDate = $temp;
        }

        return [$startDate, $endDate];
    }

    /**
     * Determine grouping mode based on date range
     */
    private static function getGroupingMode($daysInterval)
    {
        if ($daysInterval <= 7) {
            return 'day'; // 1-7 days: group by day
        } elseif ($daysInterval <= 31) {
            return '3days'; // 8-31 days: group by 3 days
        } elseif ($daysInterval <= 92) {
            return 'week'; // Group by week for 1-3 months
        } else {
            return 'month'; // 3+ months: group by month
        }
    }

    /**
     * Get aggregated data based on grouping mode
     */
    private static function getAggregatedData($startDate, $endDate, $groupingMode, $view, $formId)
    {
        $baseQuery = Submission::whereBetween('created_at', [$startDate, $endDate]);

        // Filter by form ID if provided
        if ($formId) {
            $baseQuery->where('form_id', $formId);
        }

        if ($view === 'revenue') {
            // Clone the base query for each payment status
            $paidQuery = clone $baseQuery;
            $pendingQuery = clone $baseQuery;
            $refundedQuery = clone $baseQuery;

            // Get paid payments
            $paidQuery->whereNotNull('payment_total')
                ->where(function ($query) {
                    $query->where('payment_status', 'paid');
                })
                ->selectRaw('ROUND(SUM(payment_total) / 100, 2) as count');

            // Get pending payments
            $pendingQuery->whereNotNull('payment_total')
                ->where('payment_status', 'pending')
                ->selectRaw('ROUND(SUM(payment_total) / 100, 2) as count');

            // Get refunded payments
            $refundedQuery->whereNotNull('payment_total')
                ->where('payment_status', 'refunded')
                ->selectRaw('ROUND(SUM(payment_total) / 100, 2) as count');

            // Apply grouping based on mode to all three queries
            if ($groupingMode === 'day') {
                $paidQuery->selectRaw('DATE(created_at) as date_group')->groupBy('date_group');
                $pendingQuery->selectRaw('DATE(created_at) as date_group')->groupBy('date_group');
                $refundedQuery->selectRaw('DATE(created_at) as date_group')->groupBy('date_group');
            } elseif ($groupingMode === '3days') {
                // Get minimum date for reference
                $minDateRecord = Submission::whereBetween('created_at', [$startDate, $endDate])
                    ->selectRaw('MIN(DATE(created_at)) as min_date')
                    ->first();

                if ($minDateRecord && $minDateRecord->min_date) {
                    $minDate = $minDateRecord->min_date;

                    $paidQuery->selectRaw("MIN(DATE(created_at)) as date_group")
                        ->selectRaw("FLOOR(DATEDIFF(DATE(created_at), ?) / 3) as group_num", [$minDate])
                        ->groupBy('group_num');

                    $pendingQuery->selectRaw("MIN(DATE(created_at)) as date_group")
                        ->selectRaw("FLOOR(DATEDIFF(DATE(created_at), ?) / 3) as group_num", [$minDate])
                        ->groupBy('group_num');

                    $refundedQuery->selectRaw("MIN(DATE(created_at)) as date_group")
                        ->selectRaw("FLOOR(DATEDIFF(DATE(created_at), ?) / 3) as group_num", [$minDate])
                        ->groupBy('group_num');
                } else {
                    $paidQuery->selectRaw('DATE(created_at) as date_group')->groupBy('date_group');
                    $pendingQuery->selectRaw('DATE(created_at) as date_group')->groupBy('date_group');
                    $refundedQuery->selectRaw('DATE(created_at) as date_group')->groupBy('date_group');
                }
            } elseif ($groupingMode === 'week') {
                $paidQuery->selectRaw("DATE(DATE_ADD(created_at, INTERVAL(-WEEKDAY(created_at)) DAY)) as date_group")->groupBy('date_group');
                $pendingQuery->selectRaw("DATE(DATE_ADD(created_at, INTERVAL(-WEEKDAY(created_at)) DAY)) as date_group")->groupBy('date_group');
                $refundedQuery->selectRaw("DATE(DATE_ADD(created_at, INTERVAL(-WEEKDAY(created_at)) DAY)) as date_group")->groupBy('date_group');
            } else {
                $paidQuery->selectRaw("DATE_FORMAT(created_at, '%Y-%m-01') as date_group")->groupBy('date_group');
                $pendingQuery->selectRaw("DATE_FORMAT(created_at, '%Y-%m-01') as date_group")->groupBy('date_group');
                $refundedQuery->selectRaw("DATE_FORMAT(created_at, '%Y-%m-01') as date_group")->groupBy('date_group');
            }

            // Execute the queries
            $paidResults = $paidQuery->orderBy('date_group')->get();
            $pendingResults = $pendingQuery->orderBy('date_group')->get();
            $refundedResults = $refundedQuery->orderBy('date_group')->get();


            // Format the data
            $paidData = $revenuePayments = [];
            foreach ($paidResults as $result) {
                $paidData[$result->date_group] = $result->count;
                $revenuePayments[$result->date_group] = $result->count;
            }

            $pendingData = [];
            foreach ($pendingResults as $result) {
                $pendingData[$result->date_group] = $result->count;
            }

            $refundedData = [];
            foreach ($refundedResults as $result) {
                $refundedData[$result->date_group] = $result->count;
                if (isset($revenuePayments[$result->date_group])) {
                    $revenuePayments[$result->date_group] -= $result->count;
                }
            }

            // Return all three datasets
            return [
                'paid'     => $paidData,
                'pending'  => $pendingData,
                'refunded' => $refundedData,
                'payments' => $revenuePayments
            ];
        } else {
            $query = $baseQuery->selectRaw('COUNT(*) as count')->selectRaw('status');
            $query->groupBy('status');

            // Apply grouping based on mode
            if ($groupingMode === 'day') {
                $query->selectRaw('DATE(created_at) as date_group')
                    ->groupBy('date_group');
            } elseif ($groupingMode === '3days') {
                $minDateRecord = Submission::whereBetween('created_at', [$startDate, $endDate])
                    ->selectRaw('MIN(DATE(created_at)) as min_date')
                    ->first();

                if ($minDateRecord && $minDateRecord->min_date) {
                    $query->selectRaw("MIN(DATE(created_at)) as date_group")
                        ->selectRaw("FLOOR(DATEDIFF(DATE(created_at), '{$minDateRecord->min_date}') / 3) as group_num")
                        ->groupBy('group_num');
                } else {
                    $query->selectRaw('DATE(created_at) as date_group')
                        ->groupBy('date_group');
                }
            } elseif ($groupingMode === 'week') {
                $query->selectRaw("DATE(DATE_ADD(created_at, INTERVAL(-WEEKDAY(created_at)) DAY)) as date_group")
                    ->groupBy('date_group');
            } else {
                $query->selectRaw("DATE_FORMAT(created_at, '%Y-%m-01') as date_group")
                    ->groupBy('date_group');
            }

            $results = $query->orderBy('date_group')->get();
            $total = $read = $unread = $spam = $trashed = [];
            foreach ($results as $result) {
                if ($result->status === 'read') {
                    $read[$result->date_group] = $result->count;
                }
                if ($result->status === 'unread') {
                    $unread[$result->date_group] = $result->count;
                }
                if ($result->status === 'spam') {
                    $spam[$result->date_group] = $result->count;
                }
                if ($result->status === 'trashed') {
                    $trashed[$result->date_group] = $result->count;
                }
                $total[$result->date_group] = isset($total[$result->date_group]) ? $total[$result->date_group] + $result->count : $result->count;
            }
            // Return all four datasets
            return [
                'submissions' => $total,
                'read'        => $read,
                'unread'      => $unread,
                'spam'        => $spam,
                'trashed'     => $trashed
            ];
        }
    }

    /**
     * Generate date labels based on grouping mode
     */
    private static function getDateLabels(\DateTime $startDate, \DateTime $endDate, $groupingMode)
    {
        $dates = [];
        $labels = [];
        $current = clone $startDate;

        if ($groupingMode === 'day') {
            // Generate daily labels
            while ($current <= $endDate) {
                $dateKey = $current->format('Y-m-d');
                $dates[] = $dateKey;
                $labels[] = $current->format('M d');
                $current->modify('+1 day');
            }
        } elseif ($groupingMode === '3days') {
            // Generate labels for every 3 days
            $dayIndex = 0;
            $groupStartDate = clone $current;

            while ($current <= $endDate) {
                if ($dayIndex % 3 === 0 && $dayIndex > 0) {
                    $previousDate = clone $current;
                    $previousDate->modify('-1 day');

                    $dateKey = $groupStartDate->format('Y-m-d');
                    $dates[] = $dateKey;
                    $labels[] = $groupStartDate->format('M d');

                    $groupStartDate = clone $current;
                }

                $current->modify('+1 day');
                $dayIndex++;
            }

            // Add the last group if needed
            if ($groupStartDate <= $endDate) {
                $dateKey = $groupStartDate->format('Y-m-d');
                $dates[] = $dateKey;
                $labels[] = $groupStartDate->format('M d');
            }
        } elseif ($groupingMode === 'week') {
            // Generate weekly labels
            while ($current <= $endDate) {
                // Use simple approach to get Monday (start of week)
                $dayOfWeek = (int)$current->format('N'); // 1 (Monday) through 7 (Sunday)
                $daysToSubtract = $dayOfWeek - 1;

                $weekStart = clone $current;
                if ($daysToSubtract > 0) {
                    $weekStart->modify("-{$daysToSubtract} days");
                }

                // Calculate end of week (Sunday)
                $weekEnd = clone $weekStart;
                $weekEnd->modify('+6 days');

                // If weekend exceeds the range end, cap it
                if ($weekEnd > $endDate) {
                    $weekEnd = clone $endDate;
                }

                $dateKey = $weekStart->format('Y-m-d');
                $dates[] = $dateKey;
                $labels[] = $weekStart->format('M d');

                // Move to next week
                $current->modify('+7 days');
            }
        } else {
            // Generate monthly labels
            while ($current <= $endDate) {
                $dateKey = $current->format('Y-m-01');
                $dates[] = $dateKey;
                $labels[] = $current->format('M Y');

                // Manually move to first day of next month
                $year = (int)$current->format('Y');
                $month = (int)$current->format('m');

                // Move to next month
                $month++;
                if ($month > 12) {
                    $month = 1;
                    $year++;
                }

                // Set to first day of next month
                $current = new \DateTime("$year-$month-01");
            }
        }

        return ['dates' => $dates, 'labels' => $labels];
    }
    
    public static function getPaymentsByType($startDate, $endDate, $type, $formId = 0)
    {
        $paymentSettings = get_option('__fluentform_payment_module_settings');
        if (!$paymentSettings || !Arr::isTrue($paymentSettings, 'status')) {
            return []; // Return empty if payment module is disabled
        }
        list($startDate, $endDate) = self::processDateRange($startDate, $endDate);
        
        // Base query for transactions
        $query = wpFluent()->table('fluentform_transactions')
            ->whereBetween('created_at', [$startDate, $endDate]);
        
        // Filter by transaction type if specified
        if ($type === 'subscription') {
            $query->whereIn('transaction_type', ['subscription', 'subscription_signup_fee']);
        } elseif ($type === 'onetime') {
            $query->where('transaction_type', 'onetime');
        }
        
        if ($formId) {
            $query->where('form_id', $formId);
        }
        
        // Get payments grouped by status
        $payments = $query->select('status')
            ->selectRaw('SUM(payment_total) as total_amount')
            ->selectRaw('COUNT(*) as count')
            ->groupBy('status')
            ->get();
        
        // Get the total payment amount
        $totalAmount = 0;
        foreach ($payments as $payment) {
            $totalAmount += $payment->total_amount;
        }
        
        $formattedData = [];
        foreach ($payments as $payment) {
            $status = strtolower($payment->status);
            $amount = $payment->total_amount / 100; // Convert from cents to dollars
            $percentage = $totalAmount > 0 ? round(($payment->total_amount / $totalAmount) * 100, 2) : 0;
            
            $formattedData[$status] = [
                'amount' => $amount,
                'percentage' => $percentage,
                'count' => $payment->count
            ];
        }
        
        // Calculate weekly average paid amount
        $daysInRange = self::getDateDifference($startDate, $endDate);
        $weeksInRange = max(1, round($daysInRange / 7, 1));
        
        $paidAmount = 0;
        foreach ($formattedData as $status => $data) {
            if ($status === 'paid') {
                $paidAmount = $data['amount'];
                break;
            }
        }
        
        $weeklyAverage = $paidAmount / $weeksInRange;
        
        return [
            'currency_symbol' => Arr::get(PaymentHelper::getCurrencyConfig($formId), 'currency_sign', '$'),
            'payment_statuses' => $formattedData,
            'total_amount'     => $totalAmount / 100, // Convert from cents to dollars
            'weekly_average'   => round($weeklyAverage, 2)
        ];
    }
    
    /**
     * Format payment method name for display
     */
    protected static function formatPaymentMethodName($paymentMethod)
    {
        $methodNames = [
            'stripe'   => 'Stripe',
            'paypal'   => 'PayPal',
            'razorpay' => 'Razorpay',
            'paystack' => 'Paystack',
            'mollie'   => 'Mollie',
            'square'   => 'Square',
            'paddle'   => 'Paddle',
            'test'     => 'Offline/Test',
            'offline'  => 'Offline'
        ];

        return $methodNames[$paymentMethod] ?? ucfirst($paymentMethod);
    }

    /**
     * Format data for the chart
     */
    private static function formatDataForChart($dateLabels, $data, $formId)
    {
        $dates = $dateLabels['dates'];
        $labels = $dateLabels['labels'];

        if (is_array($data) && isset($data['paid'])) {
            $paidValues = self::fillMissingData($dates, $data['paid']);
            $pendingValues = self::fillMissingData($dates, $data['pending']);
            $refundedValues = self::fillMissingData($dates, $data['refunded']);
            $paymentsValues = self::fillMissingData($dates, $data['payments']);
            $currencyConfig = PaymentHelper::getCurrencyConfig($formId);

            return [
                'dates'         => $labels,
                'currency_sign' => Arr::get($currencyConfig, 'currency_sign', '$'),
                'currency'      => Arr::get($currencyConfig, 'currency', 'USD'),
                'values'        => [
                    'paid'     => array_values($paidValues),
                    'pending'  => array_values($pendingValues),
                    'refunded' => array_values($refundedValues),
                    'payments' => array_values($paymentsValues)
                ]
            ];
        } else {
            return [
                'dates'  => $labels,
                'values' => [
                    'submissions' => array_values(self::fillMissingData($dates, $data['submissions'])),
                    'read'        => array_values(self::fillMissingData($dates, $data['read'])),
                    'unread'      => array_values(self::fillMissingData($dates, $data['unread'])),
                    'spam'        => array_values(self::fillMissingData($dates, $data['spam'])),
                    'trashed'     => array_values(self::fillMissingData($dates, $data['trashed']))
                ]
            ];
        }
    }

    /**
     * Fill in missing data based on date intervals
     *
     * @param array $allDates Array of interval start dates
     * @param array $data     Associative array of date => value pairs
     *
     * @return array Result with interval start dates mapped to summed values
     */
    private static function fillMissingData($allDates, $data)
    {
        $result = [];

        // Pre-convert dates to timestamps for faster comparison
        $dataTimestamps = [];
        foreach ($data as $date => $value) {
            $dataTimestamps[strtotime($date)] = $value;
        }

        $allDatesCount = count($allDates);
        for ($i = 0; $i < $allDatesCount; $i++) {
            $startTimestamp = strtotime($allDates[$i]);

            // Calculate end timestamp of interval (exclusive)
            $endTimestamp = isset($allDates[$i + 1])
                ? strtotime($allDates[$i + 1])
                : $startTimestamp + (3 * 24 * 60 * 60); // 3 days in seconds

            $sum = 0;
            $hasData = false;

            // Check each timestamp in the data array
            foreach ($dataTimestamps as $timestamp => $value) {
                if ($timestamp >= $startTimestamp && $timestamp < $endTimestamp) {
                    $sum += $value;
                    $hasData = true;
                }
            }

            $result[$allDates[$i]] = $hasData ? $sum : 0;
        }

        return $result;
    }

    private static function getPaymentStats($startDate, $endDate, $previousStartDate, $previousEndDate, $formId)
    {
        // Get total payments (paid status) for current period
        $currentPayments = wpFluent()
            ->table('fluentform_transactions')
            ->whereBetween('created_at', [$startDate, $endDate])
            ->where('status', 'paid')
            ->when($formId, function ($q) use ($formId) {
                return $q->where('form_id', $formId);
            })
            ->sum('payment_total');

        // Get total payments for previous period
        $previousPayments = wpFluent()
            ->table('fluentform_transactions')
            ->whereBetween('created_at', [$previousStartDate, $previousEndDate])
            ->where('status', 'paid')
            ->when($formId, function ($q) use ($formId) {
                return $q->where('form_id', $formId);
            })
            ->sum('payment_total');

        // Get pending payments for current period
        $currentPending = wpFluent()
            ->table('fluentform_transactions')
            ->whereBetween('created_at', [$startDate, $endDate])
            ->where('status', 'pending')
            ->when($formId, function ($q) use ($formId) {
                return $q->where('form_id', $formId);
            })
            ->sum('payment_total');

        // Get pending payments for previous period
        $previousPending = wpFluent()
            ->table('fluentform_transactions')
            ->whereBetween('created_at', [$previousStartDate, $previousEndDate])
            ->where('status', 'pending')
            ->when($formId, function ($q) use ($formId) {
                return $q->where('form_id', $formId);
            })
            ->sum('payment_total');

        // Get total refunds for current period
        $currentRefunds = wpFluent()
            ->table('fluentform_transactions')
            ->whereBetween('created_at', [$startDate, $endDate])
            ->where('status', 'refunded')
            ->when($formId, function ($q) use ($formId) {
                return $q->where('form_id', $formId);
            })
            ->sum('payment_total');

        // Get total refunds for previous period
        $previousRefunds = wpFluent()
            ->table('fluentform_transactions')
            ->whereBetween('created_at', [$previousStartDate, $previousEndDate])
            ->where('status', 'refunded')
            ->when($formId, function ($q) use ($formId) {
                return $q->where('form_id', $formId);
            })
            ->sum('payment_total');

        // Convert from cents to dollars
        $currentPayments = $currentPayments ? $currentPayments / 100 : 0;
        $previousPayments = $previousPayments ? $previousPayments / 100 : 0;
        $currentPending = $currentPending ? $currentPending / 100 : 0;
        $previousPending = $previousPending ? $previousPending / 100 : 0;
        $currentRefunds = $currentRefunds ? $currentRefunds / 100 : 0;
        $previousRefunds = $previousRefunds ? $previousRefunds / 100 : 0;

        // Calculate payment growth percentage
        $paymentGrowthPercentage = 0;
        if ($previousPayments > 0) {
            $paymentGrowthPercentage = round((($currentPayments - $previousPayments) / $previousPayments) * 100, 1);
        } elseif ($currentPayments > 0) {
            $paymentGrowthPercentage = 100;
        }

        $paymentGrowthText = $paymentGrowthPercentage > 0 ? '+' . $paymentGrowthPercentage . '%' : $paymentGrowthPercentage . '%';
        $paymentGrowthType = $paymentGrowthPercentage > 0 ? 'up' : ($paymentGrowthPercentage < 0 ? 'down' : 'neutral');

        // Calculate refund growth percentage
        $refundGrowthPercentage = 0;
        if ($previousRefunds > 0) {
            $refundGrowthPercentage = round((($currentRefunds - $previousRefunds) / $previousRefunds) * 100, 1);
        } elseif ($currentRefunds > 0) {
            $refundGrowthPercentage = 100;
        }

        $refundGrowthText = $refundGrowthPercentage > 0 ? '+' . $refundGrowthPercentage . '%' : $refundGrowthPercentage . '%';
        $refundGrowthType = $refundGrowthPercentage > 0 ? 'down' : ($refundGrowthPercentage < 0 ? 'up' : 'neutral'); // Refunds going up is bad

        // Calculate pending growth percentage
        $pendingGrowthPercentage = 0;
        if ($previousPending > 0) {
            $pendingGrowthPercentage = round((($currentPending - $previousPending) / $previousPending) * 100, 1);
        } elseif ($currentPending > 0) {
            $pendingGrowthPercentage = 100;
        }

        $pendingGrowthText = $pendingGrowthPercentage > 0 ? '+' . $pendingGrowthPercentage . '%' : $pendingGrowthPercentage . '%';
        $pendingGrowthType = $pendingGrowthPercentage > 0 ? 'up' : ($pendingGrowthPercentage < 0 ? 'down' : 'neutral');

        // Calculate revenue percentage
        $totalRevenue = $currentPayments - $currentRefunds;
        $previousRevenue = $previousPayments - $previousRefunds;
        $revenuePercentage = 0;
        if ($previousRevenue > 0) {
            $revenuePercentage = round((($totalRevenue - $previousRevenue) / $previousRevenue) * 100, 1);
        } elseif ($totalRevenue > 0) {
            $revenuePercentage = 100;
        }
        $revenueText = $revenuePercentage > 0 ? '+' . $revenuePercentage . '%' : $revenuePercentage . '%';
        $revenueType = $revenuePercentage > 0 ? 'up' : ($revenuePercentage < 0 ? 'down' : 'neutral');

        // Get default currency from payment settings
        $paymentSettings = PaymentHelper::getPaymentSettings();
        $currency = Arr::get($paymentSettings, 'currency', 'USD');
        $currencySymbol = PaymentHelper::getCurrencySymbol($currency);

        return [
            'total_payments'   => [
                'value'           => number_format($currentPayments, 2),
                'raw_value'       => $currentPayments,
                'currency'        => $currency,
                'currency_symbol' => $currencySymbol,
                'change'          => $paymentGrowthText,
                'change_type'     => $paymentGrowthType
            ],
            'pending_payments' => [
                'value'           => number_format($currentPending, 2),
                'raw_value'       => $currentPending,
                'currency'        => $currency,
                'currency_symbol' => $currencySymbol,
                'change'          => $pendingGrowthText,
                'change_type'     => $pendingGrowthType
            ],
            'total_refunds'    => [
                'value'           => number_format($currentRefunds, 2),
                'raw_value'       => $currentRefunds,
                'currency'        => $currency,
                'currency_symbol' => $currencySymbol,
                'change'          => $refundGrowthText,
                'change_type'     => $refundGrowthType
            ],
            'total_revenue'    => [
                'value'           => number_format($totalRevenue, 2),
                'raw_value'       => $totalRevenue,
                'change'          => $revenueText,
                'change_type'     => $revenueType,
                'currency'        => $currency,
                'currency_symbol' => $currencySymbol
            ]
        ];
    }

    
    protected static function getDateDifference($startDate, $endDate)
    {
        $start = new \DateTime($startDate);
        $end = new \DateTime($endDate);
        $interval = $start->diff($end);
        return $interval->days + 1;
    }
}