<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\AuditLog;
use App\Models\Invoice;
use App\Models\PaymentProvider;
use App\Services\AuditLogger;
use App\Services\Billing\BillingManager;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;
use Throwable;

class InvoicesController extends Controller
{
    public function index(Request $request): Response
    {
        $filters = [
            'q' => trim((string) $request->query('q', '')),
            'status' => (string) $request->query('status', 'all'),
            'provider_key' => $request->query('provider_key') ?: null,
            'from_date' => $request->query('from_date') ?: null,
            'to_date' => $request->query('to_date') ?: null,
        ];

        $query = Invoice::query()
            ->with([
                'subscription.user:id,email,name',
                'subscription.plan:id,key,name',
            ])
            ->orderByDesc('created_at');

        if ($filters['q'] !== '') {
            $needle = $filters['q'];
            $query->whereHas('subscription.user', fn ($q) => $q->where('email', 'like', "%{$needle}%"));
        }

        if ($filters['status'] !== '' && $filters['status'] !== 'all') {
            $query->where('status', $filters['status']);
        }

        if ($filters['provider_key']) {
            $query->where('provider_key', $filters['provider_key']);
        }

        if ($filters['from_date']) {
            try {
                $query->where('created_at', '>=', \Carbon\Carbon::parse($filters['from_date'])->startOfDay());
            } catch (Throwable) {
                // Ignore unparseable dates rather than 422 — filter UI is the
                // forgiving surface; keeping the query running with the rest
                // of the filters is more useful than a hard reject.
            }
        }

        if ($filters['to_date']) {
            try {
                $query->where('created_at', '<=', \Carbon\Carbon::parse($filters['to_date'])->endOfDay());
            } catch (Throwable) {
                //
            }
        }

        $paginator = $query->paginate(50)->withQueryString();

        $rows = $paginator->through(fn (Invoice $i) => [
            'id' => $i->id,
            'subscription_id' => $i->subscription_id,
            'user' => [
                'id' => $i->subscription?->user_id,
                'email' => $i->subscription?->user?->email ?? $i->subscription?->user_email_snapshot,
                'name' => $i->subscription?->user?->name,
                'deleted' => $i->subscription !== null && $i->subscription->user_id === null,
            ],
            'plan' => [
                'id' => $i->subscription?->plan_id,
                'key' => $i->subscription?->plan?->key,
                'name' => $i->subscription?->plan?->name,
            ],
            'provider_key' => $i->provider_key,
            'provider_invoice_id' => $i->provider_invoice_id,
            'amount_cents' => (int) $i->amount_cents,
            'tax_cents' => (int) $i->tax_cents,
            'tax_rate_bps' => $i->tax_rate_bps !== null ? (int) $i->tax_rate_bps : null,
            'tax_label' => $i->tax_label,
            'tax_country' => $i->tax_country,
            'currency' => $i->currency,
            'status' => $i->status,
            'paid_at' => $i->paid_at?->toIso8601String(),
            'hosted_url' => $i->hosted_url,
            'created_at' => $i->created_at?->toIso8601String(),
        ]);

        return Inertia::render('Admin/Invoices/Index', [
            'invoices' => $rows,
            'filters' => $filters,
            'providers' => PaymentProvider::orderBy('position')->get(['key', 'label']),
        ]);
    }

    public function show(Request $request, Invoice $invoice): Response
    {
        $invoice->load([
            'subscription:id,user_id,plan_id,provider_key,provider_subscription_id,period,status,user_email_snapshot',
            'subscription.user:id,email,name',
            'subscription.plan:id,key,name',
        ]);

        $sub = $invoice->subscription;
        $user = $sub?->user;
        $userEmail = $user?->email ?? $sub?->user_email_snapshot;

        $auditLogs = AuditLog::query()
            ->with('actor:id,name,email')
            ->where('target_type', 'Invoice')
            ->where('target_id', $invoice->id)
            ->orderByDesc('id')
            ->limit(20)
            ->get()
            ->map(fn (AuditLog $log) => [
                'id' => $log->id,
                'action' => $log->action,
                'summary' => $log->summary,
                'context' => $log->context,
                'created_at' => $log->created_at?->toIso8601String(),
                'actor' => $log->actor ? [
                    'id' => $log->actor->id,
                    'name' => $log->actor->name,
                    'email' => $log->actor->email,
                ] : null,
            ])
            ->values();
        $auditLogsTotal = (int) AuditLog::query()
            ->where('target_type', 'Invoice')
            ->where('target_id', $invoice->id)
            ->count();

        return Inertia::render('Admin/Invoices/Show', [
            'invoice' => [
                'id' => $invoice->id,
                'provider_key' => $invoice->provider_key,
                'provider_invoice_id' => $invoice->provider_invoice_id,
                'amount_cents' => (int) $invoice->amount_cents,
                'tax_cents' => (int) $invoice->tax_cents,
                'tax_rate_bps' => $invoice->tax_rate_bps !== null ? (int) $invoice->tax_rate_bps : null,
                'tax_label' => $invoice->tax_label,
                'tax_country' => $invoice->tax_country,
                'subtotal_cents' => $invoice->subtotalCents(),
                'currency' => $invoice->currency,
                'status' => $invoice->status,
                'paid_at' => $invoice->paid_at?->toIso8601String(),
                'hosted_url' => $invoice->hosted_url,
                'created_at' => $invoice->created_at?->toIso8601String(),
            ],
            'subscription' => $sub ? [
                'id' => $sub->id,
                'plan' => $sub->plan ? [
                    'id' => $sub->plan->id,
                    'key' => $sub->plan->key,
                    'name' => $sub->plan->name,
                ] : null,
                'period' => $sub->period,
                'provider_subscription_id' => $sub->provider_subscription_id,
                'status' => $sub->status,
            ] : null,
            'user' => [
                'id' => $user?->id,
                'name' => $user?->name,
                'email' => $userEmail,
                // user.deleted = the sub's user_id was nulled by purgeUser;
                // email is from the snapshot, not a live user row.
                'deleted' => $sub !== null && $sub->user_id === null,
            ],
            'auditLogs' => $auditLogs,
            'auditLogsTotal' => $auditLogsTotal,
        ]);
    }

    /**
     * Refund an admin-selected invoice. Dispatches to the matching provider
     * via `BillingProvider::refundInvoice` — Stripe refunds the linked
     * PaymentIntent, Razorpay refunds the linked Payment id. Local invoice
     * is marked 'refunded' (not a provider-canonical status — Stripe keeps
     * it 'paid' and tracks refunds separately — but we persist it locally
     * so the admin filter UI + audit history surface refunded invoices
     * without needing to walk provider-side records).
     */
    public function refund(Request $request, Invoice $invoice, BillingManager $manager): JsonResponse
    {
        if ($invoice->status !== 'paid') {
            return response()->json([
                'message' => "Cannot refund an invoice with status [{$invoice->status}]; only paid invoices are refundable.",
            ], 422);
        }

        $data = $request->validate([
            'amount_cents' => ['nullable', 'integer', 'min:1', 'max:'.(int) $invoice->amount_cents],
        ]);

        $amountCents = $data['amount_cents'] ?? (int) $invoice->amount_cents;

        $provider = $manager->provider($invoice->provider_key);
        if (! $provider) {
            return response()->json([
                'message' => "Provider [{$invoice->provider_key}] not configured. Add API keys in admin → billing.",
            ], 422);
        }

        try {
            $providerRefundId = $provider->refundInvoice($invoice, $amountCents);
        } catch (Throwable $e) {
            return response()->json([
                'message' => "Refund failed: {$e->getMessage()}",
            ], 422);
        }

        $invoice->update(['status' => 'refunded']);

        // Notify both customer and admin of the refund. Dispatcher checks
        // each toggle + queues the email; failures don't roll back the
        // refund. (Webhook-driven refunds dispatch from the provider
        // handler; this covers the admin-initiated path.)
        $freshInvoice = $invoice->fresh('subscription');
        \App\Services\NotificationDispatcher::refundIssued($freshInvoice, $amountCents);
        \App\Services\NotificationDispatcher::adminRefundIssued($freshInvoice, $amountCents);

        // Audit summary shows the major-unit amount (e.g. "7.50 USD"), not
        // the raw cents — the cents value is preserved verbatim in the
        // context payload below for machine consumers. v1 currencies (USD /
        // INR / EUR / GBP) are all 100-base subunits, so /100 is safe.
        $amountMajor = number_format($amountCents / 100, 2);

        AuditLogger::record(
            'invoice.refund',
            $invoice,
            "Refunded {$amountMajor} {$invoice->currency}",
            [
                'amount_cents' => $amountCents,
                'provider_refund_id' => $providerRefundId,
            ],
        );

        return response()->json([
            'ok' => true,
            'invoice' => $invoice->fresh(),
            'provider_refund_id' => $providerRefundId,
        ]);
    }
}
