<?php

namespace App\Http\Controllers\Admin;

use App\Actions\Customers\CreateCustomer;
use App\Actions\Customers\DeleteCustomer;
use App\Actions\Customers\ExportCustomers;
use App\Actions\Customers\UpdateCustomer;
use App\Exceptions\CustomerHasBalance;
use App\Http\Controllers\Concerns\RespondsJsonOrRedirect;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\CustomerRequest;
use App\Models\Customer;
use App\Models\CustomerGroup;
use App\Models\Store;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

/**
 * Customers admin — full CRUD per `docs/features/customers.md` §6–7.
 * The feature-rich tabs (Statement, Credit, Loyalty, Activity) are
 * deferred to post-Sales slices; the Show page here surfaces what we
 * can show today (header + KPIs at zero + addresses + notes).
 */
class CustomerController extends Controller
{
    use RespondsJsonOrRedirect;

    /**
     * Typeahead source for the `remoteSelect` customer picker (e.g. the
     * Sales History customer filter). Returns `[{value, label}]`, matching
     * the products.search contract. Searches name / phone / code.
     */
    public function search(Request $request): JsonResponse
    {
        $this->authorize('viewAny', Customer::class);

        $q = trim((string) $request->query('q', ''));

        $customers = Customer::query()
            ->when($q !== '', fn ($qb) => $qb->where(fn ($w) =>
                $w->where('name', 'like', "%{$q}%")
                  ->orWhere('phone', 'like', "%{$q}%")
                  ->orWhere('code', 'like', "%{$q}%")
            ))
            ->orderBy('name')
            ->limit(25)
            ->get(['id', 'name', 'phone', 'code']);

        return response()->json(
            $customers->map(fn (Customer $c) => [
                'value' => (string) $c->id,
                'label' => $c->name.($c->phone ? ' · '.$c->phone : ($c->code ? ' · '.$c->code : '')),
            ])->all()
        );
    }

    public function index(Request $request): View
    {
        $this->authorize('viewAny', Customer::class);

        $q       = trim((string) $request->query('q', ''));
        $status  = (string) $request->query('status', 'all');
        $groupId = $request->integer('group_id') ?: null;

        $customers = Customer::query()
            ->with('group:id,name')
            ->when($status === 'active',   fn ($qb) => $qb->where('is_active', true))
            ->when($status === 'inactive', fn ($qb) => $qb->where('is_active', false))
            ->when($groupId, fn ($qb) => $qb->where('customer_group_id', $groupId))
            ->when($q !== '', function ($qb) use ($q) {
                $qb->where(function ($w) use ($q) {
                    $w->where('name', 'like', "%{$q}%")
                      ->orWhere('code', 'like', "%{$q}%")
                      ->orWhere('phone', 'like', "%{$q}%")
                      ->orWhere('email', 'like', "%{$q}%")
                      ->orWhere('business_name', 'like', "%{$q}%");
                });
            })
            // Newest customers first — system-wide DESC default for tables
            // without a manual `sort_order` column (see memory
            // table-and-form-conventions).
            ->orderByDesc('id')
            ->get();

        // Summary cards — computed from the same filtered collection.
        $summaryCards = [
            ['label' => __('customers.summary.total'),        'value' => number_format($customers->count())],
            ['label' => __('customers.summary.with_balance'), 'value' => number_format($customers->where('outstanding_balance', '>', 0)->count())],
            ['label' => __('customers.summary.outstanding'),  'value' => format_money($customers->sum('outstanding_balance')), 'tone' => 'warning'],
        ];

        return view('admin.customers.index', [
            'customers'    => $customers,
            'summaryCards' => $summaryCards,
            'groups'       => CustomerGroup::query()->orderBy('name')->get(['id', 'name']),
            'filters'      => compact('q', 'status', 'groupId'),
        ]);
    }

    public function create(): View
    {
        $this->authorize('create', Customer::class);

        $customer = new Customer([
            'is_active'   => true,
            'is_business' => false,
        ]);
        $customer->setRelation('addresses', collect());

        return view('admin.customers.edit', $this->editorPayload($customer));
    }

    public function store(CustomerRequest $request, CreateCustomer $create): JsonResponse|RedirectResponse
    {
        $this->authorize('create', Customer::class);

        $customer = $create($request->customerData(), $request->addressData(), $request->user());

        return $this->jsonOrRedirect(
            $request,
            __('customers.flash.created', ['name' => $customer->name]),
            route('admin.customers.index'),
        );
    }

    public function show(Customer $customer): View
    {
        $this->authorize('view', $customer);

        $customer->load(['group', 'firstStore', 'addresses', 'creator:id,name']);

        // Open sales (balance_due > 0, not voided) — drives the
        // "Open sales" drill-down card on the customer page. Sorted
        // oldest first so the most-overdue rises to the top.
        $openSales = \App\Models\Sale::query()
            ->where('customer_id', $customer->id)
            ->whereRaw('balance_due > 0')
            ->where('status', '!=', \App\Models\Sale::STATUS_VOIDED)
            ->orderBy('sale_date')
            ->orderBy('id')
            ->limit(50)
            ->get(['id', 'number', 'sale_date', 'currency_code', 'grand_total', 'paid_total', 'balance_due', 'status']);

        return view('admin.customers.show', [
            'customer'  => $customer,
            'openSales' => $openSales,
        ]);
    }

    public function edit(Customer $customer): View
    {
        $this->authorize('update', $customer);

        $customer->load('addresses');

        return view('admin.customers.edit', $this->editorPayload($customer));
    }

    public function update(CustomerRequest $request, Customer $customer, UpdateCustomer $update): JsonResponse|RedirectResponse
    {
        $this->authorize('update', $customer);

        $update($customer, $request->customerData(), $request->addressData(), $request->user());

        return $this->jsonOrRedirect(
            $request,
            __('customers.flash.updated', ['name' => $customer->name]),
            route('admin.customers.index'),
        );
    }

    /**
     * AJAX flip of `is_active` without opening the edit page. Returns
     * JSON so the row-level toggle on the index can swap state without
     * a full reload. Mirrors Products' `toggleField` pattern.
     */
    public function toggleActive(Customer $customer, Request $request): \Illuminate\Http\JsonResponse
    {
        $this->authorize('update', $customer);

        $customer->update([
            'is_active'  => ! $customer->is_active,
            'updated_by' => $request->user()?->id,
        ]);

        return response()->json([
            'ok'        => true,
            'is_active' => (bool) $customer->is_active,
            'message'   => __($customer->is_active ? 'customers.flash.activated' : 'customers.flash.deactivated', ['name' => $customer->name]),
        ]);
    }

    public function destroy(Request $request, Customer $customer, DeleteCustomer $delete): JsonResponse|RedirectResponse
    {
        $this->authorize('delete', $customer);

        $name = $customer->name;
        try {
            $delete($customer);
        } catch (CustomerHasBalance $e) {
            return $this->jsonOrError(
                $request,
                __('customers.errors.has_balance', ['kind' => $e->kind, 'amount' => number_format($e->amount, 2)]),
                route('admin.customers.show', $customer),
            );
        }

        return $this->jsonOrRedirect(
            $request,
            __('customers.flash.deleted', ['name' => $name]),
            route('admin.customers.index'),
        );
    }

    public function export(Request $request, ExportCustomers $export): \Symfony\Component\HttpFoundation\StreamedResponse
    {
        $this->authorize('viewAny', Customer::class);

        $format = $request->query('format', 'csv');
        if (! in_array($format, ['csv', 'xlsx'], true)) {
            abort(422, 'Unsupported export format. Use csv or xlsx.');
        }

        return ($export)($format);
    }

    /** @return array<string, mixed> */
    private function editorPayload(Customer $customer): array
    {
        return [
            'customer'      => $customer,
            // Eager-load `default_discount_percent` so the JS form can
            // auto-apply a group's default when the user picks one.
            'groups'        => CustomerGroup::query()->active()->orderBy('name')
                                            ->get(['id', 'name', 'default_discount_percent']),
            'stores'        => Store::query()->where('is_active', true)->orderBy('name')->get(['id', 'name']),
            'genders'       => \App\Http\Requests\Admin\CustomerRequest::GENDERS,
            'countries'     => \App\Support\Countries::all(),
            // Default country for new addresses — the company's
            // configured country, falling back to the store's, then 'IN'.
            'defaultCountry' => \App\Models\Company::current()?->country_code
                                ?? Store::query()->value('country_code')
                                ?? 'IN',
        ];
    }
}
