<?php

namespace App\Services;

use App\Support\IntegrationConfigResolver;
use Carbon\Carbon;
use Illuminate\Http\Client\Pool;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

class OwnerDashboardAggregator
{
    /**
     * @return array{date_from: string, date_to: string, esborder: array, sysreflex: array, combined: array, errors: array<int, array{source: string, message: string}>}
     */
    public function aggregate(Carbon $dateFrom, Carbon $dateTo): array
    {
        $from = $dateFrom->toDateString();
        $to = $dateTo->toDateString();
        $cacheTtl = IntegrationConfigResolver::aggregationCacheSeconds();
        $cacheKey = "owner_dashboard:{$from}:{$to}";

        if ($cacheTtl > 0) {
            return Cache::remember($cacheKey, $cacheTtl, fn () => $this->fetchLive($from, $to));
        }

        return $this->fetchLive($from, $to);
    }

    /**
     * @return array{date_from: string, date_to: string, esborder: array, sysreflex: array, combined: array, errors: array<int, array{source: string, message: string}>}
     */
    private function fetchLive(string $from, string $to): array
    {
        $errors = [];
        $esb = $this->emptyEsbPayload($from, $to);
        $ref = $this->emptyReflexPayload($from, $to);

        $esbUrl = IntegrationConfigResolver::esborderBaseUrl();
        $esbKey = IntegrationConfigResolver::esborderApiKey();
        $refUrl = IntegrationConfigResolver::sysreflexBaseUrl();
        $refTok = IntegrationConfigResolver::sysreflexApiToken();

        $canEsb = $esbUrl !== '' && $esbKey !== '';
        $canRef = $refUrl !== '' && $refTok !== '';

        if ($canEsb && $canRef) {
            $responses = Http::pool(function (Pool $pool) use ($esbUrl, $esbKey, $refUrl, $refTok, $from, $to) {
                $pool->as('consolidated')
                    ->timeout((int) config('integrations.esborder.timeout_seconds', 20))
                    ->withHeaders([
                        'Accept' => 'application/json',
                        'Authorization' => 'Bearer '.$esbKey,
                    ])
                    ->get($esbUrl.'/api/reports/consolidated', [
                        'date_from' => $from,
                        'date_to' => $to,
                    ]);

                $pool->as('sales')
                    ->timeout((int) config('integrations.esborder.timeout_seconds', 20))
                    ->withHeaders([
                        'Accept' => 'application/json',
                        'Authorization' => 'Bearer '.$esbKey,
                    ])
                    ->get($esbUrl.'/api/reports/sales', [
                        'date_from' => $from,
                        'date_to' => $to,
                    ]);

                $pool->as('top_items')
                    ->timeout((int) config('integrations.esborder.timeout_seconds', 20))
                    ->withHeaders([
                        'Accept' => 'application/json',
                        'Authorization' => 'Bearer '.$esbKey,
                    ])
                    ->get($esbUrl.'/api/reports/top-items', [
                        'date_from' => $from,
                        'date_to' => $to,
                    ]);

                $pool->as('reflex')
                    ->timeout((int) config('integrations.sysreflex.timeout_seconds', 20))
                    ->withHeaders([
                        'Accept' => 'application/json',
                        'Authorization' => 'Bearer '.$refTok,
                    ])
                    ->get($refUrl.'/api/owner/summary', [
                        'date_from' => $from,
                        'date_to' => $to,
                    ]);
            });

            $esb = $this->parseEsbResponse($responses['consolidated'] ?? null, $from, $to, $errors);
            $esb['daily'] = $this->parseEsbSalesDaily($responses['sales'] ?? null, $errors);
            $esb['top_items'] = $this->parseEsbTopItems($responses['top_items'] ?? null, $errors);
            $ref = $this->parseReflexResponse($responses['reflex'] ?? null, $from, $to, $errors);
        } else {
            if ($canEsb) {
                try {
                    $timeout = (int) config('integrations.esborder.timeout_seconds', 20);
                    $headers = [
                        'Accept' => 'application/json',
                        'Authorization' => 'Bearer '.$esbKey,
                    ];
                    $r = Http::timeout($timeout)
                        ->withHeaders($headers)
                        ->get($esbUrl.'/api/reports/consolidated', [
                            'date_from' => $from,
                            'date_to' => $to,
                        ]);
                    $esb = $this->parseEsbResponse($r, $from, $to, $errors);
                    $r2 = Http::timeout($timeout)
                        ->withHeaders($headers)
                        ->get($esbUrl.'/api/reports/sales', [
                            'date_from' => $from,
                            'date_to' => $to,
                        ]);
                    $esb['daily'] = $this->parseEsbSalesDaily($r2, $errors);
                    $r3 = Http::timeout($timeout)
                        ->withHeaders($headers)
                        ->get($esbUrl.'/api/reports/top-items', [
                            'date_from' => $from,
                            'date_to' => $to,
                        ]);
                    $esb['top_items'] = $this->parseEsbTopItems($r3, $errors);
                } catch (\Throwable $e) {
                    $errors[] = ['source' => 'esborder', 'message' => $e->getMessage()];
                }
            } else {
                $errors[] = ['source' => 'esborder', 'message' => 'URL atau API key SysOrder belum diatur (.env atau menu Pengaturan).'];
            }

            if ($canRef) {
                try {
                    $r = Http::timeout((int) config('integrations.sysreflex.timeout_seconds', 20))
                        ->withHeaders([
                            'Accept' => 'application/json',
                            'Authorization' => 'Bearer '.$refTok,
                        ])
                        ->get($refUrl.'/api/owner/summary', [
                            'date_from' => $from,
                            'date_to' => $to,
                        ]);
                    $ref = $this->parseReflexResponse($r, $from, $to, $errors);
                } catch (\Throwable $e) {
                    $errors[] = ['source' => 'sysreflex', 'message' => $e->getMessage()];
                }
            } else {
                $errors[] = ['source' => 'sysreflex', 'message' => 'URL atau token sysReflex belum diatur (.env atau menu Pengaturan).'];
            }
        }

        $combined = $this->combine($esb, $ref);

        return [
            'date_from' => $from,
            'date_to' => $to,
            'esborder' => $esb,
            'sysreflex' => $ref,
            'combined' => $combined,
            'errors' => $errors,
        ];
    }

    private function emptyEsbPayload(string $from, string $to): array
    {
        return [
            'ok' => false,
            'date_from' => $from,
            'date_to' => $to,
            'revenue' => 0.0,
            'order_count' => 0,
            'cogs' => 0.0,
            'gross_profit' => 0.0,
            'per_outlet' => [],
            'daily' => [],
            'top_items' => [],
        ];
    }

    private function emptyReflexPayload(string $from, string $to): array
    {
        return [
            'ok' => false,
            'date_from' => $from,
            'date_to' => $to,
            'gross_revenue' => 0.0,
            'transaction_count' => 0,
            'total_commission' => 0.0,
            'margin_after_commission' => 0.0,
            'outlet_allocated_share' => 0.0,
            'daily' => [],
            'top_services' => [],
        ];
    }

    private function parseEsbResponse(?Response $response, string $from, string $to, array &$errors): array
    {
        $base = $this->emptyEsbPayload($from, $to);
        if ($response === null) {
            $errors[] = ['source' => 'esborder', 'message' => 'Tidak ada respons (pool).'];

            return $base;
        }
        if (! $response->successful()) {
            $errors[] = ['source' => 'esborder', 'message' => 'HTTP '.$response->status().': '.$response->body()];

            return $base;
        }
        $j = $response->json();
        if (! is_array($j)) {
            $errors[] = ['source' => 'esborder', 'message' => 'JSON tidak valid.'];

            return $base;
        }
        $gt = $j['grand_total'] ?? [];
        $base['ok'] = true;
        $base['revenue'] = (float) ($gt['revenue'] ?? 0);
        $base['order_count'] = (int) ($gt['order_count'] ?? 0);
        $base['cogs'] = (float) ($gt['cogs'] ?? 0);
        $base['gross_profit'] = (float) ($gt['gross_profit'] ?? ($base['revenue'] - $base['cogs']));
        $base['per_outlet'] = is_array($j['per_outlet'] ?? null) ? $j['per_outlet'] : [];

        return $base;
    }

    /**
     * @return list<array{date: string, revenue: float, cogs: float, gross_profit: float, order_count: int}>
     */
    private function parseEsbSalesDaily(?Response $response, array &$errors): array
    {
        if ($response === null || ! $response->successful()) {
            if ($response !== null && ! $response->successful()) {
                $errors[] = ['source' => 'esborder_sales', 'message' => 'Laporan harian SysOrder tidak tersedia (HTTP '.$response->status().').'];
            }

            return [];
        }
        $j = $response->json();
        if (! is_array($j) || ! isset($j['summary']) || ! is_array($j['summary'])) {
            return [];
        }
        $byDate = [];
        foreach ($j['summary'] as $row) {
            if (! is_array($row) || ! isset($row['date'])) {
                continue;
            }
            $d = (string) $row['date'];
            if (! isset($byDate[$d])) {
                $byDate[$d] = [
                    'date' => $d,
                    'revenue' => 0.0,
                    'cogs' => 0.0,
                    'gross_profit' => 0.0,
                    'order_count' => 0,
                ];
            }
            $byDate[$d]['revenue'] += (float) ($row['revenue'] ?? 0);
            $byDate[$d]['cogs'] += (float) ($row['cogs'] ?? 0);
            $byDate[$d]['gross_profit'] += (float) ($row['gross_profit'] ?? 0);
            $byDate[$d]['order_count'] += (int) ($row['order_count'] ?? 0);
        }
        ksort($byDate);

        return array_values($byDate);
    }

    /**
     * @return list<array<string, mixed>>
     */
    private function parseEsbTopItems(?Response $response, array &$errors): array
    {
        if ($response === null || ! $response->successful()) {
            if ($response !== null && ! $response->successful()) {
                $errors[] = ['source' => 'esborder_top_items', 'message' => 'Laporan item SysOrder tidak tersedia (HTTP '.$response->status().'). Perbarui backend SysOrder (POS) atau abaikan jika endpoint belum di-deploy.'];
            }

            return [];
        }
        $j = $response->json();
        if (! is_array($j) || ! isset($j['items']) || ! is_array($j['items'])) {
            return [];
        }
        $out = [];
        foreach ($j['items'] as $row) {
            if (is_array($row) && isset($row['product_name'])) {
                $out[] = $row;
            }
        }

        return $out;
    }

    private function parseReflexResponse(?Response $response, string $from, string $to, array &$errors): array
    {
        $base = $this->emptyReflexPayload($from, $to);
        if ($response === null) {
            $errors[] = ['source' => 'sysreflex', 'message' => 'Tidak ada respons (pool).'];

            return $base;
        }
        if (! $response->successful()) {
            $errors[] = ['source' => 'sysreflex', 'message' => 'HTTP '.$response->status().': '.$response->body()];

            return $base;
        }
        $j = $response->json();
        if (! is_array($j)) {
            $errors[] = ['source' => 'sysreflex', 'message' => 'JSON tidak valid.'];

            return $base;
        }
        $base['ok'] = true;
        $base['gross_revenue'] = (float) ($j['gross_revenue'] ?? 0);
        $base['transaction_count'] = (int) ($j['transaction_count'] ?? 0);
        $base['total_commission'] = (float) ($j['total_commission'] ?? 0);
        $base['margin_after_commission'] = (float) ($j['margin_after_commission'] ?? 0);
        $base['outlet_allocated_share'] = (float) ($j['outlet_allocated_share'] ?? 0);
        $base['daily'] = [];
        if (isset($j['daily']) && is_array($j['daily'])) {
            foreach ($j['daily'] as $row) {
                if (is_array($row)) {
                    $base['daily'][] = $row;
                }
            }
        }
        $base['top_services'] = [];
        if (isset($j['top_services']) && is_array($j['top_services'])) {
            foreach ($j['top_services'] as $row) {
                if (is_array($row) && isset($row['service_name'])) {
                    $base['top_services'][] = $row;
                }
            }
        }

        return $base;
    }

    /**
     * Gabungkan deret harian FnB + refleksologi per tanggal (untuk pemantauan satu tabel).
     *
     * @param  list<array<string, mixed>>  $esbDaily
     * @param  list<array<string, mixed>>  $refDaily
     * @return list<array<string, mixed>>
     */
    private function mergeDailySeries(array $esbDaily, array $refDaily): array
    {
        $by = [];
        foreach ($esbDaily as $row) {
            $d = (string) ($row['date'] ?? '');
            if ($d === '') {
                continue;
            }
            $by[$d] = [
                'date' => $d,
                'fnb_revenue' => (float) ($row['revenue'] ?? 0),
                'fnb_gross_profit' => (float) ($row['gross_profit'] ?? 0),
                'fnb_order_count' => (int) ($row['order_count'] ?? 0),
                'reflex_gross_revenue' => 0.0,
                'reflex_margin' => 0.0,
                'reflex_transaction_count' => 0,
            ];
        }
        foreach ($refDaily as $row) {
            $d = (string) ($row['date'] ?? '');
            if ($d === '') {
                continue;
            }
            if (! isset($by[$d])) {
                $by[$d] = [
                    'date' => $d,
                    'fnb_revenue' => 0.0,
                    'fnb_gross_profit' => 0.0,
                    'fnb_order_count' => 0,
                    'reflex_gross_revenue' => 0.0,
                    'reflex_margin' => 0.0,
                    'reflex_transaction_count' => 0,
                ];
            }
            $by[$d]['reflex_gross_revenue'] = (float) ($row['gross_revenue'] ?? 0);
            $by[$d]['reflex_margin'] = (float) ($row['margin_after_commission'] ?? 0);
            $by[$d]['reflex_transaction_count'] = (int) ($row['transaction_count'] ?? 0);
        }
        ksort($by);
        $list = array_values($by);
        foreach ($list as $i => $row) {
            $list[$i]['combined_revenue'] = round($row['fnb_revenue'] + $row['reflex_gross_revenue'], 2);
            $list[$i]['combined_contribution'] = round($row['fnb_gross_profit'] + $row['reflex_margin'], 2);
        }

        return $list;
    }

    private function combine(array $esb, array $ref): array
    {
        $fnBRevenue = (float) ($esb['revenue'] ?? 0);
        $fnBCogs = (float) ($esb['cogs'] ?? 0);
        $fnBGross = (float) ($esb['gross_profit'] ?? 0);

        $refGross = (float) ($ref['gross_revenue'] ?? 0);
        $refComm = (float) ($ref['total_commission'] ?? 0);
        $refMargin = (float) ($ref['margin_after_commission'] ?? 0);

        return [
            'total_revenue_all_businesses' => round($fnBRevenue + $refGross, 2),
            'total_gross_profit_combined' => round($fnBGross + $refMargin, 2),
            'note' => 'Kombinasi: laba kotor FnB (SysOrder) + margin setelah komisi (sysReflex). Bukan laporan akuntansi penuh.',
            'simple_pl' => [
                'fnb_revenue' => round($fnBRevenue, 2),
                'fnb_cogs' => round($fnBCogs, 2),
                'fnb_gross_profit' => round($fnBGross, 2),
                'reflex_gross_revenue' => round($refGross, 2),
                'reflex_commission' => round($refComm, 2),
                'reflex_margin_after_commission' => round($refMargin, 2),
                'total_revenue_lines' => round($fnBRevenue + $refGross, 2),
                'combined_contribution' => round($fnBGross + $refMargin, 2),
                'disclaimer' => 'Laba rugi sederhana untuk pemantauan: SysOrder (FnB) memakai laba kotor (revenue − HPP); refleksologi memakai margin setelah komisi therapist. Belum termasuk biaya operasional lain (sewa, gaji admin, pajak, dll.).',
            ],
            'daily_combined' => $this->mergeDailySeries($esb['daily'] ?? [], $ref['daily'] ?? []),
        ];
    }
}
