<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Referral;
use App\Services\AuditLogService;
use App\Services\ReferralService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Inertia\Inertia;
use Inertia\Response;
use InvalidArgumentException;
use Throwable;

class ReferralController extends Controller
{
    public function index(Request $request, ReferralService $referralService): Response
    {
        $filters = $request->only(['q', 'status']);

        $referrals = Referral::query()
            ->with([
                'referrer:id,name,email,referral_code',
                'referredUser:id,name,email,created_at',
            ])
            ->when($request->filled('q'), function ($query) use ($request) {
                $term = trim((string) $request->string('q'));

                $query->where(function ($subQuery) use ($term) {
                    $subQuery->where('referral_code', 'like', "%{$term}%")
                        ->orWhereHas('referrer', fn ($q) => $q->where('name', 'like', "%{$term}%")->orWhere('email', 'like', "%{$term}%"))
                        ->orWhereHas('referredUser', fn ($q) => $q->where('name', 'like', "%{$term}%")->orWhere('email', 'like', "%{$term}%"));
                });
            })
            ->when($request->filled('status'), fn ($query) => $query->where('status', (string) $request->string('status')))
            ->latest()
            ->paginate(20)
            ->withQueryString()
            ->through(function (Referral $referral) {
                return [
                    'id' => $referral->id,
                    'referral_code' => $referral->referral_code,
                    'status' => $referral->status,
                    'reward_amount' => $referral->reward_amount,
                    'rewarded_at' => optional($referral->rewarded_at)?->toDateTimeString(),
                    'cancelled_at' => optional($referral->cancelled_at)?->toDateTimeString(),
                    'created_at' => optional($referral->created_at)?->toDateTimeString(),
                    'wallet_ledger_reference' => $referral->wallet_ledger_reference,
                    'referrer' => $referral->referrer ? [
                        'id' => $referral->referrer->id,
                        'name' => $referral->referrer->name,
                        'email' => $referral->referrer->email,
                        'referral_code' => $referral->referrer->referral_code,
                    ] : null,
                    'referred_user' => $referral->referredUser ? [
                        'id' => $referral->referredUser->id,
                        'name' => $referral->referredUser->name,
                        'email' => $referral->referredUser->email,
                    ] : null,
                    'meta' => $referral->meta_json ?? [],
                ];
            });

        $summary = [
            'total' => Referral::query()->count(),
            'pending' => Referral::query()->where('status', Referral::STATUS_PENDING)->count(),
            'rewarded' => Referral::query()->where('status', Referral::STATUS_REWARDED)->count(),
            'cancelled' => Referral::query()->where('status', Referral::STATUS_CANCELLED)->count(),
            'reward_total' => (string) (DB::table('referrals')
                ->where('status', Referral::STATUS_REWARDED)
                ->selectRaw('COALESCE(SUM(reward_amount),0) as total')
                ->value('total') ?? '0'),
        ];

        return Inertia::render('Admin/Referrals/Index', [
            'referrals' => $referrals,
            'filters' => $filters,
            'statuses' => Referral::STATUSES,
            'summary' => $summary,
            'settings' => $referralService->settings(),
        ]);
    }

    public function updateSettings(
        Request $request,
        ReferralService $referralService,
        AuditLogService $auditLogService
    ): RedirectResponse {
        $data = $request->validate([
            'enabled' => ['required', 'boolean'],
            'default_reward_amount' => ['required', 'numeric', 'min:0', 'max:1000000'],
        ]);

        $old = $referralService->settings();
        $referralService->updateSettings((bool) $data['enabled'], (string) $data['default_reward_amount']);
        $new = $referralService->settings();

        $auditLogService->log(
            admin: $request->user(),
            action: 'referrals.settings.updated',
            entity: 'system_settings',
            entityId: 'features.referrals',
            oldValues: $old,
            newValues: $new,
            ipAddress: $request->ip(),
            userAgent: $request->userAgent()
        );

        return back()->with('success', 'Referral settings updated.');
    }

    public function reward(
        Request $request,
        Referral $referral,
        ReferralService $referralService,
        AuditLogService $auditLogService
    ): RedirectResponse {
        $data = $request->validate([
            'amount' => ['nullable', 'numeric', 'min:0.01', 'max:1000000'],
        ]);

        $before = [
            'status' => $referral->status,
            'reward_amount' => $referral->reward_amount,
            'rewarded_at' => optional($referral->rewarded_at)?->toDateTimeString(),
            'wallet_ledger_reference' => $referral->wallet_ledger_reference,
        ];

        try {
            $updated = $referralService->rewardReferral($referral, $request->user(), isset($data['amount']) ? (float) $data['amount'] : null);
        } catch (InvalidArgumentException $e) {
            return back()->with('error', $e->getMessage());
        } catch (Throwable $e) {
            report($e);

            return back()->with('error', 'Unable to reward referral right now.');
        }

        $auditLogService->log(
            admin: $request->user(),
            action: 'referrals.rewarded',
            entity: $updated,
            oldValues: $before,
            newValues: [
                'status' => $updated->status,
                'reward_amount' => $updated->reward_amount,
                'rewarded_at' => optional($updated->rewarded_at)?->toDateTimeString(),
                'wallet_ledger_reference' => $updated->wallet_ledger_reference,
            ],
            ipAddress: $request->ip(),
            userAgent: $request->userAgent()
        );

        return back()->with('success', 'Referral reward credited successfully.');
    }

    public function updateStatus(
        Request $request,
        Referral $referral,
        ReferralService $referralService,
        AuditLogService $auditLogService
    ): RedirectResponse {
        $data = $request->validate([
            'status' => ['required', Rule::in([Referral::STATUS_CANCELLED, Referral::STATUS_PENDING])],
            'reason' => ['nullable', 'string', 'max:255'],
        ]);

        $before = [
            'status' => $referral->status,
            'cancelled_at' => optional($referral->cancelled_at)?->toDateTimeString(),
            'meta' => $referral->meta_json,
        ];

        try {
            if ($data['status'] === Referral::STATUS_CANCELLED) {
                $updated = $referralService->cancelReferral($referral, $request->user(), $data['reason'] ?? null);
            } else {
                $updated = Referral::query()->findOrFail($referral->id);
                if ($updated->status === Referral::STATUS_REWARDED) {
                    throw new InvalidArgumentException('Rewarded referral cannot be moved back to pending.');
                }

                $updated->update([
                    'status' => Referral::STATUS_PENDING,
                    'cancelled_at' => null,
                    'meta_json' => array_merge($updated->meta_json ?? [], [
                        'reopened_by_admin_id' => $request->user()->id,
                        'reopened_by_admin_email' => $request->user()->email,
                        'reopen_reason' => $data['reason'] ?? null,
                    ]),
                ]);
            }
        } catch (InvalidArgumentException $e) {
            return back()->with('error', $e->getMessage());
        } catch (Throwable $e) {
            report($e);

            return back()->with('error', 'Unable to update referral status.');
        }

        $auditLogService->log(
            admin: $request->user(),
            action: 'referrals.status.updated',
            entity: $updated,
            oldValues: $before,
            newValues: [
                'status' => $updated->status,
                'cancelled_at' => optional($updated->cancelled_at)?->toDateTimeString(),
                'meta' => $updated->meta_json,
            ],
            ipAddress: $request->ip(),
            userAgent: $request->userAgent()
        );

        return back()->with('success', 'Referral status updated.');
    }
}
