<?php

namespace FluentCrm\App\Services;

use FluentCrm\App\Models\CampaignEmail;
use FluentCrm\App\Models\CampaignUrlMetric;
use FluentCrm\App\Models\FunnelMetric;
use FluentCrm\App\Models\FunnelSequence;
use FluentCrm\App\Models\FunnelSubscriber;
use FluentCrm\Framework\Support\Arr;
use FluentCrm\Framework\Support\Collection;

class Reporting
{
    use ReportingHelperTrait;

    public function getSubscribersGrowth($from = false, $to = false, $tagId = 0, $listId = 0)
    {
        $period = $this->makeDatePeriod(
            $from = $this->makeFromDate($from),
            $to = $this->makeToDate($to),
            $frequency = $this->getFrequency($from, $to)
        );

        list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);

        $query = fluentCrmDb()->table('fc_subscribers')
            ->select($this->prepareSelect($frequency))
            ->whereBetween('created_at', [$from->format('Y-m-d'), $to->format('Y-m-d 23:59:59')])
            ->where('status', 'subscribed');

        if ($tagId) {
            $query->whereIn('id', function ($q) use ($tagId) {
                $q->select('subscriber_id')
                    ->from('fc_subscriber_pivot')
                    ->where('object_id', $tagId)
                    ->where('object_type', 'FluentCrm\App\Models\Tag');
            });
        }

        if ($listId) {
            $query->whereIn('id', function ($q) use ($listId) {
                $q->select('subscriber_id')
                    ->from('fc_subscriber_pivot')
                    ->where('object_id', $listId)
                    ->where('object_type', 'FluentCrm\App\Models\Lists');
            });
        }

        $items = $query->groupBy($groupBy)
            ->orderBy($orderBy, 'ASC')
            ->get();

        return $this->getResult($period, $items);
    }

    public function getEmailOpenStats($from = false, $to = false, $campaignId = 0)
    {
        $period = $this->makeDatePeriod(
            $from = $this->makeFromDate($from),
            $to = $this->makeToDate($to),
            $frequency = $this->getFrequency($from, $to)
        );

        list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);

        $query = fluentCrmDb()->table('fc_campaign_emails')
            ->select($this->prepareSelect($frequency, 'updated_at'))
            ->whereBetween('updated_at', [$from->format('Y-m-d'), $to->format('Y-m-d 23:59:59')])
            ->where('is_open', 1);

        if ($campaignId) {
            $query->where('campaign_id', $campaignId);
        }

        $items = $query->groupBy($groupBy)
            ->orderBy($orderBy, 'ASC')
            ->get();

        return $this->getResult($period, $items);
    }

    public function getEmailClickStats($from = false, $to = false, $campaignId = 0)
    {
        $period = $this->makeDatePeriod(
            $from = $this->makeFromDate($from),
            $to = $this->makeToDate($to),
            $frequency = $this->getFrequency($from, $to)
        );

        list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);

        $query = fluentCrmDb()->table('fc_campaign_url_metrics')
            ->select($this->prepareSelect($frequency))
            ->whereBetween('created_at', [$from->format('Y-m-d'), $to->format('Y-m-d 23:59:59')])
            ->where('type', 'click');

        if ($campaignId) {
            $query->where('campaign_id', $campaignId);
        }

        $items = $query->groupBy($groupBy)
            ->orderBy($orderBy, 'ASC')
            ->get();

        return $this->getResult($period, $items);
    }

    public function getEmailStats($from = false, $to = false, $status = 'sent', $campaignId = 0)
    {
        $period = $this->makeDatePeriod(
            $from = $this->makeFromDate($from),
            $to = $this->makeToDate($to),
            $frequency = $this->getFrequency($from, $to)
        );

        list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);

        $query = fluentCrmDb()->table('fc_campaign_emails')
            ->select($this->prepareSelect($frequency, 'scheduled_at'))
            ->whereBetween('scheduled_at', [$from->format('Y-m-d'), $to->format('Y-m-d 23:59:59')])
            ->where('status', $status);

        if ($campaignId) {
            $query->where('campaign_id', $campaignId);
        }

        $items = $query->groupBy($groupBy)
            ->orderBy($orderBy, 'ASC')
            ->get();

        return $this->getResult($period, $items);
    }

    public function getUnsubscribeStats($from = false, $to = false)
    {
        $period = $this->makeDatePeriod(
            $from = $this->makeFromDate($from),
            $to = $this->makeToDate($to),
            $frequency = $this->getFrequency($from, $to)
        );

        list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);

        $items = fluentCrmDb()->table('fc_subscriber_meta')
            ->select($this->prepareSelect($frequency))
            ->whereBetween('created_at', [$from->format('Y-m-d'), $to->format('Y-m-d 23:59:59')])
            ->where('key', 'unsubscribe_reason')
            ->groupBy($groupBy)
            ->orderBy($orderBy, 'ASC')
            ->get();

        return $this->getResult($period, $items);
    }

    public function funnelStat($funnelId, $sequences = [], $from = false, $to = false)
    {
        if (!$sequences) {
            $sequences = FunnelSequence::where('funnel_id', $funnelId)
                ->orderBy('sequence', 'ASC')
                ->get();
        }

        if (!$sequences || $sequences->isEmpty()) {
            return [];
        }

        $sequenceIds = $sequences->pluck('id')->toArray();

        $totalSubscriberCount = FunnelSubscriber::where('funnel_id', $funnelId)
            ->distinct()
            ->count('subscriber_id');

        $items = FunnelMetric::select([
            'sequence_id',
            'benchmark_currency',
            'benchmark_value',
            fluentCrmDb()->raw('COUNT(sequence_id) AS count'),
        ])
            ->groupBy('sequence_id')
            ->whereIn('sequence_id', $sequenceIds)
            ->get()->keyBy('sequence_id');

        $totalRevenue = FunnelMetric::select([
            fluentCrmDb()->raw('SUM(benchmark_value) AS benchmark_total'),
        ])->whereIn('sequence_id', $sequenceIds)->first();

        if ($totalRevenue && $totalRevenue->benchmark_total) {
            $totalRevenue = $totalRevenue->benchmark_total / 100;
        } else {
            $totalRevenue = 0;
        }

        $formattedReports = [
            [
                'label'               => __('Entrance', 'fluent-crm'),
                'count'               => $totalSubscriberCount,
                'sequence_id'         => 0,
                'type'                => 'root',
                'percent'             => 100,
                'percent_text'        => 100,
                'previous_step_count' => $totalSubscriberCount,
                'drop_count'          => 0,
                'drop_percent'        => 0
            ]
        ];

        $currency = 'USD';
        $prevCount = $totalSubscriberCount;
        foreach ($sequences as $sequence) {
            if (empty($items[$sequence->id])) {
                continue;
            }

            $count = ($items[$sequence->id]->count) ? $items[$sequence->id]->count : 0;
            $dropCount = $prevCount - $count;
            $percent = ($totalSubscriberCount) ? ceil(($count / $totalSubscriberCount) * 100) : 0;

            $report = [
                'label'               => $sequence->title,
                'count'               => intval($count),
                'sequence_id'         => $sequence->id,
                'type'                => $sequence->type,
                'percent'             => $percent > 100 ? 100 : $percent,
                'percent_text'        => $percent,
                'previous_step_count' => $prevCount,
                'drop_count'          => $dropCount,
                'drop_percent'        => ($dropCount && $count && $prevCount) ? floor((1 - ($count / $prevCount)) * 100) : 0
            ];

            if ($sequence->action_name == 'send_custom_email') {
                // Calculate the revenue of this campaign
                $refCampaign = Arr::get($sequence->settings, 'reference_campaign');
                if ($refCampaign) {
                    if ($revenue = fluentcrm_get_campaign_meta($refCampaign, '_campaign_revenue')) {
                        $revs = [];
                        foreach ($revenue->value as $currency => $cents) {
                            if (is_numeric($cents) && $cents && $currency !== 'orderIds') {
                                $money = $cents / 100;
                                $money = number_format($money, (is_int($money)) ? 0 : 2);
                                $revs[] = strtoupper($currency) . ' ' . $money;
                            }
                        }
                        if ($revs) {
                            $report['revenues'] = $revs;
                        }
                    }

                    $report['link_clicks'] = CampaignUrlMetric::where('campaign_id', $refCampaign)
                        ->where('type', 'click')
                        ->count();

                    $report['email_opens'] = CampaignEmail::where('campaign_id', $refCampaign)
                        ->where('is_open', 1)
                        ->count();
                }

            }

            $formattedReports[] = $report;

            if ($items[$sequence->id]->benchmark_value > 0) {
                $currency = $items[$sequence->id]->benchmark_currency;
            }

        }

        return [
            'metrics'                 => $formattedReports,
            'total_revenue'           => $totalRevenue,
            'total_revenue_formatted' => number_format($totalRevenue, 2, '.', ' '),
            'revenue_currency'        => $currency
        ];
    }

    public function getEmailPerformance($from = null, $to = null)
    {
        $from = $this->makeFromDate($from);
        $to = $this->makeToDate($to);

        // One range scan with conditional aggregates instead of four separate ones.
        $totals = fluentCrmDb()->table('fc_campaign_emails')
            ->whereIn('status', ['sent', 'bounced'])
            ->whereBetween('scheduled_at', [$from->format('Y-m-d'), $to->format('Y-m-d 23:59:59')])
            ->select([
                fluentCrmDb()->raw("SUM(CASE WHEN status = 'sent' THEN 1 ELSE 0 END) AS sent"),
                fluentCrmDb()->raw("SUM(CASE WHEN status = 'bounced' THEN 1 ELSE 0 END) AS bounced"),
                fluentCrmDb()->raw("SUM(CASE WHEN status = 'sent' AND is_open = 1 THEN 1 ELSE 0 END) AS opened"),
                fluentCrmDb()->raw("SUM(CASE WHEN status = 'sent' AND click_counter > 0 THEN 1 ELSE 0 END) AS clicked"),
            ])
            ->first();

        $sent = $totals ? (int)$totals->sent : 0;
        $bounced = $totals ? (int)$totals->bounced : 0;
        $opened = $totals ? (int)$totals->opened : 0;
        $clicked = $totals ? (int)$totals->clicked : 0;

        $delivered = max(0, $sent - $bounced);

        $deliveryBase = $sent + $bounced;
        $rateBase = $delivered;

        $deliveryPercent = $deliveryBase ? round(($delivered / $deliveryBase) * 100, 2) : 0;
        $bouncePercent = $deliveryBase ? round(($bounced / $deliveryBase) * 100, 2) : 0;
        $openPercent = $rateBase ? round(($opened / $rateBase) * 100, 2) : 0;
        $clickPercent = $rateBase ? round(($clicked / $rateBase) * 100, 2) : 0;

        return [
            'totals'      => [
                'sent'      => (int)$sent,
                'delivered' => (int)$delivered,
                'opened'    => (int)$opened,
                'clicked'   => (int)$clicked,
                'bounced'   => (int)$bounced,
            ],
            'percentages' => [
                'delivered' => $deliveryPercent,
                'opened'    => $openPercent,
                'clicked'   => $clickPercent,
                'bounced'   => $bouncePercent,
            ]
        ];
    }
}
