<?php

namespace App\Http\Controllers\Settings;

use App\Http\Controllers\Controller;
use App\Models\Branch;
use App\Models\User;
use App\Models\Dealer;
use App\Support\DealerContext;
use App\Support\BranchCodeGenerator;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;

class BranchController extends Controller
{
    public function index(): View
    {
        $branches = Branch::query()
            ->withCount('users')
            ->orderByDesc('is_default')
            ->orderBy('name')
            ->paginate(20);

        return view('settings.branches.index', compact('branches'));
    }

    public function create(): View
    {
        return view('settings.branches.create');
    }

    public function edit(int $branch): View
    {
        $branchModel = $this->findScopedBranch($branch);

        return view('settings.branches.edit', [
            'branch' => $branchModel,
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $dealerId = DealerContext::currentDealerId();

        if ($dealerId === null) {
            abort(500, 'No current dealer context available.');
        }

        // Normalize name and address before validation so uniqueness and required checks apply to trimmed values.
        $normalizedName = trim((string) $request->input('name', ''));
        $normalizedAddress = trim((string) $request->input('address', ''));
        $request->merge([
            'name' => $normalizedName,
            'address' => $normalizedAddress,
        ]);

        $data = $request->validate([
            'name' => [
                'required',
                'string',
                'max:255',
                Rule::unique('branches', 'name')->where(fn ($query) => $query->where('dealer_id', $dealerId)),
            ],
            'address' => [
                'required',
                'string',
                'max:255',
            ],
            'is_default' => ['sometimes', 'boolean'],
            'is_active' => ['sometimes', 'boolean'],
        ]);

        $isDefault = (bool) ($data['is_default'] ?? false);
        $isActive = array_key_exists('is_active', $data)
            ? (bool) $data['is_active']
            : true;

        $isFirstBranch = Branch::withoutGlobalScope('dealer_scope')
            ->where('dealer_id', $dealerId)
            ->count() === 0;
        if ($isFirstBranch) {
            $isDefault = true;
        }

        $dealer = Dealer::query()->select(['id', 'code'])->findOrFail($dealerId);

        DB::transaction(function () use ($data, $isDefault, $isActive, $dealer): void {
            if ($isDefault) {
                Branch::withoutGlobalScope('dealer_scope')
                    ->where('dealer_id', $dealer->id)
                    ->where('is_default', true)
                    ->update(['is_default' => false]);
            }

            $branch = new Branch();
            $branch->dealer_id = $dealer->id;
            $branch->name = $data['name'];
            $branch->address = $data['address'];
            $branch->code = BranchCodeGenerator::nextForDealer($dealer);
            $branch->is_default = $isDefault;
            $branch->is_active = $isActive;
            $branch->save();
        });

        return redirect()
            ->route('settings.branches.index')
            ->with('status', 'Branch created successfully.');
    }

    public function update(Request $request, int $branch): RedirectResponse
    {
        $branchModel = $this->findScopedBranch($branch);
        $dealerId = $branchModel->dealer_id;

        $normalizedName = trim((string) $request->input('name', ''));
        $normalizedAddress = trim((string) $request->input('address', ''));
        $request->merge([
            'name' => $normalizedName,
            'address' => $normalizedAddress,
        ]);

        $data = $request->validate([
            'name' => [
                'required',
                'string',
                'max:255',
                Rule::unique('branches', 'name')
                    ->where(fn ($query) => $query->where('dealer_id', $dealerId))
                    ->ignore($branchModel->id),
            ],
            'address' => [
                'required',
                'string',
                'max:255',
            ],
            'is_active' => ['sometimes', 'boolean'],
        ]);

        $isActive = array_key_exists('is_active', $data)
            ? (bool) $data['is_active']
            : (bool) $branchModel->is_active;

        if ($branchModel->is_default && $isActive === false) {
            return back()
                ->withErrors(['is_active' => 'The default branch cannot be disabled.'])
                ->withInput();
        }

        $branchModel->name = $data['name'];
        $branchModel->address = $data['address'];
        $branchModel->is_active = $isActive;
        $branchModel->save();

        return redirect()
            ->route('settings.branches.index')
            ->with('status', 'Branch updated successfully.');
    }

    public function setDefault(int $branch): RedirectResponse
    {
        $branchModel = $this->findScopedBranch($branch);

        DB::transaction(function () use ($branchModel): void {
            Branch::withoutGlobalScope('dealer_scope')
                ->where('dealer_id', $branchModel->dealer_id)
                ->where('is_default', true)
                ->update(['is_default' => false]);

            $branchModel->is_default = true;
            $branchModel->is_active = true;
            $branchModel->save();
        });

        return back()->with('status', 'Default branch updated.');
    }

    public function toggleActive(int $branch): RedirectResponse
    {
        $branchModel = $this->findScopedBranch($branch);

        $newActive = ! (bool) $branchModel->is_active;

        if (! $newActive && $branchModel->is_default) {
            return back()
                ->withErrors(['is_active' => 'The default branch cannot be disabled.']);
        }

        $branchModel->is_active = $newActive;
        $branchModel->save();

        return back()->with('status', $newActive ? 'Branch activated.' : 'Branch deactivated.');
    }

    public function users(int $branch): View
    {
        $branchModel = $this->findScopedBranch($branch);

        $assignedUsers = User::query()
            ->where('dealer_id', $branchModel->dealer_id)
            ->where('branch_id', $branchModel->id)
            ->with(['department'])
            ->orderBy('name')
            ->orderBy('surname')
            ->get();

        $availableUsers = User::query()
            ->where('dealer_id', $branchModel->dealer_id)
            ->where(function ($query) use ($branchModel) {
                $query->whereNull('branch_id')
                    ->orWhere('branch_id', '!=', $branchModel->id);
            })
            ->orderBy('name')
            ->orderBy('surname')
            ->get();

        $otherBranches = Branch::withoutGlobalScope('dealer_scope')
            ->where('dealer_id', $branchModel->dealer_id)
            ->where('id', '!=', $branchModel->id)
            ->orderByDesc('is_default')
            ->orderBy('name')
            ->get();

        return view('settings.branches.users', [
            'branch' => $branchModel,
            'assignedUsers' => $assignedUsers,
            'availableUsers' => $availableUsers,
            'otherBranches' => $otherBranches,
        ]);
    }

    public function assignUser(Request $request, int $branch): RedirectResponse
    {
        $branchModel = $this->findScopedBranch($branch);

        $data = $request->validate([
            'user_id' => ['required', 'integer', 'exists:users,id'],
        ]);

        $user = User::query()
            ->where('id', $data['user_id'])
            ->where('dealer_id', $branchModel->dealer_id)
            ->firstOrFail();

        $user->branch_id = $branchModel->id;
        $user->save();

        return redirect()
            ->route('settings.branches.users.index', $branchModel->id)
            ->with('status', 'User assigned to branch.');
    }

    public function reassignUser(Request $request, int $branch, int $user): RedirectResponse
    {
        $branchModel = $this->findScopedBranch($branch);

        $data = $request->validate([
            'new_branch_id' => ['required', 'integer', 'exists:branches,id'],
        ]);

        $targetBranch = $this->findScopedBranch($data['new_branch_id']);

        if ($targetBranch->dealer_id !== $branchModel->dealer_id) {
            abort(403, 'Cannot move user to a branch in a different dealer.');
        }

        $userModel = User::query()
            ->where('id', $user)
            ->where('dealer_id', $branchModel->dealer_id)
            ->where('branch_id', $branchModel->id)
            ->firstOrFail();

        $userModel->branch_id = $targetBranch->id;
        $userModel->save();

        return redirect()
            ->route('settings.branches.users.index', $branchModel->id)
            ->with('status', 'User moved to new branch.');
    }

    protected function findScopedBranch(int $id): Branch
    {
        $dealerId = DealerContext::currentDealerId();

        if ($dealerId === null) {
            abort(500, 'No current dealer context available.');
        }

        return Branch::withoutGlobalScope('dealer_scope')
            ->where('dealer_id', $dealerId)
            ->whereKey($id)
            ->firstOrFail();
    }
}

