<?php

namespace App\Http\Controllers\Company\Masters;

use App\Http\Controllers\Controller;
use App\Models\{Account, AuditLog, Currency, Customer, PriceLevel};
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;

class CustomersController extends Controller
{
    public function index(Request $request)
    {
        $this->gate('ar', 'view');

        $customers = $this->filteredCustomers($request);
        $tree = $this->buildTree($customers);
        $expandAll = $request->filled('search');

        $stats = [
            'total'    => Customer::count(),
            'active'   => Customer::where('is_active', true)->count(),
            'inactive' => Customer::where('is_active', false)->count(),
        ];

        return view('masters.customers.index', compact('tree', 'stats', 'expandAll', 'customers'));
    }

    public function create()
    {
        $this->gate('ar', 'create');
        return view('masters.customers.create', $this->formData());
    }

    public function store(Request $request)
    {
        $this->gate('ar', 'create');

        $companyId = auth()->user()->company_id;
        $validated = $this->validateCustomer($request, $companyId);
        $validated = $this->applyJobAttributes($validated);

        $customer = Customer::withoutGlobalScopes()->create(array_merge(
            $validated,
            ['company_id' => $companyId, 'is_active' => true]
        ));

        AuditLog::record('create', 'customers', (string) $customer->id, null,
            ['name' => $customer->name], "Customer created: {$customer->name}");

        $label = $customer->is_job ? 'Job' : 'Customer';

        return redirect()->route('masters.customers.index')
            ->with('success', "{$label} '{$customer->name}' created successfully.");
    }

    public function edit(Customer $customer)
    {
        $this->gate('ar', 'edit');
        $this->sameCompany($customer);
        return view('masters.customers.edit', array_merge($this->formData($customer), compact('customer')));
    }

    public function update(Request $request, Customer $customer)
    {
        $this->gate('ar', 'edit');
        $this->sameCompany($customer);

        $companyId = auth()->user()->company_id;
        $validated = $this->validateCustomer($request, $companyId, $customer->id);
        $validated = $this->applyJobAttributes($validated);

        $old = $customer->only(['name','email','phone','is_active']);
        $customer->update($validated);

        AuditLog::record('update', 'customers', (string) $customer->id, $old,
            $customer->only(['name','email','phone','is_active']),
            "Customer updated: {$customer->name}");

        $label = $customer->is_job ? 'Job' : 'Customer';

        return redirect()->route('masters.customers.index')
            ->with('success', "{$label} '{$customer->name}' updated successfully.");
    }

    public function toggleActive(Customer $customer)
    {
        $this->gate('ar', 'edit');
        $this->sameCompany($customer);

        $old = $customer->is_active;
        $customer->update(['is_active' => !$old]);

        AuditLog::record('status_change', 'customers', (string) $customer->id,
            ['is_active' => $old], ['is_active' => !$old],
            ($old ? 'Deactivated' : 'Activated') . " customer: {$customer->name}");

        return back()->with('success', "Customer " . (!$old ? 'activated' : 'deactivated') . " successfully.");
    }

    public function destroy(Customer $customer)
    {
        $this->gate('ar', 'edit');
        $this->sameCompany($customer);

        $guard = app(\App\Services\MasterDeletionGuard::class);
        if ($message = $guard->blockingMessage($customer)) {
            return back()->withErrors(['error' => $message]);
        }

        $name = $customer->name;
        $id = $customer->id;
        $guard->hardDelete($customer);

        AuditLog::record('hard_delete', 'customers', (string) $id,
            null, null, "Customer permanently deleted: {$name}");

        return redirect()->route('masters.customers.index')
            ->with('success', "Customer '{$name}' permanently deleted.");
    }

    // ── AJAX search ───────────────────────────────────────────────────────────
    public function search(Request $request)
    {
        $q = $request->input('q', '');
        $customers = Customer::where('is_active', true)
            ->when($q, fn($query) => $query->where(fn($qq) =>
                $qq->where('name','like',"%{$q}%")->orWhere('code','like',"%{$q}%")
            ))
            ->orderBy('name')->limit(50)->get(['id','code','name','currency_id','price_level_id']);

        return response()->json([
            'results' => $customers->map(fn($c) => [
                'id'             => $c->id,
                'text'           => ($c->code ? "[{$c->code}] " : '') . $c->name,
                'currency_id'    => $c->currency_id,
                'price_level_id' => $c->price_level_id,
            ])
        ]);
    }

    private function validateCustomer(Request $request, int $companyId, ?int $ignoreId = null): array
    {
        $validated = $request->validate([
            'parent_id'             => [
                'nullable',
                'integer',
                Rule::exists('customers', 'id')->where(function ($query) use ($companyId) {
                    $query->where('company_id', $companyId)
                        ->whereNull('deleted_at')
                        ->where('is_job', false)
                        ->whereNull('parent_id');
                }),
            ],
            'code'                  => ['nullable','string','max:30',
                Rule::unique('customers')->where(fn($q) => $q->where('company_id',$companyId)->whereNull('deleted_at'))->ignore($ignoreId)],
            'name'                  => 'required|string|max:150',
            'contact_person'        => 'nullable|string|max:100',
            'email'                 => 'nullable|email|max:150',
            'phone'                 => 'nullable|string|max:30',
            'billing_address'       => 'nullable|string|max:500',
            'shipping_address'      => 'nullable|string|max:500',
            'tax_id'                => 'nullable|string|max:50',
            'currency_id'           => 'nullable|integer|exists:currencies,id',
            'price_level_id'        => 'nullable|integer|exists:price_levels,id',
            'credit_limit'          => 'nullable|numeric|min:0',
            'payment_terms_days'    => 'nullable|integer|min:0|max:365',
            'account_receivable_id' => 'nullable|integer|exists:accounts,id',
            'notes'                 => 'nullable|string|max:1000',
            'is_active'             => 'boolean',
        ]);

        if ($ignoreId && !empty($validated['parent_id']) && (int) $validated['parent_id'] === $ignoreId) {
            throw ValidationException::withMessages([
                'parent_id' => 'A customer cannot be a job of itself.',
            ]);
        }

        if ($ignoreId && !empty($validated['parent_id'])) {
            $customer = Customer::withoutGlobalScopes()->find($ignoreId);
            if ($customer && $customer->children()->exists()) {
                throw ValidationException::withMessages([
                    'parent_id' => 'This customer has jobs under it and cannot be assigned to another customer.',
                ]);
            }
        }

        return $validated;
    }

    private function applyJobAttributes(array $validated): array
    {
        $parentId = !empty($validated['parent_id']) ? (int) $validated['parent_id'] : null;

        if ($parentId) {
            $validated['parent_id'] = $parentId;
            $validated['is_job'] = true;
            $validated['depth'] = 1;
            $validated['path'] = (string) $parentId;
        } else {
            $validated['parent_id'] = null;
            $validated['is_job'] = false;
            $validated['depth'] = 0;
            $validated['path'] = null;
        }

        return $validated;
    }

    private function formData(?Customer $customer = null): array
    {
        $companyId = auth()->user()->company_id;

        $jobOfQuery = Customer::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->whereNull('deleted_at')
            ->where('is_job', false)
            ->whereNull('parent_id')
            ->where('is_active', true)
            ->orderBy('name');

        if ($customer) {
            $jobOfQuery->where('id', '!=', $customer->id);
        }

        return [
            'currencies'       => Currency::withoutGlobalScopes()->where('company_id',$companyId)->where('is_active',true)->orderBy('code')->get(),
            'priceLevels'      => PriceLevel::withoutGlobalScopes()->where('company_id',$companyId)->where('is_active',true)->orderBy('name')->get(),
            'arAccounts'       => Account::withoutGlobalScopes()->where('company_id',$companyId)->where('is_group',false)->where('is_active',true)->whereNull('deleted_at')->orderBy('code')->get(['id','code','name']),
            'jobOfCustomers'   => $jobOfQuery->get(['id','code','name']),
            'canSelectJobOf'   => !$customer || !$customer->children()->exists(),
        ];
    }

    private function filteredCustomers(Request $request)
    {
        $query = Customer::withTrashed(false)
            ->with('currency', 'priceLevel')
            ->orderBy('name');

        if ($request->filled('status')) {
            $query->where('is_active', $request->status === 'active');
        }

        $all = $query->get();

        if (!$request->filled('search')) {
            return $all;
        }

        $term = strtolower(trim($request->search));
        $matches = static function (Customer $customer) use ($term): bool {
            return str_contains(strtolower($customer->name), $term)
                || str_contains(strtolower($customer->code ?? ''), $term)
                || str_contains(strtolower($customer->email ?? ''), $term)
                || str_contains(strtolower($customer->phone ?? ''), $term);
        };

        $visibleIds = collect();

        foreach ($all as $customer) {
            if (!$matches($customer)) {
                continue;
            }

            $visibleIds->push($customer->id);

            if ($customer->parent_id) {
                $visibleIds->push($customer->parent_id);
            }

            foreach ($all as $child) {
                if ($child->parent_id === $customer->id) {
                    $visibleIds->push($child->id);
                }
            }
        }

        return $all->whereIn('id', $visibleIds->unique()->values());
    }

    private function buildTree($customers, $parentId = null): array
    {
        $branch = [];

        foreach ($customers as $customer) {
            if (($customer->parent_id ?? null) != $parentId) {
                continue;
            }

            $customer->children_tree = $this->buildTree($customers, $customer->id);
            $branch[] = $customer;
        }

        usort($branch, static fn (Customer $a, Customer $b) => strcasecmp($a->name, $b->name));

        return $branch;
    }

    private function gate(string $module, string $action): void
    {
        if (!auth()->user()->hasPermission($module, $action)) abort(403);
    }

    private function sameCompany(Customer $customer): void
    {
        if ($customer->company_id !== auth()->user()->company_id) abort(403);
    }
}
