<?php

namespace App\Http\Controllers;

use App\Models\Branch;
use App\Models\Task;
use App\Models\User;
use App\Services\AuditService;
use App\Support\Authorization\PermissionName;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

class TaskController extends Controller
{
    public function index(Request $request): View
    {
        $branchIds = $this->branchIds($request);
        $query = Task::query()->with(['branch','assignee','creator'])->whereIn('branch_id',$branchIds);

        if ($request->filled('status')) $query->where('status',(string)$request->string('status'));
        if ($request->filled('assigned_to')) $query->where('assigned_to',(int)$request->integer('assigned_to'));
        if ($request->filled('branch_id')) {
            $branchId = (int)$request->integer('branch_id');
            abort_unless(in_array($branchId,$branchIds,true),403);
            $query->where('branch_id',$branchId);
        }
        if ($request->filled('search')) {
            $term = trim((string)$request->string('search'));
            $query->where(fn ($q) => $q->where('title','like','%'.$term.'%')->orWhere('description','like','%'.$term.'%'));
        }

        $summaryQuery = Task::query()->whereIn('branch_id',$branchIds);
        $summary = collect(Task::STATUSES)->mapWithKeys(fn ($status) => [$status => (clone $summaryQuery)->where('status',$status)->count()]);

        return view('tasks.index',[
            'tasks'=>$query->orderByRaw("CASE status WHEN 'pending' THEN 1 WHEN 'in_progress' THEN 2 WHEN 'completed' THEN 3 ELSE 4 END")
                ->orderByRaw('CASE WHEN due_at IS NULL THEN 1 ELSE 0 END')->orderBy('due_at')->paginate(25)->withQueryString(),
            'summary'=>$summary,'branches'=>Branch::whereIn('id',$branchIds)->orderBy('name')->get(),
            'staff'=>$this->staff($branchIds),'statuses'=>Task::STATUSES,
        ]);
    }

    public function create(Request $request): View
    {
        Gate::forUser($request->user())->authorize(PermissionName::TASKS_CREATE->value);
        $branchIds = $this->branchIds($request);
        return view('tasks.create',[
            'task'=>new Task(['status'=>'pending','priority'=>'normal','category'=>'general']),
            'branches'=>Branch::whereIn('id',$branchIds)->orderBy('name')->get(),'staff'=>$this->staff($branchIds),
            'statuses'=>Task::STATUSES,'priorities'=>Task::PRIORITIES,'categories'=>Task::CATEGORIES,
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        Gate::forUser($request->user())->authorize(PermissionName::TASKS_CREATE->value);
        $data = $this->validated($request);
        $this->authorizeSelections($request,(int)$data['branch_id'],$data['assigned_to'] ?? null, ! empty($data['assigned_to']));
        $task = Task::create(array_merge($data,['created_by'=>$request->user()->id,'updated_by'=>$request->user()->id]));
        AuditService::record('task.created',$task,[],$task->toArray());
        return redirect()->route('tasks.show',$task)->with('success','Task created.');
    }

    public function show(Request $request, Task $task): View
    {
        Gate::forUser($request->user())->authorize('view', $task);
        $task->load(['branch','assignee','creator','updater','completedBy','cancelledBy']);
        return view('tasks.show',['task'=>$task]);
    }

    public function edit(Request $request, Task $task): View
    {
        Gate::forUser($request->user())->authorize('update', $task);
        $branchIds = $this->branchIds($request);
        return view('tasks.edit',[
            'task'=>$task,'branches'=>Branch::whereIn('id',$branchIds)->orderBy('name')->get(),'staff'=>$this->staff($branchIds),
            'statuses'=>Task::STATUSES,'priorities'=>Task::PRIORITIES,'categories'=>Task::CATEGORIES,
        ]);
    }

    public function update(Request $request, Task $task): RedirectResponse
    {
        Gate::forUser($request->user())->authorize('update', $task);
        $data = $this->validated($request);
        $assignmentChanged = (int) ($task->assigned_to ?? 0) !== (int) ($data['assigned_to'] ?? 0);
        $this->authorizeSelections($request,(int)$data['branch_id'],$data['assigned_to'] ?? null,$assignmentChanged);
        $before = $task->toArray();
        $task->update(array_merge($data,['updated_by'=>$request->user()->id]));
        AuditService::record('task.updated',$task,$before,$task->fresh()->toArray());
        return redirect()->route('tasks.show',$task)->with('success','Task updated.');
    }

    public function status(Request $request, Task $task): RedirectResponse
    {
        Gate::forUser($request->user())->authorize('updateStatus', $task);

        $data = $request->validate(['status'=>['required',Rule::in(Task::STATUSES)]]);
        if ($data['status'] === $task->status) return back()->with('success','Task status is unchanged.');
        abort_if(in_array($task->status,['completed','cancelled'],true),422,'Completed or cancelled tasks are permanent.');

        $before = $task->toArray();
        $changes = ['status'=>$data['status'],'updated_by'=>$request->user()->id];
        if ($data['status'] === 'completed') $changes += ['completed_by'=>$request->user()->id,'completed_at'=>now()];
        if ($data['status'] === 'cancelled') $changes += ['cancelled_by'=>$request->user()->id,'cancelled_at'=>now()];
        $task->update($changes);
        AuditService::record('task.status_changed',$task,$before,$task->fresh()->toArray());
        return back()->with('success','Task status updated.');
    }

    private function validated(Request $request): array
    {
        return $request->validate([
            'branch_id'=>['required','integer','exists:branches,id'],'title'=>['required','string','max:255'],
            'description'=>['nullable','string','max:10000'],'category'=>['required',Rule::in(Task::CATEGORIES)],
            'priority'=>['required',Rule::in(Task::PRIORITIES)],'due_at'=>['nullable','date'],'assigned_to'=>['nullable','integer','exists:users,id'],
        ]);
    }

    private function branchIds(Request $request): array
    {
        return app(\App\Services\BranchAccessService::class)->accessibleBranchIds($request->user());
    }

    private function staff(array $branchIds)
    {
        return User::query()
            ->where('status','active')
            ->where(function ($query) use ($branchIds): void {
                $query->whereIn('primary_branch_id',$branchIds)
                    ->orWhereHas('branches',fn ($branches) => $branches->whereIn('branches.id',$branchIds));
            })
            ->orderBy('name')
            ->get();
    }

    private function authorizeSelections(Request $request, int $branchId, mixed $assigneeId, bool $assignmentChanged): void
    {
        abort_unless($request->user()->canAccessBranch($branchId),403);
        if ($assignmentChanged) {
            Gate::forUser($request->user())->authorize(PermissionName::TASKS_ASSIGN->value);
        }
        if ($assigneeId) {
            abort_unless(User::query()->whereKey((int)$assigneeId)->where('status','active')
                ->where(function ($query) use ($branchId): void {
                    $query->where('primary_branch_id',$branchId)
                        ->orWhereHas('branches',fn ($branches) => $branches->where('branches.id',$branchId));
                })->exists(),422,'Assignee must be an active staff member of the selected branch.');
        }
    }
}
