<?php
declare(strict_types=1);

namespace Infolab\Controllers;

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

/**
 * Gestion de l'équipe d'un projet : pivot project_members avec un rôle.
 */
final class ProjectMembersController
{
    public function index(Request $req, array $params): void
    {
        $p = ProjectsController::findOr404($params['slug']);
        $rows = Database::fetchAll(
            'SELECT m.id, m.slug, m.display_name, m.initials, m.kind, m.avatar_url, m.website_url,
                    pm.role, pm.joined_at
             FROM project_members pm
             INNER JOIN members m ON m.id = pm.member_id
             WHERE pm.project_id = :pid
             ORDER BY pm.joined_at ASC',
            [':pid' => (int) $p['id']],
        );
        $data = array_map(static fn(array $r): array => [
            'id'           => (int) $r['id'],
            'slug'         => $r['slug'],
            'display_name' => $r['display_name'],
            'initials'     => $r['initials'],
            'kind'         => $r['kind'],
            'avatar_url'   => $r['avatar_url'],
            'website_url'  => $r['website_url'],
            'role'         => $r['role'],
            'joined_at'    => $r['joined_at'],
        ], $rows);
        Response::json(['data' => $data]);
    }

    public function store(Request $req, array $params): void
    {
        $p = ProjectsController::findOr404($params['slug']);
        $body = $req->json();
        $v = new Validator($body);
        $v->required('member_slug')->string('member_slug', 96);
        $v->in('role', ['admin', 'contributor', 'observer']);
        $v->check();

        $member = Database::fetchOne(
            'SELECT id, slug, display_name FROM members WHERE slug = :s',
            [':s' => (string) $body['member_slug']],
        );
        if ($member === null) {
            $v->setError('member_slug', 'not_found');
            $v->check();
        }

        $role = $body['role'] ?? 'contributor';

        // PK (project_id, member_id) -> on vérifie pour un 409 propre plutôt qu'une SQLException.
        $exists = Database::fetchValue(
            'SELECT 1 FROM project_members WHERE project_id = :pid AND member_id = :mid',
            [':pid' => (int) $p['id'], ':mid' => (int) $member['id']],
        );
        if ($exists !== null) {
            throw new \ApiException(409, [
                'error'   => 'conflict',
                'message' => 'Ce membre fait déjà partie du projet.',
            ]);
        }

        Database::insert('project_members', [
            'project_id' => (int) $p['id'],
            'member_id'  => (int) $member['id'],
            'role'       => $role,
        ]);

        ActivityLogger::log(
            'joined',
            sprintf('a rejoint le projet « %s »', $p['title']),
            (int) $p['id'],
            (int) $member['id'],
            (string) $member['display_name'],
        );

        Response::json([
            'project_id' => (int) $p['id'],
            'member'     => [
                'id'           => (int) $member['id'],
                'slug'         => $member['slug'],
                'display_name' => $member['display_name'],
            ],
            'role'      => $role,
            'joined_at' => gmdate('Y-m-d H:i:s'),
        ], 201);
    }

    public function update(Request $req, array $params): void
    {
        $p = ProjectsController::findOr404($params['slug']);
        $member = Database::fetchOne(
            'SELECT id, slug, display_name FROM members WHERE slug = :s',
            [':s' => $params['member_slug']],
        );
        if ($member === null) {
            throw new \ApiException(404, ['error' => 'not_found']);
        }

        $body = $req->json();
        $v = new Validator($body);
        $v->required('role')->in('role', ['admin', 'contributor', 'observer']);
        $v->check();

        $updated = Database::update(
            'project_members',
            ['role' => (string) $body['role']],
            'project_id = :pid AND member_id = :mid',
            [':pid' => (int) $p['id'], ':mid' => (int) $member['id']],
        );
        if ($updated === 0) {
            // Soit la ligne pivot n'existe pas, soit le rôle est déjà le même.
            // Vérifie pour distinguer 404 vs no-op.
            $exists = Database::fetchValue(
                'SELECT 1 FROM project_members WHERE project_id = :pid AND member_id = :mid',
                [':pid' => (int) $p['id'], ':mid' => (int) $member['id']],
            );
            if ($exists === null) {
                throw new \ApiException(404, ['error' => 'not_found']);
            }
        }

        Response::json([
            'project_id' => (int) $p['id'],
            'member'     => [
                'id'           => (int) $member['id'],
                'slug'         => $member['slug'],
                'display_name' => $member['display_name'],
            ],
            'role' => (string) $body['role'],
        ]);
    }

    public function destroy(Request $req, array $params): void
    {
        $p = ProjectsController::findOr404($params['slug']);
        $member = Database::fetchOne(
            'SELECT id, slug, display_name FROM members WHERE slug = :s',
            [':s' => $params['member_slug']],
        );
        if ($member === null) {
            throw new \ApiException(404, ['error' => 'not_found']);
        }
        $deleted = Database::delete(
            'project_members',
            'project_id = :pid AND member_id = :mid',
            [':pid' => (int) $p['id'], ':mid' => (int) $member['id']],
        );
        if ($deleted === 0) {
            throw new \ApiException(404, ['error' => 'not_found']);
        }

        ActivityLogger::log(
            'left',
            sprintf('a quitté le projet « %s »', $p['title']),
            (int) $p['id'],
            (int) $member['id'],
            (string) $member['display_name'],
        );
        Response::noContent();
    }
}
