<?php
declare(strict_types=1);

namespace Infolab\Controllers;

use Infolab\ActivityLogger;
use Infolab\Database;
use Infolab\Request;
use Infolab\Response;
use Infolab\Validator;

/**
 * Messages d'un projet, arborescence à 1 niveau (parent_id pour les réponses).
 * Liste renvoyée en arbre : racines DESC date, réponses ASC date sous chaque racine.
 */
final class DiscussionsController
{
    public function index(Request $req, array $params): void
    {
        $p = ProjectsController::findOr404($params['slug']);

        $roots = Database::fetchAll(
            'SELECT id, project_id, parent_id, author_name, author_member_id, body, created_at, updated_at
             FROM discussions WHERE project_id = :pid AND parent_id IS NULL
             ORDER BY created_at DESC, id DESC',
            [':pid' => (int) $p['id']],
        );
        if ($roots === []) {
            Response::json(['data' => []]);
            return;
        }

        // 1 requête en plus pour toutes les réponses, regroupées par parent_id.
        $rootIds = array_map(static fn(array $r): int => (int) $r['id'], $roots);
        $in      = implode(',', array_fill(0, count($rootIds), '?'));
        $replies = Database::fetchAll(
            "SELECT id, project_id, parent_id, author_name, author_member_id, body, created_at, updated_at
             FROM discussions WHERE parent_id IN ($in) ORDER BY created_at ASC, id ASC",
            $rootIds,
        );

        $byParent = [];
        foreach ($replies as $r) {
            $byParent[(int) $r['parent_id']][] = self::present($r);
        }

        $tree = [];
        foreach ($roots as $r) {
            $node = self::present($r);
            $node['replies'] = $byParent[(int) $r['id']] ?? [];
            $tree[] = $node;
        }
        Response::json(['data' => $tree]);
    }

    public function store(Request $req, array $params): void
    {
        $p = ProjectsController::findOr404($params['slug']);
        $body = $req->json();
        $v = new Validator($body);
        $v->required('author_name')->string('author_name', 128);
        $v->required('body')->string('body');
        $v->integer('parent_id', 1);
        $v->integer('author_member_id', 1);
        $v->check();

        $parentId = null;
        if (isset($body['parent_id'])) {
            $parent = Database::fetchOne(
                'SELECT id, project_id FROM discussions WHERE id = :id',
                [':id' => (int) $body['parent_id']],
            );
            if ($parent === null || (int) $parent['project_id'] !== (int) $p['id']) {
                $v->setError('parent_id', 'not_found');
                $v->check();
            }
            $parentId = (int) $parent['id'];
        }

        $authorMemberId = null;
        if (isset($body['author_member_id'])) {
            $exists = Database::fetchValue(
                'SELECT 1 FROM members WHERE id = :id',
                [':id' => (int) $body['author_member_id']],
            );
            if ($exists === null) {
                $v->setError('author_member_id', 'not_found');
                $v->check();
            }
            $authorMemberId = (int) $body['author_member_id'];
        }

        $id = Database::insert('discussions', [
            'project_id'       => (int) $p['id'],
            'parent_id'        => $parentId,
            'author_name'      => (string) $body['author_name'],
            'author_member_id' => $authorMemberId,
            'body'             => (string) $body['body'],
        ]);

        ActivityLogger::log(
            'posted_discussion',
            sprintf('a publié un message dans « %s »', $p['title']),
            (int) $p['id'],
            $authorMemberId,
            (string) $body['author_name'],
        );

        $row = Database::fetchOne(
            'SELECT * FROM discussions WHERE id = :id',
            [':id' => $id],
        );
        Response::json(self::present($row), 201);
    }

    public function update(Request $req, array $params): void
    {
        $p   = ProjectsController::findOr404($params['slug']);
        $msg = self::findMsgOr404((int) $params['id'], (int) $p['id']);

        $body = $req->json();
        $v = new Validator($body);
        $v->string('author_name', 128);
        $v->string('body');
        $v->check();

        $update = [];
        foreach (['author_name', 'body'] as $f) {
            if (array_key_exists($f, $body)) {
                $update[$f] = $body[$f];
            }
        }
        if ($update !== []) {
            Database::update('discussions', $update, 'id = :id', [':id' => (int) $msg['id']]);
        }
        $fresh = Database::fetchOne('SELECT * FROM discussions WHERE id = :id', [':id' => (int) $msg['id']]);
        Response::json(self::present($fresh));
    }

    public function destroy(Request $req, array $params): void
    {
        $p   = ProjectsController::findOr404($params['slug']);
        $msg = self::findMsgOr404((int) $params['id'], (int) $p['id']);
        // FK parent_id ON DELETE CASCADE -> les réponses partent avec.
        Database::delete('discussions', 'id = :id', [':id' => (int) $msg['id']]);
        Response::noContent();
    }

    private static function findMsgOr404(int $id, int $projectId): array
    {
        $row = Database::fetchOne(
            'SELECT * FROM discussions WHERE id = :id AND project_id = :pid',
            [':id' => $id, ':pid' => $projectId],
        );
        if ($row === null) {
            throw new \ApiException(404, ['error' => 'not_found']);
        }
        return $row;
    }

    private static function present(array $row): array
    {
        return [
            'id'               => (int) $row['id'],
            'project_id'       => (int) $row['project_id'],
            'parent_id'        => $row['parent_id'] === null ? null : (int) $row['parent_id'],
            'author_name'      => $row['author_name'],
            'author_member_id' => $row['author_member_id'] === null ? null : (int) $row['author_member_id'],
            'body'             => $row['body'],
            'created_at'       => $row['created_at'],
            'updated_at'       => $row['updated_at'],
        ];
    }
}
