<?php

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Api\V1\Concerns\RespondsWithApi;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class AdminController extends Controller
{
    use RespondsWithApi;

    private array $resources = [
        'cities' => 'cities',
        'localities' => 'localities',
        'hubs' => 'hubs',
        'categories' => 'service_categories',
        'services' => 'services',
        'variants' => 'service_variants',
        'addons' => 'service_addons',
        'price-books' => 'price_books',
        'service-prices' => 'service_prices',
        'fee-rules' => 'fee_rules',
        'tax-rules' => 'tax_rules',
        'coupons' => 'coupons',
        'faqs' => 'faqs',
        'cms-pages' => 'cms_pages',
        'app-configs' => 'app_configs',
        'notification-templates' => 'notification_templates',
    ];

    public function dashboard(Request $request)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        return $this->ok([
            'users' => DB::table('users')->count(),
            'customers' => DB::table('users')->where('role', 'customer')->count(),
            'professionals' => DB::table('professionals')->count(),
            'active_bookings' => DB::table('bookings')->whereNotIn('status', ['completed', 'cancelled'])->count(),
            'completed_bookings' => DB::table('bookings')->where('status', 'completed')->count(),
            'open_tickets' => DB::table('support_tickets')->where('status', 'open')->count(),
            'revenue_paid' => DB::table('payments')->where('status', 'captured')->sum('amount'),
            'refunds_requested' => DB::table('refunds')->where('status', 'requested')->sum('amount'),
        ]);
    }

    public function liveBookings(Request $request)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        return $this->ok(DB::table('bookings')
            ->leftJoin('booking_assignments', 'booking_assignments.booking_id', '=', 'bookings.id')
            ->leftJoin('professionals', 'professionals.id', '=', 'booking_assignments.professional_id')
            ->select('bookings.*', 'professionals.pro_code')
            ->whereNotIn('bookings.status', ['completed', 'cancelled'])
            ->latest('bookings.id')
            ->paginate(50));
    }

    public function bookings(Request $request)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        return $this->ok(DB::table('bookings')->latest('id')->paginate(50));
    }

    public function booking(Request $request, int $booking)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        $row = DB::table('bookings')->where('id', $booking)->first();

        if (! $row) {
            return $this->fail('BOOKING_NOT_FOUND', 'Booking not found.', [], 404);
        }

        return $this->ok([
            'booking' => $row,
            'items' => DB::table('booking_items')->where('booking_id', $booking)->get(),
            'timeline' => DB::table('booking_status_histories')->where('booking_id', $booking)->orderBy('id')->get(),
            'assignment' => DB::table('booking_assignments')->where('booking_id', $booking)->latest('id')->first(),
            'payments' => DB::table('payments')->where('booking_id', $booking)->get(),
            'refunds' => DB::table('refunds')->where('booking_id', $booking)->get(),
        ]);
    }

    public function assignBooking(Request $request, int $booking)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        $data = $request->validate([
            'professional_id' => ['required', 'integer'],
            'assignment_type' => ['nullable', 'in:auto,manual,reassignment'],
        ]);

        DB::table('booking_assignments')->insert([
            'booking_id' => $booking,
            'professional_id' => $data['professional_id'],
            'assigned_by_type' => 'admin',
            'assigned_by_id' => $request->user()->id,
            'assignment_type' => $data['assignment_type'] ?? 'manual',
            'status' => 'assigned',
            'assigned_at' => now(),
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        DB::table('bookings')->where('id', $booking)->update([
            'status' => 'assigned',
            'assignment_status' => 'assigned',
            'updated_at' => now(),
        ]);

        $this->audit($request, 'booking.assign', 'bookings', $booking, $data);

        return $this->ok(DB::table('booking_assignments')->where('booking_id', $booking)->latest('id')->first(), 'Booking assigned.');
    }

    public function refundBooking(Request $request, int $booking)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        $data = $request->validate([
            'amount' => ['required', 'numeric', 'min:0'],
            'reason' => ['nullable', 'string', 'max:255'],
        ]);

        $id = DB::table('refunds')->insertGetId([
            'booking_id' => $booking,
            'refund_number' => 'RF-' . now()->format('Ymd') . '-' . strtoupper(Str::random(6)),
            'amount' => $data['amount'],
            'reason' => $data['reason'] ?? 'Admin refund',
            'status' => 'approved',
            'approved_by' => $request->user()->id,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        $this->audit($request, 'booking.refund', 'bookings', $booking, $data);

        return $this->ok(DB::table('refunds')->where('id', $id)->first(), 'Refund created.');
    }

    public function customers(Request $request)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        return $this->ok(DB::table('users')->where('role', 'customer')->latest('id')->paginate(50));
    }

    public function professionals(Request $request)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        return $this->ok(DB::table('professionals')
            ->leftJoin('users', 'users.id', '=', 'professionals.user_id')
            ->select('professionals.*', 'users.name', 'users.phone')
            ->latest('professionals.id')
            ->paginate(50));
    }

    public function verifyProfessional(Request $request, int $professional)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        DB::table('professionals')->where('id', $professional)->update([
            'onboarding_status' => 'active',
            'status' => 'active',
            'updated_at' => now(),
        ]);

        $this->audit($request, 'professional.verify', 'professionals', $professional);

        return $this->ok(DB::table('professionals')->where('id', $professional)->first(), 'Professional verified.');
    }

    public function suspendProfessional(Request $request, int $professional)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        DB::table('professionals')->where('id', $professional)->update(['status' => 'suspended', 'updated_at' => now()]);
        $this->audit($request, 'professional.suspend', 'professionals', $professional);

        return $this->ok(DB::table('professionals')->where('id', $professional)->first(), 'Professional suspended.');
    }

    public function supportTickets(Request $request)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        return $this->ok(DB::table('support_tickets')->latest('id')->paginate(50));
    }

    public function replyTicket(Request $request, int $ticket)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        $data = $request->validate(['message' => ['required', 'string', 'max:2000']]);

        DB::table('support_messages')->insert([
            'support_ticket_id' => $ticket,
            'sender_type' => 'admin',
            'sender_id' => $request->user()->id,
            'message' => $data['message'],
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        DB::table('support_tickets')->where('id', $ticket)->update(['status' => 'pending_customer', 'updated_at' => now()]);

        return $this->ok(DB::table('support_messages')->where('support_ticket_id', $ticket)->latest('id')->first(), 'Reply sent.');
    }

    public function resourceIndex(Request $request, string $resource)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        $table = $this->tableFor($resource);
        if (! $table) {
            return $this->fail('RESOURCE_NOT_FOUND', 'Admin resource not found.', [], 404);
        }

        return $this->ok(DB::table($table)->latest('id')->paginate(50));
    }

    public function resourceStore(Request $request, string $resource)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        $table = $this->tableFor($resource);
        if (! $table) {
            return $this->fail('RESOURCE_NOT_FOUND', 'Admin resource not found.', [], 404);
        }

        $data = $request->except(['id', 'created_at', 'updated_at']);
        $data['created_at'] = now();
        $data['updated_at'] = now();
        $id = DB::table($table)->insertGetId($data);
        $this->audit($request, $resource . '.create', $table, $id, $data);

        return $this->ok(DB::table($table)->where('id', $id)->first(), 'Resource created.', [], 201);
    }

    public function resourceUpdate(Request $request, string $resource, int $id)
    {
        if (! $this->isAdmin($request)) {
            return $this->notAllowed();
        }

        $table = $this->tableFor($resource);
        if (! $table) {
            return $this->fail('RESOURCE_NOT_FOUND', 'Admin resource not found.', [], 404);
        }

        $data = $request->except(['id', 'created_at', 'updated_at']);
        $data['updated_at'] = now();
        DB::table($table)->where('id', $id)->update($data);
        $this->audit($request, $resource . '.update', $table, $id, $data);

        return $this->ok(DB::table($table)->where('id', $id)->first(), 'Resource updated.');
    }

    private function tableFor(string $resource): ?string
    {
        return $this->resources[$resource] ?? null;
    }

    private function isAdmin(Request $request): bool
    {
        return $request->user() && $request->user()->isAdmin();
    }

    private function notAllowed()
    {
        return $this->fail('ADMIN_NOT_ALLOWED', 'Admin permission required.', [], 403);
    }

    private function audit(Request $request, string $action, ?string $type = null, ?int $id = null, array $values = []): void
    {
        DB::table('audit_logs')->insert([
            'actor_type' => 'user',
            'actor_id' => $request->user()?->id,
            'action' => $action,
            'auditable_type' => $type,
            'auditable_id' => $id,
            'new_values_json' => $values ? json_encode($values) : null,
            'ip_address' => $request->ip(),
            'created_at' => now(),
            'updated_at' => now(),
        ]);
    }
}
