<?php
declare(strict_types=1);

namespace App\Meta;

use App\Core\Database;
use InvalidArgumentException;
use PDO;

final class MetaRepository
{
    private PDO $pdo;

    public function __construct()
    {
        $this->pdo = Database::connection();
    }

    public function entities(): array
    {
        return $this->pdo->query("SELECT * FROM axiom_meta_entities WHERE archived_at IS NULL ORDER BY label, code")->fetchAll() ?: [];
    }

    public function entity(int $id): ?array
    {
        $stmt = $this->pdo->prepare('SELECT * FROM axiom_meta_entities WHERE id = ? AND archived_at IS NULL');
        $stmt->execute([$id]);
        $row = $stmt->fetch();
        return $row ?: null;
    }

    public function fields(int $entityId): array
    {
        $stmt = $this->pdo->prepare('SELECT f.*, d.label AS dictionary_label FROM axiom_meta_fields f LEFT JOIN axiom_dictionary d ON d.code=f.dictionary_code WHERE f.entity_id=? AND f.archived_at IS NULL ORDER BY f.position, f.id');
        $stmt->execute([$entityId]);
        return $stmt->fetchAll() ?: [];
    }

    public function dictionary(): array
    {
        return $this->pdo->query('SELECT * FROM axiom_dictionary WHERE active=1 ORDER BY category, label')->fetchAll() ?: [];
    }

    public function saveEntity(array $input): int
    {
        $label = trim((string)($input['label'] ?? ''));
        $code = $this->code((string)($input['code'] ?? $label));
        $module = $this->code((string)($input['module_code'] ?? 'cda'));
        if ($label === '' || $code === '') {
            throw new InvalidArgumentException('Le nom et le code technique sont obligatoires.');
        }
        $stmt = $this->pdo->prepare('INSERT INTO axiom_meta_entities(code,label,module_code,description,table_name,icon,active) VALUES(?,?,?,?,?,?,1) ON DUPLICATE KEY UPDATE label=VALUES(label),module_code=VALUES(module_code),description=VALUES(description),table_name=VALUES(table_name),icon=VALUES(icon),archived_at=NULL,active=1,id=LAST_INSERT_ID(id)');
        $stmt->execute([$code,$label,$module,trim((string)($input['description'] ?? '')),$this->code((string)($input['table_name'] ?? $code), true),trim((string)($input['icon'] ?? '◇'))]);
        return (int)$this->pdo->lastInsertId();
    }

    public function saveField(array $input): int
    {
        $entityId = max(0, (int)($input['entity_id'] ?? 0));
        if (!$this->entity($entityId)) throw new InvalidArgumentException('Entité introuvable.');
        $label = trim((string)($input['label'] ?? ''));
        $code = $this->code((string)($input['code'] ?? $label));
        $type = (string)($input['field_type'] ?? 'string');
        $allowed = ['string','text','integer','decimal','money','boolean','date','datetime','email','phone','postal_code','city','relation','reference','file'];
        if ($label==='' || $code==='') throw new InvalidArgumentException('Le libellé et le code du champ sont obligatoires.');
        if (!in_array($type,$allowed,true)) throw new InvalidArgumentException('Type de champ non autorisé.');
        $position = max(1,(int)($input['position'] ?? 10));
        $stmt=$this->pdo->prepare('INSERT INTO axiom_meta_fields(entity_id,code,label,field_type,dictionary_code,required,searchable,listable,position,default_value,validation_json) VALUES(?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE label=VALUES(label),field_type=VALUES(field_type),dictionary_code=VALUES(dictionary_code),required=VALUES(required),searchable=VALUES(searchable),listable=VALUES(listable),position=VALUES(position),default_value=VALUES(default_value),validation_json=VALUES(validation_json),archived_at=NULL,id=LAST_INSERT_ID(id)');
        $validation = trim((string)($input['validation_json'] ?? ''));
        if ($validation !== '') {
            json_decode($validation, true);
            if (json_last_error() !== JSON_ERROR_NONE) throw new InvalidArgumentException('Les règles de validation JSON sont invalides.');
        }
        $stmt->execute([$entityId,$code,$label,$type,($input['dictionary_code'] ?? '') ?: null,isset($input['required'])?1:0,isset($input['searchable'])?1:0,isset($input['listable'])?1:0,$position,trim((string)($input['default_value'] ?? '')),$validation ?: null]);
        return (int)$this->pdo->lastInsertId();
    }

    public function archiveEntity(int $id): void
    {
        $stmt=$this->pdo->prepare('UPDATE axiom_meta_entities SET active=0, archived_at=NOW() WHERE id=?');
        $stmt->execute([$id]);
    }

    private function code(string $value, bool $allowPlural=false): string
    {
        $value = iconv('UTF-8','ASCII//TRANSLIT//IGNORE',$value) ?: $value;
        $value = strtolower(trim($value));
        $value = preg_replace('/[^a-z0-9_]+/','_',$value) ?? '';
        $value = trim($value,'_');
        return substr($value,0,120);
    }
}
