<?php

namespace App\Services\AdminDashboardOverview;

use App\Models\Transaction;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

class TransactionOverview extends BaseOverview
{
    /**
     * Generates an overview of transactions based on the specified time period and transaction matters.
     *
     * The overview can be grouped by day, month, or year, depending on the 'overview_by' parameter.
     * It fetches transaction counts grouped by the specified time period and transaction matter,
     * which can be either booking, commission, purchase, or withdrawal.
     *
     * @return array An array containing:
     *               - 'series': An array of series data for each transaction matter.
     *               - 'categories': Time period categories (days, months, or years).
     *               - 'colors': An array of colors for chart representation.
     */
    public function overview()
    {
        $overviewBy = request('overview_by', 'month');
        $year = request('year', now()->year);
        $query = Transaction::query();
        $dateColumn = 'created_at';

        [$data, $groupBy, $selectRaw] = $this->getQueryParameters($overviewBy, $dateColumn);

        $transactions = $query->selectRaw("$selectRaw, type, COUNT(id) as total")
            ->when($overviewBy === 'day', function ($query) use ($dateColumn) { // day query condition
                $from_date = request('from_date', now()->subDays(7)->toDateString());
                $to_date = request('to_date', now()->toDateString());
                $query->whereBetween($dateColumn, [
                    Carbon::parse($from_date)->startOfDay(),
                    Carbon::parse($to_date)->endOfDay(),
                ]);
            })
            ->when($year, function ($query) use ($year, $dateColumn) { // year query condition
                $query->whereYear($dateColumn, $year);
            })
            ->groupBy($groupBy, 'type')
            ->get()
            ->groupBy('type');

        $series = $this->initializeSeries($data);

        foreach ($transactions as $matter => $transactionGroup) {
            $seriesIndex = $matter;
            foreach ($transactionGroup as $transaction) {
                $index = $this->getArrayIndexByOverviewBy($overviewBy, $transaction->{$overviewBy}, $data);
                $series[$seriesIndex]['data'][$index] = $transaction->total;
            }
        }

        return [
            'series' => array_values($series),
            'categories' => $data,
            'colors' => ['#F44336', '#4CAF50'],
        ];
    }

    /**
     * Get the query parameters based on the overview_by value.
     *
     * @param  string  $overviewBy
     * @param  string  $dateColumn
     * @return array [$data, $groupBy, $selectRaw]
     */
    private function getQueryParameters($overviewBy, $dateColumn)
    {
        switch ($overviewBy) {
            case 'month':
                return [
                    $this->getMonths(),
                    [DB::raw("MONTH($dateColumn)")],
                    "MONTH($dateColumn) as month",
                ];
            case 'year':
                return [
                    $this->getYears(),
                    [DB::raw("YEAR($dateColumn)")],
                    "YEAR($dateColumn) as year",
                ];
            default:
                $fromDate = request('from_date', now()->subDays(7)->toDateString());
                $toDate = request('to_date', now()->toDateString());

                return [
                    $this->getDays($fromDate, $toDate),
                    [DB::raw("DATE($dateColumn)")],
                    "DATE($dateColumn) as day",
                ];
        }
    }

    /**
     * Initializes the series data structure for transaction matters.
     *
     * @param  array  $data  The data used to determine the length of the series.
     * @return array An associative array where keys are transaction matters and values are arrays
     *               containing the name, type, and initialized data for each matter.
     */
    private function initializeSeries($data)
    {
        $types = [
            'debit',
            'credit',
        ];

        return collect($types)->mapWithKeys(function ($type) use ($data) {
            return [
                $type => [
                    'name' => ucwords(str_replace('_', ' ', $type)),
                    'type' => 'area',
                    'data' => array_fill(0, count($data), 0),
                ],
            ];
        })->toArray();

    }
}
