<?php

namespace App\Services;

class PaymentHealthReportService
{
    /**
     * Build payment health summary for a single date.
     *
     * @return array<string, mixed>
     */
    public function summarizeDate(string $date): array
    {
        $logPath = storage_path("logs/payments/payment-{$date}.log");
        if (!is_file($logPath)) {
            return $this->emptySummary($date, false);
        }

        $lines = @file($logPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        if ($lines === false) {
            return $this->emptySummary($date, false);
        }

        return $this->computeStats($date, $lines);
    }

    /**
     * Build summaries for a date range (inclusive).
     *
     * @return array<int, array<string, mixed>>
     */
    public function summarizeRange(string $startDate, string $endDate): array
    {
        $start = strtotime($startDate);
        $end = strtotime($endDate);

        if ($start === false || $end === false || $start > $end) {
            return [];
        }

        $rows = [];
        for ($ts = $start; $ts <= $end; $ts += 86400) {
            $rows[] = $this->summarizeDate(date('Y-m-d', $ts));
        }

        return $rows;
    }

    /**
     * @param array<int, array<string, mixed>> $summaries
     * @return array<string, mixed>
     */
    public function aggregate(array $summaries): array
    {
        $aggregate = [
            'captured_events' => 0,
            'failed_events' => 0,
            'verify_stock_error_events' => 0,
            'backend_state_race_events' => 0,
            'gateway_timeout_events' => 0,
            'customer_decline_events' => 0,
            'webhook_order_not_found_events' => 0,
            'backend_failure_events' => 0,
            'already_paid_verification_skips' => 0,
            'recovered_non_payable_events' => 0,
            'attempted_unique_orders' => 0,
            'captured_unique_orders' => 0,
            'failed_unique_orders' => 0,
            'hard_fail_unique_orders' => 0,
            'hard_fail_rate_pct' => 0.0,
            'backend_failure_rate_pct' => 0.0,
            'days_with_logs' => 0,
            'days_total' => count($summaries),
        ];

        foreach ($summaries as $row) {
            if (!empty($row['log_exists'])) {
                $aggregate['days_with_logs']++;
            }

            foreach ([
                'captured_events',
                'failed_events',
                'verify_stock_error_events',
                'backend_state_race_events',
                'gateway_timeout_events',
                'customer_decline_events',
                'webhook_order_not_found_events',
                'backend_failure_events',
                'already_paid_verification_skips',
                'recovered_non_payable_events',
                'attempted_unique_orders',
                'captured_unique_orders',
                'failed_unique_orders',
                'hard_fail_unique_orders',
            ] as $key) {
                $aggregate[$key] += (int) ($row[$key] ?? 0);
            }
        }

        if ($aggregate['attempted_unique_orders'] > 0) {
            $aggregate['hard_fail_rate_pct'] = round(
                ($aggregate['hard_fail_unique_orders'] * 100) / $aggregate['attempted_unique_orders'],
                2
            );
            $aggregate['backend_failure_rate_pct'] = round(
                ($aggregate['backend_failure_events'] * 100) / $aggregate['attempted_unique_orders'],
                2
            );
        }

        return $aggregate;
    }

    /**
     * @param array<int, string> $lines
     * @return array<string, mixed>
     */
    private function computeStats(string $date, array $lines): array
    {
        $capturedEvents = 0;
        $failedEvents = 0;
        $verifyStockErrorEvents = 0;
        $backendStateRaceEvents = 0;
        $gatewayTimeoutEvents = 0;
        $customerDeclineEvents = 0;
        $webhookOrderNotFoundEvents = 0;
        $alreadyPaidVerificationSkips = 0;
        $recoveredNonPayableEvents = 0;

        /** @var array<int, true> $capturedOrders */
        $capturedOrders = [];
        /** @var array<int, true> $failedOrders */
        $failedOrders = [];
        /** @var array<string, int> $failureReasons */
        $failureReasons = [];

        foreach ($lines as $line) {
            if (str_contains($line, 'Razorpay webhook: Payment captured')) {
                $capturedEvents++;
                $orderId = $this->extractIntContext('order_id', $line);
                if ($orderId !== null) {
                    $capturedOrders[$orderId] = true;
                }
            }

            if (str_contains($line, 'Razorpay webhook: Payment failed')) {
                $failedEvents++;
                $orderId = $this->extractIntContext('order_id', $line);
                if ($orderId !== null) {
                    $failedOrders[$orderId] = true;
                }

                $bucket = $this->extractStringContext('failure_bucket', $line);
                if ($bucket === 'gateway_timeout') {
                    $gatewayTimeoutEvents++;
                } elseif ($bucket === 'customer_decline') {
                    $customerDeclineEvents++;
                } else {
                    $reasonLower = strtolower((string) $this->extractStringContext('error', $line));
                    if (
                        str_contains($reasonLower, 'in time')
                        || str_contains($reasonLower, 'delay')
                        || str_contains($reasonLower, 'timeout')
                    ) {
                        $gatewayTimeoutEvents++;
                    } else {
                        $customerDeclineEvents++;
                    }
                }

                $reason = $this->extractStringContext('error', $line);
                if ($reason !== null && $reason !== '') {
                    $failureReasons[$reason] = ($failureReasons[$reason] ?? 0) + 1;
                }
            }

            if (str_contains($line, 'Razorpay verify failed with stock error')) {
                $verifyStockErrorEvents++;
                $backendStateRaceEvents++;
            }

            if (str_contains($line, 'Payment verification skipped: Order already paid')) {
                $alreadyPaidVerificationSkips++;
            }

            if (str_contains($line, 'recovered non-payable state using captured payment')) {
                $recoveredNonPayableEvents++;
                $backendStateRaceEvents++;
            }

            if (str_contains($line, 'recovered after reservation expiry using captured payment')) {
                $backendStateRaceEvents++;
            }

            if (str_contains($line, 'Razorpay webhook: Order not found')) {
                $webhookOrderNotFoundEvents++;
            }
        }

        arsort($failureReasons);

        $capturedOrderIds = array_keys($capturedOrders);
        $failedOrderIds = array_keys($failedOrders);
        $attemptedOrderIds = array_values(array_unique(array_merge($capturedOrderIds, $failedOrderIds)));
        $hardFailedOrderIds = array_values(array_diff($failedOrderIds, $capturedOrderIds));
        $hardFailRate = count($attemptedOrderIds) > 0
            ? round((count($hardFailedOrderIds) * 100) / count($attemptedOrderIds), 2)
            : 0.0;

        $backendFailureEvents = $backendStateRaceEvents + $webhookOrderNotFoundEvents;

        return [
            'date' => $date,
            'gateway' => 'razorpay',
            'log_exists' => true,
            'captured_events' => $capturedEvents,
            'failed_events' => $failedEvents,
            'verify_stock_error_events' => $verifyStockErrorEvents,
            'backend_state_race_events' => $backendStateRaceEvents,
            'gateway_timeout_events' => $gatewayTimeoutEvents,
            'customer_decline_events' => $customerDeclineEvents,
            'webhook_order_not_found_events' => $webhookOrderNotFoundEvents,
            'backend_failure_events' => $backendFailureEvents,
            'already_paid_verification_skips' => $alreadyPaidVerificationSkips,
            'recovered_non_payable_events' => $recoveredNonPayableEvents,
            'attempted_unique_orders' => count($attemptedOrderIds),
            'captured_unique_orders' => count($capturedOrderIds),
            'failed_unique_orders' => count($failedOrderIds),
            'hard_fail_unique_orders' => count($hardFailedOrderIds),
            'hard_fail_rate_pct' => $hardFailRate,
            'backend_failure_rate_pct' => count($attemptedOrderIds) > 0
                ? round(($backendFailureEvents * 100) / count($attemptedOrderIds), 2)
                : 0.0,
            'top_failure_reasons' => array_slice($failureReasons, 0, 5, true),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function emptySummary(string $date, bool $logExists): array
    {
        return [
            'date' => $date,
            'gateway' => 'razorpay',
            'log_exists' => $logExists,
            'captured_events' => 0,
            'failed_events' => 0,
            'verify_stock_error_events' => 0,
            'backend_state_race_events' => 0,
            'gateway_timeout_events' => 0,
            'customer_decline_events' => 0,
            'webhook_order_not_found_events' => 0,
            'backend_failure_events' => 0,
            'already_paid_verification_skips' => 0,
            'recovered_non_payable_events' => 0,
            'attempted_unique_orders' => 0,
            'captured_unique_orders' => 0,
            'failed_unique_orders' => 0,
            'hard_fail_unique_orders' => 0,
            'hard_fail_rate_pct' => 0.0,
            'backend_failure_rate_pct' => 0.0,
            'top_failure_reasons' => [],
        ];
    }

    private function extractIntContext(string $key, string $line): ?int
    {
        if (preg_match('/"' . preg_quote($key, '/') . '":\s*(\d+)/', $line, $matches)) {
            return (int) $matches[1];
        }

        return null;
    }

    private function extractStringContext(string $key, string $line): ?string
    {
        if (preg_match('/"' . preg_quote($key, '/') . '":"([^"]*)"/', $line, $matches)) {
            return $matches[1];
        }

        return null;
    }
}
