<?php

namespace App\Http\Controllers\Tenant;

use App\Http\Controllers\Controller;
use App\Models\CommunicationUnitTopup;
use App\Models\CommunicationUnitTransaction;
use App\Models\GatewayPayment;
use App\Services\CommunicationUnitService;
use App\Services\NumberGenerator;
use App\Services\PaymentGatewayService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;

class CommunicationUnitController extends Controller
{
    public function index(Request $request, CommunicationUnitService $units, PaymentGatewayService $gateway): View
    {
        $tenant = $request->user()->tenant;

        return view('tenant.communication-units.index', [
            'tenant' => $tenant,
            'wallets' => $units->walletsFor($tenant),
            'channels' => $units->channels(),
            'topups' => CommunicationUnitTopup::with(['creator', 'approver'])->latest()->paginate(10, ['*'], 'topups_page'),
            'transactions' => CommunicationUnitTransaction::with(['customer', 'topup', 'savingsTransaction'])->latest()->paginate(15, ['*'], 'ledger_page'),
            'gatewayEnabled' => $gateway->supportsHostedCheckout(),
            'platformPrices' => collect($units->channels())->mapWithKeys(fn ($label, $channel) => [$channel => $units->platformUnitPrice($channel)])->all(),
        ]);
    }

    public function storeManual(Request $request, CommunicationUnitService $units): RedirectResponse
    {
        $validated = $request->validate([
            'channel' => ['required', Rule::in(array_keys($units->channels()))],
            'units' => ['required', 'integer', 'min:1', 'max:1000000'],
            'proof' => ['required_without:proof_camera', 'nullable', 'file', 'mimes:jpg,jpeg,png,pdf', 'max:4096'],
            'proof_camera' => ['required_without:proof', 'nullable', 'string', 'max:7000000'],
            'notes' => ['nullable', 'string', 'max:1000'],
        ], [
            'proof.required_without' => 'Upload a receipt/image or snap payment proof before submitting.',
            'proof_camera.required_without' => 'Upload a receipt/image or snap payment proof before submitting.',
            'proof.mimes' => 'Payment proof must be JPG, PNG, or PDF.',
        ]);

        if ($request->hasFile('proof')) {
            $validated['proof_path'] = $request->file('proof')->store('unit-payment-proofs', 'uploads');
        } elseif (filled($validated['proof_camera'] ?? null)) {
            $validated['proof_path'] = $this->storeCameraProof($validated['proof_camera']);
        }

        $price = $units->platformUnitPrice($validated['channel']);
        $amount = round($price * (int) $validated['units'], 2);

        CommunicationUnitTopup::create([
            'tenant_id' => $request->user()->tenant_id,
            'created_by' => $request->user()->id,
            'channel' => $validated['channel'],
            'units' => $validated['units'],
            'unit_price' => $price,
            'amount' => $amount,
            'payment_channel' => 'manual',
            'status' => CommunicationUnitTopup::STATUS_PENDING,
            'reference' => NumberGenerator::reference('UNITPAY'),
            'proof_path' => $validated['proof_path'] ?? null,
            'notes' => $validated['notes'] ?? null,
        ]);

        return back()->with('status', 'Unit payment submitted for approval.');
    }

    public function startGateway(Request $request, CommunicationUnitService $units, PaymentGatewayService $gateway): RedirectResponse
    {
        $validated = $request->validate([
            'channel' => ['required', Rule::in(array_keys($units->channels()))],
            'units' => ['required', 'integer', 'min:1', 'max:1000000'],
        ]);

        $tenant = $request->user()->tenant;
        $price = $units->platformUnitPrice($validated['channel']);
        $amount = round($price * (int) $validated['units'], 2);
        $reference = NumberGenerator::reference('UNITPAY');

        $topup = CommunicationUnitTopup::create([
            'tenant_id' => $tenant->id,
            'created_by' => $request->user()->id,
            'channel' => $validated['channel'],
            'units' => $validated['units'],
            'unit_price' => $price,
            'amount' => $amount,
            'payment_channel' => 'gateway',
            'status' => CommunicationUnitTopup::STATUS_PENDING,
            'reference' => $reference,
            'notes' => 'Online '.$units->channels()[$validated['channel']].' unit purchase.',
        ]);

        $payment = GatewayPayment::create([
            'tenant_id' => $tenant->id,
            'user_id' => $request->user()->id,
            'payable_type' => CommunicationUnitTopup::class,
            'payable_id' => $topup->id,
            'purpose' => GatewayPayment::PURPOSE_COMMUNICATION_UNITS,
            'provider' => $gateway->provider(),
            'amount' => $amount,
            'currency' => $tenant->settings['currency'] ?? 'NGN',
            'reference' => $reference,
            'status' => 'pending',
            'metadata' => [
                'channel' => $validated['channel'],
                'units' => $validated['units'],
                'unit_price' => $price,
            ],
        ]);

        $checkout = $gateway->initializeCheckout($payment, $tenant->email, route('tenant.communication-units.gateway.callback'));

        if (! $checkout['successful'] || ! $checkout['authorization_url']) {
            $payment->update(['status' => 'failed', 'gateway_response' => ['message' => $checkout['message']]]);
            $topup->update(['status' => CommunicationUnitTopup::STATUS_REJECTED, 'rejected_reason' => $checkout['message']]);

            return back()->with('warning', $checkout['message']);
        }

        $payment->update(['checkout_url' => $checkout['authorization_url']]);

        return redirect()->away($checkout['authorization_url']);
    }

    public function completeGateway(Request $request, PaymentGatewayService $gateway, CommunicationUnitService $units): RedirectResponse
    {
        $reference = (string) $request->query('reference');

        if ($reference === '') {
            return redirect()->route('tenant.communication-units.index')->with('warning', 'Payment reference is missing.');
        }

        $payment = GatewayPayment::where('reference', $reference)
            ->where('purpose', GatewayPayment::PURPOSE_COMMUNICATION_UNITS)
            ->where('user_id', $request->user()->id)
            ->firstOrFail();

        if ($payment->status === 'paid') {
            return redirect()->route('tenant.communication-units.index')->with('status', 'Unit payment already confirmed.');
        }

        $verified = $gateway->verifyCheckout($payment->reference);

        if (! $verified['successful'] || $verified['amount'] === null || round($verified['amount'], 2) !== round((float) $payment->amount, 2)) {
            $payment->update(['status' => 'failed', 'provider_reference' => $verified['reference'], 'gateway_response' => $verified['raw']]);

            return redirect()->route('tenant.communication-units.index')->with('warning', $verified['message']);
        }

        DB::transaction(function () use ($payment, $verified, $units): void {
            $topup = CommunicationUnitTopup::whereKey($payment->payable_id)->lockForUpdate()->firstOrFail();
            $topup->update([
                'payment_channel' => $payment->provider,
                'gateway_reference' => $verified['reference'],
            ]);

            $units->approveTopup($topup, auth()->id(), $payment->provider);

            $payment->update([
                'status' => 'paid',
                'provider_reference' => $verified['reference'],
                'authorization_code' => $verified['authorization_code'],
                'gateway_response' => $verified['raw'],
                'paid_at' => now(),
            ]);
        });

        return redirect()->route('tenant.communication-units.index')->with('status', 'Payment confirmed and units credited.');
    }

    private function storeCameraProof(string $proofCamera): string
    {
        if (! preg_match('/^data:image\/(jpe?g|png);base64,/', $proofCamera, $matches)) {
            throw ValidationException::withMessages([
                'proof_camera' => 'Capture a valid JPG or PNG payment proof image.',
            ]);
        }

        $extension = strtolower($matches[1]) === 'png' ? 'png' : 'jpg';
        $encoded = substr($proofCamera, strpos($proofCamera, ',') + 1);
        $bytes = base64_decode($encoded, true);

        if ($bytes === false || strlen($bytes) < 100) {
            throw ValidationException::withMessages([
                'proof_camera' => 'The captured payment proof image could not be read. Please retake the photo.',
            ]);
        }

        if (strlen($bytes) > 4 * 1024 * 1024) {
            throw ValidationException::withMessages([
                'proof_camera' => 'The captured payment proof image is too large. Please retake a clearer, smaller photo.',
            ]);
        }

        $path = 'unit-payment-proofs/'.now()->format('YmdHis').'-'.Str::random(16).'.'.$extension;
        Storage::disk('uploads')->put($path, $bytes);

        return $path;
    }
}
