<?php

namespace App\Http\Controllers;

use App\Services\FinanceApiService;
use App\Services\FinanceSyncService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;

class FinanceWebhookController extends Controller
{
    public function disabledPaymentRoute(Request $request, FinanceApiService $finance)
    {
        $user = $request->user();
        $schoolId = optional($user)->school_id ?: $request->route('school_id') ?: $request->input('school_id');
        $admissionNumber = trim((string) (
            $request->input('admission_number')
            ?: optional($user)->admission_number
            ?: optional(optional($user)->student)->admission_no
        ));
        $paymentUrl = $finance->payUrl($admissionNumber, $schoolId);

        return response()->json([
            'status' => $paymentUrl !== '' ? 'redirect' : 'disabled',
            'message' => 'Student payments are processed only in the WordPress finance portal.',
            'payment_url' => $paymentUrl,
        ], $paymentUrl !== '' ? 200 : 409);
    }

    public function ping(Request $request, FinanceApiService $finance)
    {
        if (!$this->hasValidSignature($request, $finance->allWebhookSecrets()) || !$this->hasKnownSource($request, $finance->knownSourceUrls())) {
            Log::warning('Finance ping rejected.', [
                'ip' => $request->ip(),
                'source' => $request->header('X-RBYTES-SOURCE'),
            ]);

            return response()->json(['message' => 'Invalid signature'], 401);
        }

        return response()->json(['status' => 'ok']);
    }

    public function handle(Request $request, FinanceApiService $finance, FinanceSyncService $sync)
    {
        $payload = $request->json()->all();
        $admissionNumber = trim((string) data_get($payload, 'admission_number', ''));
        $eventId = trim((string) data_get($payload, 'event_id', ''));
        $transactionId = trim((string) data_get($payload, 'transaction_id', ''));
        $transactionId = $transactionId === '0' ? '' : $transactionId;

        $user = null;
        if ($admissionNumber !== '' && Schema::hasTable('users')) {
            $user = DB::table('users')->where('admission_number', $admissionNumber)->first();
        }

        if (!$this->hasValidSignature($request, $finance->webhookSecrets(optional($user)->school_id))) {
            Log::warning('Finance webhook rejected: invalid signature.', [
                'ip' => $request->ip(),
                'admission_number' => $admissionNumber,
                'event_id' => $eventId,
                'transaction_id' => $transactionId,
            ]);

            return response()->json(['message' => 'Invalid signature'], 401);
        }

        if (!$this->hasKnownSource($request, $finance->knownSourceUrls(optional($user)->school_id))) {
            Log::warning('Finance webhook rejected: unknown source.', [
                'ip' => $request->ip(),
                'source' => $request->header('X-RBYTES-SOURCE'),
                'admission_number' => $admissionNumber,
            ]);

            return response()->json(['message' => 'Unknown source'], 403);
        }

        if (!$user) {
            Log::warning('Finance webhook rejected: student not found.', [
                'ip' => $request->ip(),
                'admission_number' => $admissionNumber,
            ]);

            return response()->json(['message' => 'Student not found'], 404);
        }

        if ($this->isDuplicateEvent($eventId, $transactionId)) {
            return response()->json(['status' => 'ok', 'duplicate' => true]);
        }

        if (!$this->recordWebhookEvent($payload, $eventId, $transactionId, $admissionNumber)) {
            return response()->json(['status' => 'ok', 'duplicate' => true]);
        }

        $updates = [];
        if (Schema::hasColumn('users', 'finance_balance')) {
            $updates['finance_balance'] = (float) data_get($payload, 'balance', 0);
        }
        if (Schema::hasColumn('users', 'finance_status')) {
            $updates['finance_status'] = (string) data_get($payload, 'status', '');
        }
        $userType = $this->normalizeUserType(data_get($payload, 'user_type'));
        if ($userType !== null && Schema::hasColumn('users', 'user_type')) {
            $updates['user_type'] = $userType;
        }
        $currencyCode = strtoupper(trim((string) data_get($payload, 'currency', '')));
        if ($currencyCode !== '' && Schema::hasColumn('users', 'currency_code')) {
            $updates['currency_code'] = $currencyCode;
        }
        $currencySymbol = $this->cleanFinanceText(data_get($payload, 'symbol', ''));
        if ($currencySymbol !== '' && Schema::hasColumn('users', 'currency_symbol')) {
            $updates['currency_symbol'] = $currencySymbol;
        }
        if (Schema::hasColumn('users', 'last_payment_at')) {
            $updates['last_payment_at'] = now();
        }
        if (Schema::hasColumn('users', 'updated_at')) {
            $updates['updated_at'] = now();
        }

        if (!empty($updates)) {
            DB::table('users')->where('id', $user->id)->update($updates);
        }

        $this->touchLastWebhookAt(optional($user)->school_id);

        $sync->rememberWebhookSnapshot($admissionNumber, optional($user)->school_id, [
            'admission_number' => $admissionNumber,
            'mandatory_outstanding' => (float) data_get($payload, 'balance', 0),
            'status' => (string) data_get($payload, 'status', 'pending'),
            'user_type' => data_get($payload, 'user_type'),
            'currency' => data_get($payload, 'currency'),
            'symbol' => $this->cleanFinanceText(data_get($payload, 'symbol')),
            'formatted_balance' => $this->cleanFinanceText(data_get($payload, 'formatted_balance')),
            'formatted_optional_total' => $this->cleanFinanceText(data_get($payload, 'formatted_optional_total')),
            'formatted_total_paid' => $this->cleanFinanceText(data_get($payload, 'formatted_total_paid')),
        ]);

        return response()->json(['status' => 'ok']);
    }

    private function hasValidSignature(Request $request, array $secrets)
    {
        $signature = trim((string) $request->header('X-Rbytes-Signature', ''));
        $timestamp = trim((string) $request->header('X-Rbytes-Timestamp', ''));

        if ($signature === '' || $timestamp === '' || empty($secrets)) {
            return false;
        }

        if (!ctype_digit($timestamp) || abs(time() - (int) $timestamp) > (int) config('finance.signature_tolerance', 300)) {
            return false;
        }

        $signature = strpos($signature, 'sha256=') === 0 ? substr($signature, 7) : $signature;
        $body = $request->getContent();

        foreach ($secrets as $secret) {
            $expected = hash_hmac('sha256', $timestamp . '.' . $body, $secret);
            if (hash_equals($expected, $signature)) {
                return true;
            }
        }

        return false;
    }

    private function hasKnownSource(Request $request, array $knownSources)
    {
        $source = trim((string) $request->header('X-RBYTES-SOURCE', ''));
        if ($source === '') {
            return false;
        }

        $sourceHost = parse_url($source, PHP_URL_HOST);
        if (!$sourceHost) {
            return false;
        }

        foreach ($knownSources as $knownSource) {
            $knownHost = parse_url((string) $knownSource, PHP_URL_HOST);
            if ($knownHost && strtolower($knownHost) === strtolower($sourceHost)) {
                return true;
            }
        }

        return false;
    }

    private function isDuplicateEvent($eventId, $transactionId)
    {
        if (!Schema::hasTable('finance_webhook_events')) {
            return false;
        }

        if ($eventId === '' && $transactionId === '') {
            return false;
        }

        return DB::table('finance_webhook_events')
            ->where(function ($query) use ($eventId, $transactionId) {
                if ($eventId !== '') {
                    $query->where('event_id', $eventId);
                }

                if ($transactionId !== '') {
                    $method = $eventId !== '' ? 'orWhere' : 'where';
                    $query->{$method}('transaction_id', $transactionId);
                }
            })
            ->exists();
    }

    private function recordWebhookEvent(array $payload, $eventId, $transactionId, $admissionNumber)
    {
        if (!Schema::hasTable('finance_webhook_events')) {
            return true;
        }

        try {
            DB::table('finance_webhook_events')->insert([
                'event_id' => $eventId !== '' ? $eventId : ($transactionId !== '' ? 'transaction:' . $transactionId : sha1(json_encode($payload))),
                'transaction_id' => $transactionId !== '' ? $transactionId : null,
                'admission_number' => (string) $admissionNumber,
                'invoice_id' => data_get($payload, 'invoice_id'),
                'amount' => data_get($payload, 'amount'),
                'status' => data_get($payload, 'status'),
                'payload' => json_encode($payload),
                'received_at' => now(),
                'created_at' => now(),
                'updated_at' => now(),
            ]);

            return true;
        } catch (\Throwable $e) {
            if (strpos($e->getMessage(), 'Duplicate') !== false || strpos($e->getMessage(), 'UNIQUE') !== false) {
                return false;
            }

            Log::warning('Finance webhook event could not be recorded.', [
                'event_id' => $eventId,
                'transaction_id' => $transactionId,
                'error' => $e->getMessage(),
            ]);

            return true;
        }
    }

    private function touchLastWebhookAt($schoolId)
    {
        if (!Schema::hasTable('finance_settings') || !Schema::hasColumn('finance_settings', 'last_webhook_at')) {
            return;
        }

        DB::table('finance_settings')
            ->when($schoolId, function ($query) use ($schoolId) {
                $query->where('school_id', $schoolId);
            })
            ->when(!$schoolId, function ($query) {
                $query->whereNull('school_id');
            })
            ->update([
                'last_webhook_at' => now(),
                'updated_at' => now(),
            ]);
    }

    private function normalizeUserType($userType)
    {
        $userType = strtolower(trim((string) $userType));
        if ($userType === '') {
            return null;
        }

        if (in_array($userType, ['local', 'kenyan', 'kes'], true)) {
            return 'local';
        }

        return $userType === 'international' ? 'international' : null;
    }

    private function cleanFinanceText($value)
    {
        $value = html_entity_decode((string) $value, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        $value = str_replace(["\xc2\xa0", '&nbsp;'], ' ', $value);
        $value = preg_replace('/\s+/u', ' ', $value);

        return trim(is_string($value) ? $value : '');
    }
}
