<?php
declare(strict_types=1);

namespace Infolab\Controllers;

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

/**
 * CRUD des membres (annuaire des contributeurs). Pas une table d'auth.
 */
final class MembersController
{
    private const SELECT = 'SELECT id, slug, display_name, initials, kind, bio, avatar_url, website_url, contact_email, created_at, updated_at FROM members';

    public function index(Request $req, array $params): void
    {
        [$page, $perPage, $offset] = self::pagination($req);
        $total = (int) Database::fetchValue('SELECT COUNT(*) FROM members');
        // LIMIT/OFFSET interpolés depuis des entiers validés -> safe.
        $rows = Database::fetchAll(self::SELECT . " ORDER BY display_name ASC LIMIT $perPage OFFSET $offset");
        Response::json([
            'data' => array_map([self::class, 'present'], $rows),
            'meta' => self::meta($page, $perPage, $total),
        ]);
    }

    public function show(Request $req, array $params): void
    {
        $m = self::findOr404($params['slug']);
        $projects = Database::fetchAll(
            'SELECT p.id, p.slug, p.title, p.status, p.progress_percent,
                    c.slug AS category_slug, c.name AS category_name, c.color_hex AS category_color_hex,
                    pm.role, pm.joined_at
             FROM project_members pm
             INNER JOIN projects p   ON p.id = pm.project_id AND p.deleted_at IS NULL
             INNER JOIN categories c ON c.id = p.category_id
             WHERE pm.member_id = :id
             ORDER BY p.title ASC',
            [':id' => (int) $m['id']],
        );
        $out = self::present($m);
        $out['projects'] = array_map(static fn(array $r): array => [
            'id'               => (int) $r['id'],
            'slug'             => $r['slug'],
            'title'            => $r['title'],
            'status'           => $r['status'],
            'progress_percent' => (int) $r['progress_percent'],
            'role'             => $r['role'],
            'joined_at'        => $r['joined_at'],
            'category'         => [
                'slug'      => $r['category_slug'],
                'name'      => $r['category_name'],
                'color_hex' => $r['category_color_hex'],
            ],
        ], $projects);
        Response::json($out);
    }

    public function store(Request $req, array $params): void
    {
        $body = $req->json();
        $v = new Validator($body);
        $v->required('display_name')->string('display_name', 128);
        $v->string('slug', 96);
        $v->string('initials', 8);
        $v->in('kind', ['person', 'collective', 'org']);
        $v->string('bio', 5000);
        $v->url('avatar_url', 512);
        $v->url('website_url', 512);
        $v->email('contact_email');
        $v->check();

        $name = (string) $body['display_name'];
        $base = (isset($body['slug']) && $body['slug'] !== '')
            ? Slugger::slugify((string) $body['slug'])
            : Slugger::slugify($name);
        $slug = Slugger::unique($base, 'members');

        Database::insert('members', [
            'slug'          => $slug,
            'display_name'  => $name,
            'initials'      => $body['initials'] ?? self::autoInitials($name),
            'kind'          => $body['kind'] ?? 'person',
            'bio'           => $body['bio'] ?? null,
            'avatar_url'    => $body['avatar_url'] ?? null,
            'website_url'   => $body['website_url'] ?? null,
            'contact_email' => $body['contact_email'] ?? null,
        ]);
        Response::json(self::present(self::findOr404($slug)), 201);
    }

    public function update(Request $req, array $params): void
    {
        $m = self::findOr404($params['slug']);
        $body = $req->json();
        $v = new Validator($body);
        $v->string('display_name', 128);
        $v->string('slug', 96);
        $v->string('initials', 8);
        $v->in('kind', ['person', 'collective', 'org']);
        $v->string('bio', 5000);
        $v->url('avatar_url', 512);
        $v->url('website_url', 512);
        $v->email('contact_email');
        $v->check();

        $update = [];
        foreach (['display_name', 'initials', 'kind', 'bio', 'avatar_url', 'website_url', 'contact_email'] as $f) {
            if (array_key_exists($f, $body)) {
                $update[$f] = $body[$f];
            }
        }
        if (isset($body['slug']) && $body['slug'] !== '') {
            $update['slug'] = Slugger::unique(
                Slugger::slugify((string) $body['slug']),
                'members',
                'slug',
                (int) $m['id'],
            );
        }
        if ($update !== []) {
            Database::update('members', $update, 'id = :id', [':id' => (int) $m['id']]);
        }
        Response::json(self::present(self::findOr404($update['slug'] ?? $m['slug'])));
    }

    public function destroy(Request $req, array $params): void
    {
        $m = self::findOr404($params['slug']);
        // FK : project_members CASCADE, discussions/activity_log SET NULL -> hard delete OK.
        Database::delete('members', 'id = :id', [':id' => (int) $m['id']]);
        Response::noContent();
    }

    /** Helper public : utilisé par d'autres contrôleurs (ProjectMembers, etc.). */
    public static function findOr404(string $slug): array
    {
        $row = Database::fetchOne(self::SELECT . ' WHERE slug = :slug', [':slug' => $slug]);
        if ($row === null) {
            throw new \ApiException(404, ['error' => 'not_found']);
        }
        return $row;
    }

    /** Génère 1 à 3 initiales depuis un nom complet ("Jean Dupont" -> "JD"). */
    private static function autoInitials(string $name): string
    {
        $parts = preg_split('/\s+/', trim($name)) ?: [];
        $letters = '';
        foreach ($parts as $p) {
            if ($p === '') continue;
            $letters .= mb_strtoupper(mb_substr($p, 0, 1));
            if (mb_strlen($letters) >= 3) break;
        }
        return $letters !== '' ? mb_substr($letters, 0, 8) : '?';
    }

    /** @return array{0:int,1:int,2:int} [page, per_page, offset] */
    private static function pagination(Request $req): array
    {
        $page    = max(1, (int) $req->query('page', 1));
        $perPage = max(1, min(100, (int) $req->query('per_page', 20)));
        return [$page, $perPage, ($page - 1) * $perPage];
    }

    /** @return array<string,int> */
    private static function meta(int $page, int $perPage, int $total): array
    {
        return [
            'page'        => $page,
            'per_page'    => $perPage,
            'total'       => $total,
            'total_pages' => $total === 0 ? 0 : (int) ceil($total / $perPage),
        ];
    }

    private static function present(array $row): array
    {
        return [
            'id'            => (int) $row['id'],
            'slug'          => $row['slug'],
            'display_name'  => $row['display_name'],
            'initials'      => $row['initials'],
            'kind'          => $row['kind'],
            'bio'           => $row['bio'],
            'avatar_url'    => $row['avatar_url'],
            'website_url'   => $row['website_url'],
            'contact_email' => $row['contact_email'],
            'created_at'    => $row['created_at'],
            'updated_at'    => $row['updated_at'],
        ];
    }
}
