<?php

namespace App\Http\Controllers\Api;

use App\Events\ProjectTaskStageChanged;
use App\Http\Controllers\Controller;
use App\Http\Resources\ProjectResource;
use App\Http\Resources\TaskResource;
use App\Models\Project;
use App\Models\ProjectStage;
use App\Models\Task;
use App\Support\CrmModuleManager;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;

class ProjectController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Project::class, 'project');
    }

    public function index(Request $request)
    {
        $status = $request->input('status');
        $search = trim((string) $request->input('q', ''));

        $projects = Project::query()
            ->with(['company', 'deal', 'owner', 'manager'])
            ->withCount('tasks')
            ->when($status, fn ($query) => $query->where('status', $status))
            ->when($search !== '', function ($query) use ($search): void {
                $query->where(function ($sub) use ($search): void {
                    $sub->where('name', 'like', "%{$search}%")
                        ->orWhere('code', 'like', "%{$search}%");
                });
            })
            ->latest()
            ->paginate(20)
            ->withQueryString();

        return ProjectResource::collection($projects);
    }

    public function store(Request $request, CrmModuleManager $moduleManager): ProjectResource
    {
        $payload = $this->validatedData($request);
        $payload['owner_id'] = $payload['owner_id'] ?: $request->user()->id;
        $payload = $moduleManager->applyPayloadHooks('projects.store', $payload, [
            'hook' => 'projects.store',
            'user_id' => $request->user()->id,
            'source' => 'api',
        ], array_keys($payload));
        $payload['completed_at'] = $payload['status'] === 'completed' ? now() : null;

        $project = Project::create($payload);
        $project->members()->syncWithoutDetaching([$project->owner_id => ['role' => 'owner', 'joined_at' => now()]]);

        return ProjectResource::make($project->load(['company', 'deal', 'owner', 'manager', 'members']));
    }

    public function show(Project $project): ProjectResource
    {
        return ProjectResource::make($project->load(['company', 'deal', 'owner', 'manager', 'members'])->loadCount('tasks'));
    }

    public function update(Request $request, Project $project, CrmModuleManager $moduleManager): ProjectResource
    {
        $payload = $this->validatedData($request, $project);
        $payload = $moduleManager->applyPayloadHooks('projects.update', $payload, [
            'hook' => 'projects.update',
            'user_id' => $request->user()->id,
            'project_id' => $project->id,
            'source' => 'api',
        ], array_keys($payload));
        $payload['completed_at'] = $payload['status'] === 'completed'
            ? ($project->completed_at ?? now())
            : null;

        $project->update($payload);

        return ProjectResource::make($project->load(['company', 'deal', 'owner', 'manager', 'members'])->loadCount('tasks'));
    }

    public function destroy(Project $project)
    {
        $project->delete();

        return response()->noContent();
    }

    public function updateTaskStage(Request $request, Project $project, Task $task): TaskResource
    {
        $this->authorize('manageTasks', $project);

        if ($task->project_id !== $project->id) {
            abort(404);
        }

        $validated = $request->validate([
            'project_stage_id' => ['nullable', 'exists:project_stages,id'],
        ]);

        $stage = null;
        if (! empty($validated['project_stage_id'])) {
            $stage = ProjectStage::query()
                ->where('project_id', $project->id)
                ->findOrFail((int) $validated['project_stage_id']);
        }

        $sortOrderQuery = Task::query()->where('project_id', $project->id);
        if ($stage) {
            $sortOrderQuery->where('project_stage_id', $stage->id);
        } else {
            $sortOrderQuery->whereNull('project_stage_id');
        }
        $nextSortOrder = ((int) $sortOrderQuery->max('sort_order')) + 1;

        $status = $task->status;
        if ($stage?->is_done) {
            $status = 'done';
        } elseif ($task->status === 'done') {
            $status = 'in_progress';
        }

        $task->update([
            'project_stage_id' => $stage?->id,
            'status' => $status,
            'sort_order' => $nextSortOrder,
            'completed_at' => $status === 'done' ? ($task->completed_at ?? now()) : null,
        ]);

        $task->refresh()->load(['assignee', 'creator', 'deal', 'company', 'contact', 'project', 'projectStage']);
        $project->recalculateProgress();
        $this->broadcastToOthers(new ProjectTaskStageChanged($task));

        return TaskResource::make($task);
    }

    /**
     * @return array<string, mixed>
     */
    private function validatedData(Request $request, ?Project $project = null): array
    {
        $validated = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'code' => [
                'nullable',
                'string',
                'max:40',
                Rule::unique('projects', 'code')->ignore($project?->id),
            ],
            'description' => ['nullable', 'string'],
            'company_id' => ['nullable', 'exists:companies,id'],
            'deal_id' => ['nullable', 'exists:deals,id'],
            'owner_id' => ['nullable', 'exists:users,id'],
            'manager_id' => ['nullable', 'exists:users,id'],
            'status' => ['required', Rule::in(['planned', 'active', 'on_hold', 'completed', 'cancelled'])],
            'priority' => ['required', Rule::in(['low', 'medium', 'high', 'critical'])],
            'health' => ['required', Rule::in(['normal', 'warning', 'risk'])],
            'budget' => ['nullable', 'numeric', 'min:0'],
            'spent' => ['nullable', 'numeric', 'min:0'],
            'starts_at' => ['nullable', 'date'],
            'due_at' => ['nullable', 'date'],
            'visibility' => ['required', Rule::in(['team', 'private', 'public'])],
            'notes' => ['nullable', 'string'],
        ]);

        $validated['code'] = $this->makeUniqueCode($validated['code'] ?? null, (string) $validated['name'], $project?->id);
        $validated['budget'] = (float) ($validated['budget'] ?? 0);
        $validated['spent'] = (float) ($validated['spent'] ?? 0);

        return $validated;
    }

    private function makeUniqueCode(?string $inputCode, string $name, ?int $projectId = null): string
    {
        $base = Str::upper(Str::slug(trim((string) $inputCode), '_'));
        if ($base === '') {
            $base = Str::upper(Str::slug($name, '_'));
        }
        if ($base === '') {
            $base = 'PROJECT';
        }

        $base = Str::limit($base, 28, '');
        $code = $base;
        $counter = 1;

        while (Project::query()
            ->when($projectId, fn ($query) => $query->where('id', '!=', $projectId))
            ->where('code', $code)
            ->exists()) {
            $counter++;
            $code = Str::limit($base, 24, '').'_'.$counter;
        }

        return $code;
    }
}
