<?php

namespace App\Http\Controllers\Billing;

use App\Http\Controllers\Controller;
use App\Models\Order;
use App\Models\Payment;
use App\Models\PricingPlan;
use App\Models\Subscription;
use App\Models\User;
use App\Models\WebhookEvent;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

class PakasirController extends Controller
{
    public function createTransaction(Request $request): JsonResponse
    {
        $data = $request->validate([
            'plan_code' => 'required|string|in:personal_monthly,personal_annual,personal_lifetime',
        ]);

        $user = $request->user();
        if (!$user) {
            return response()->json(['error' => 'auth_required'], 401);
        }

        $config = config('dewemoji.billing.providers.pakasir', []);
        $enabled = (bool) ($config['enabled'] ?? false);
        $apiBase = rtrim((string) ($config['api_base'] ?? ''), '/');
        $apiKey = (string) ($config['api_key'] ?? '');
        $project = (string) ($config['project'] ?? '');
        $timeout = (int) ($config['timeout'] ?? 10);

        if (!$enabled || $apiBase === '' || $apiKey === '' || $project === '') {
            return response()->json(['error' => 'pakasir_not_configured'], 422);
        }

        $amountIdr = $this->resolvePlanAmountIdr($data['plan_code']);
        if ($amountIdr <= 0) {
            return response()->json(['error' => 'invalid_plan_amount'], 422);
        }

        Subscription::where('user_id', $user->id)
            ->where('provider', 'pakasir')
            ->where('status', 'pending')
            ->update(['status' => 'cancelled']);
        Order::where('user_id', $user->id)
            ->where('provider', 'pakasir')
            ->where('status', 'pending')
            ->update(['status' => 'cancelled']);
        Payment::where('user_id', $user->id)
            ->where('provider', 'pakasir')
            ->where('status', 'pending')
            ->update(['status' => 'cancelled']);

        $order = Order::create([
            'user_id' => $user->id,
            'plan_code' => $data['plan_code'],
            'type' => $data['plan_code'] === 'personal_lifetime' ? 'one_time' : 'subscription',
            'currency' => 'IDR',
            'amount' => $amountIdr,
            'status' => 'pending',
            'provider' => 'pakasir',
        ]);

        $orderRef = 'DW-'.$order->id.'-'.now()->format('ymdHis');
        $payload = [
            'project' => $project,
            'order_id' => $orderRef,
            'amount' => $amountIdr,
            'api_key' => $apiKey,
            'description' => 'Dewemoji '.$data['plan_code'].' for '.$user->email,
        ];

        $endpoint = $apiBase.'/api/transactioncreate/qris';
        $res = Http::timeout($timeout)->post($endpoint, $payload);

        if (!$res->ok()) {
            Log::warning('Pakasir create transaction failed', ['body' => $res->body()]);
            return response()->json(['error' => 'pakasir_create_failed'], 502);
        }

        $body = $res->json();
        $payment = is_array($body['payment'] ?? null) ? $body['payment'] : $body;
        $paymentNumber = (string) ($payment['payment_number'] ?? '');
        $status = (string) ($payment['status'] ?? 'pending');
        $expiredAt = (string) ($payment['expired_at'] ?? '');
        $totalPayment = (int) ($payment['total_payment'] ?? $amountIdr);

        $order->update([
            'provider_ref' => $orderRef,
            'status' => $status === 'success' ? 'pending' : 'pending',
        ]);

        Payment::create([
            'user_id' => $user->id,
            'order_id' => $order->id,
            'provider' => 'pakasir',
            'type' => $data['plan_code'] === 'personal_lifetime' ? 'one_time' : 'subscription',
            'plan_code' => $data['plan_code'],
            'currency' => 'IDR',
            'amount' => $amountIdr,
            'status' => 'pending',
            'provider_ref' => $paymentNumber !== '' ? $paymentNumber : $orderRef,
            'raw_payload' => $body,
        ]);

        return response()->json([
            'ok' => true,
            'order_id' => $orderRef,
            'payment_number' => $paymentNumber,
            'amount' => $amountIdr,
            'total_payment' => $totalPayment,
            'expired_at' => $expiredAt,
            'status' => $status,
        ]);
    }

    public function cancelPending(Request $request): JsonResponse
    {
        $user = $request->user();
        if (!$user) {
            return response()->json(['error' => 'auth_required'], 401);
        }

        $orderRef = (string) $request->input('order_id', '');

        $orderQuery = Order::where('user_id', $user->id)
            ->where('provider', 'pakasir')
            ->where('status', 'pending');

        if ($orderRef !== '') {
            $orderQuery->where('provider_ref', $orderRef);
        }

        $order = $orderQuery->orderByDesc('id')->first();
        if (!$order) {
            return response()->json(['ok' => true, 'cancelled' => false]);
        }

        $order->update(['status' => 'cancelled']);
        Payment::where('order_id', $order->id)->where('status', 'pending')->update(['status' => 'cancelled']);
        Subscription::where('user_id', $user->id)
            ->where('provider', 'pakasir')
            ->where('status', 'pending')
            ->update(['status' => 'cancelled']);

        return response()->json(['ok' => true, 'cancelled' => true]);
    }

    public function webhook(Request $request): JsonResponse
    {
        $payload = $request->all();
        $eventId = (string) ($payload['id'] ?? $payload['event_id'] ?? Str::uuid());
        $eventType = (string) ($payload['event_type'] ?? $payload['status'] ?? 'event');

        WebhookEvent::create([
            'provider' => 'pakasir',
            'event_id' => $eventId,
            'event_type' => $eventType,
            'status' => 'received',
            'payload' => $payload,
            'headers' => $request->headers->all(),
            'received_at' => now(),
        ]);

        $this->handlePakasirPayload($payload);

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

    private function handlePakasirPayload(array $payload): void
    {
        $status = strtolower((string) ($payload['status'] ?? ''));
        if (!in_array($status, ['paid', 'success', 'settlement', 'completed'], true)) {
            return;
        }

        $orderId = (string) ($payload['order_id'] ?? '');
        if ($orderId === '') {
            return;
        }

        $order = Order::where('provider', 'pakasir')->where('provider_ref', $orderId)->first();
        if (!$order) {
            return;
        }

        $order->update(['status' => 'paid']);
        Payment::where('order_id', $order->id)->update(['status' => 'paid']);

        $user = User::find($order->user_id);
        if (!$user) {
            return;
        }

        $expiresAt = null;
        if ($order->plan_code === 'personal_monthly') {
            $expiresAt = now()->addMonth();
        } elseif ($order->plan_code === 'personal_annual') {
            $expiresAt = now()->addYear();
        }

        Subscription::updateOrCreate(
            [
                'provider' => 'pakasir',
                'provider_ref' => $orderId,
            ],
            [
                'user_id' => $user->id,
                'plan' => $order->plan_code,
                'status' => 'active',
                'started_at' => now(),
                'expires_at' => $expiresAt,
            ]
        );

        User::where('id', $user->id)->update(['tier' => 'personal']);
    }

    private function resolvePlanAmountIdr(string $planCode): int
    {
        $plan = PricingPlan::where('code', $planCode)->where('status', 'active')->first();
        if ($plan) {
            return (int) $plan->amount;
        }

        $defaults = collect(config('dewemoji.pricing.defaults', []))->keyBy('code');
        $fallback = $defaults->get($planCode);
        if (!$fallback) {
            return 0;
        }

        return (int) ($fallback['amount'] ?? 0);
    }
}
