<?php

namespace App\Http\Controllers\Admin;

use App\Enums\AppointmentStatus;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreCustomerRequest;
use App\Http\Requests\UpdateCustomerRequest;
use App\Models\Appointment;
use App\Models\Customer;
use App\Models\Invoice;
use App\Services\Tenancy\CurrentBusiness;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class CustomerController extends Controller
{
    public function index(Request $request, CurrentBusiness $currentBusiness): View
    {
        $business = $currentBusiness->get() ?? abort(403);
        $filters = $request->only(['q', 'status', 'source', 'balance', 'sort']);

        $customersQuery = Customer::query()
            ->forBusiness($business)
            ->withCount('appointments')
            ->withSum('invoices as due_total', 'balance_due')
            ->when($filters['q'] ?? null, function (Builder $query, string $search): void {
                $query->where(function (Builder $query) use ($search): void {
                    $query->where('name', 'like', "%{$search}%")
                        ->orWhere('email', 'like', "%{$search}%")
                        ->orWhere('phone', 'like', "%{$search}%");
                });
            })
            ->when($filters['status'] ?? null, fn (Builder $query, string $status) => $query->where('status', $status))
            ->when($filters['source'] ?? null, fn (Builder $query, string $source) => $query->where('source', $source))
            ->when(($filters['balance'] ?? null) === 'has_due', fn (Builder $query) => $query->whereHas('invoices', fn (Builder $invoiceQuery) => $invoiceQuery->where('balance_due', '>', 0)))
            ->when(($filters['balance'] ?? null) === 'no_due', fn (Builder $query) => $query->whereDoesntHave('invoices', fn (Builder $invoiceQuery) => $invoiceQuery->where('balance_due', '>', 0)));

        match ($filters['sort'] ?? 'newest') {
            'name' => $customersQuery->orderBy('name'),
            'spent_desc' => $customersQuery->orderByDesc('total_spent'),
            'last_visit_desc' => $customersQuery->orderByDesc('last_visit_at'),
            default => $customersQuery->latest(),
        };

        $customers = $customersQuery->paginate(10)->withQueryString();

        return view('admin.customers.index', [
            'customers' => $customers,
            'filters' => $filters,
            'sources' => Customer::query()
                ->forBusiness($business)
                ->whereNotNull('source')
                ->distinct()
                ->orderBy('source')
                ->pluck('source')
                ->filter()
                ->values(),
            'summary' => [
                'total' => Customer::query()->forBusiness($business)->count(),
                'returning_rate' => $this->returningRate($business->id),
                'outstanding_due' => Invoice::query()->forBusiness($business)->sum('balance_due'),
                'unpaid_invoices' => Invoice::query()->forBusiness($business)->where('balance_due', '>', 0)->count(),
                'vip' => Customer::query()->forBusiness($business)->where('status', Customer::STATUS_VIP)->count(),
            ],
        ]);
    }

    public function store(StoreCustomerRequest $request, CurrentBusiness $currentBusiness): RedirectResponse
    {
        $business = $currentBusiness->get() ?? abort(403);
        $payload = $request->validatedPayload();
        $payload['business_id'] = $business->id;

        $customer = Customer::query()->create($payload);

        return redirect()
            ->route('admin.customers.show', $customer)
            ->with('status', 'Customer created successfully.');
    }

    public function show(Customer $customer, CurrentBusiness $currentBusiness): View
    {
        $business = $this->authorizeCustomer($customer, $currentBusiness);
        $customer->loadCount([
            'appointments',
            'appointments as completed_appointments_count' => fn (Builder $query) => $query->where('status', AppointmentStatus::Completed->value),
            'appointments as no_show_appointments_count' => fn (Builder $query) => $query->where('status', AppointmentStatus::NoShow->value),
        ]);

        $appointments = Appointment::query()
            ->forBusiness($business)
            ->where('customer_id', $customer->id)
            ->with(['service', 'staffProfile'])
            ->latest('starts_at')
            ->get();

        $invoices = Invoice::query()
            ->forBusiness($business)
            ->where('customer_id', $customer->id)
            ->with(['appointment.service', 'items.service'])
            ->latest('issue_date')
            ->get();

        return view('admin.customers.show', [
            'customer' => $customer,
            'appointments' => $appointments,
            'invoices' => $invoices,
            'dueTotal' => $invoices->sum('balance_due'),
            'notes' => $customer->note_history,
        ]);
    }

    public function update(UpdateCustomerRequest $request, Customer $customer, CurrentBusiness $currentBusiness): RedirectResponse
    {
        $this->authorizeCustomer($customer, $currentBusiness);
        $customer->update($request->validatedPayload());

        return redirect()
            ->route('admin.customers.show', $customer)
            ->with('status', 'Customer updated successfully.');
    }

    public function destroy(Customer $customer, CurrentBusiness $currentBusiness): RedirectResponse
    {
        $this->authorizeCustomer($customer, $currentBusiness);
        $customer->delete();

        return redirect()
            ->route('admin.customers.index')
            ->with('status', 'Customer deleted successfully.');
    }

    private function authorizeCustomer(Customer $customer, CurrentBusiness $currentBusiness)
    {
        $business = $currentBusiness->get() ?? abort(403);

        abort_unless((int) $customer->business_id === (int) $business->id, 404);

        return $business;
    }

    private function returningRate(int $businessId): int
    {
        $total = Customer::query()->where('business_id', $businessId)->count();

        if ($total === 0) {
            return 0;
        }

        $returning = Customer::query()
            ->where('business_id', $businessId)
            ->whereHas('appointments', fn (Builder $query) => $query->select('customer_id')->groupBy('customer_id')->havingRaw('count(*) > 1'))
            ->count();

        return (int) round(($returning / $total) * 100);
    }
}
