<?php
namespace App\Models;

use App\Core\Database;
use Throwable;

final class WorkshopV5
{
    public static function find(int $id): ?array
    {
        $stmt=Database::connection()->prepare(
            "SELECT *
             FROM v_ateliers_v5
             WHERE id=:id
             LIMIT 1"
        );
        $stmt->execute(['id'=>$id]);
        return $stmt->fetch() ?: null;
    }

    public static function publicIds(int $id): array
    {
        $stmt=Database::connection()->prepare(
            "SELECT public_cible_id
             FROM atelier_publics
             WHERE atelier_id=:id"
        );
        $stmt->execute(['id'=>$id]);

        return array_map(
            'intval',
            array_column(
                $stmt->fetchAll(),
                'public_cible_id'
            )
        );
    }

    public static function history(int $id): array
    {
        $stmt=Database::connection()->prepare(
            "SELECT h.*,u.nom AS user_nom,u.prenom AS user_prenom
             FROM atelier_historique h
             LEFT JOIN utilisateurs u ON u.id=h.utilisateur_id
             WHERE h.atelier_id=:id
             ORDER BY h.created_at DESC,h.id DESC"
        );
        $stmt->execute(['id'=>$id]);
        return $stmt->fetchAll();
    }

    public static function update(
        int $id,
        array $data,
        int $userId
    ): void {
        $pdo=Database::connection();
        $before=self::find($id);

        if(!$before){
            throw new \RuntimeException('Atelier introuvable.');
        }

        $pdo->beginTransaction();

        try{
            $stmt=$pdo->prepare(
                "UPDATE ateliers
                 SET
                    libelle=:libelle,
                    description=:description,
                    type_atelier=:type_atelier,
                    duree_standard_minutes=:duree,
                    capacite_max=:capacite,
                    version_numero=version_numero+1,
                    updated_at=CURRENT_TIMESTAMP
                 WHERE id=:id"
            );

            $stmt->execute([
                'libelle'=>$data['libelle'],
                'description'=>$data['description'] ?: null,
                'type_atelier'=>$data['type_atelier'],
                'duree'=>$data['duree_standard_minutes'] ?: null,
                'capacite'=>$data['capacite_max'] ?: null,
                'id'=>$id,
            ]);

            if(!empty($data['public_ids'])){
                $pdo->prepare(
                    "DELETE FROM atelier_publics
                     WHERE atelier_id=:id"
                )->execute(['id'=>$id]);

                $link=$pdo->prepare(
                    "INSERT INTO atelier_publics(
                        atelier_id,public_cible_id
                     ) VALUES(
                        :atelier_id,:public_id
                     )"
                );

                foreach($data['public_ids'] as $publicId){
                    $link->execute([
                        'atelier_id'=>$id,
                        'public_id'=>(int)$publicId,
                    ]);
                }
            }

            $after=self::find($id);

            self::recordDifferences(
                $id,
                $userId,
                $before,
                $after,
                $data['motif'] ?? null,
                $data['date_effet'] ?? null
            );

            $pdo->commit();
        }catch(Throwable $error){
            $pdo->rollBack();
            throw $error;
        }
    }

    public static function changeStatus(
        int $id,
        string $status,
        int $userId,
        ?string $reason=null
    ): void {
        $allowed=[
            'brouillon',
            'ouvert',
            'complet',
            'suspendu',
            'ferme',
            'archive'
        ];

        if(!in_array($status,$allowed,true)){
            throw new \InvalidArgumentException(
                'Statut atelier invalide.'
            );
        }

        $before=self::find($id);

        Database::connection()->prepare(
            "UPDATE ateliers
             SET statut=:statut,
                 updated_at=CURRENT_TIMESTAMP
             WHERE id=:id"
        )->execute([
            'statut'=>$status,
            'id'=>$id,
        ]);

        self::log(
            $id,
            $userId,
            'changement_statut',
            'statut',
            (string)($before['statut'] ?? ''),
            $status,
            $reason
        );
    }

    public static function close(
        int $id,
        string $effectiveDate,
        string $reason,
        int $userId
    ): void {
        $before=self::find($id);

        Database::connection()->prepare(
            "UPDATE ateliers
             SET statut='ferme',
                 ferme_at=CURRENT_TIMESTAMP,
                 fermeture_effective_le=:date_effective,
                 motif_fermeture=:motif,
                 updated_at=CURRENT_TIMESTAMP
             WHERE id=:id"
        )->execute([
            'date_effective'=>$effectiveDate,
            'motif'=>$reason,
            'id'=>$id,
        ]);

        self::log(
            $id,
            $userId,
            'fermeture',
            'statut',
            (string)($before['statut'] ?? ''),
            'ferme',
            $reason,
            $effectiveDate
        );
    }

    public static function archive(
        int $id,
        string $reason,
        int $userId
    ): void {
        $before=self::find($id);

        Database::connection()->prepare(
            "UPDATE ateliers
             SET statut='archive',
                 actif=0,
                 archive_at=CURRENT_TIMESTAMP,
                 archive_by=:user_id,
                 updated_at=CURRENT_TIMESTAMP
             WHERE id=:id"
        )->execute([
            'user_id'=>$userId,
            'id'=>$id,
        ]);

        self::log(
            $id,
            $userId,
            'archivage',
            'statut',
            (string)($before['statut'] ?? ''),
            'archive',
            $reason
        );
    }

    public static function restore(
        int $id,
        int $userId
    ): void {
        $before=self::find($id);

        Database::connection()->prepare(
            "UPDATE ateliers
             SET statut='brouillon',
                 actif=1,
                 archive_at=NULL,
                 archive_by=NULL,
                 updated_at=CURRENT_TIMESTAMP
             WHERE id=:id"
        )->execute(['id'=>$id]);

        self::log(
            $id,
            $userId,
            'restauration',
            'statut',
            (string)($before['statut'] ?? ''),
            'brouillon',
            'Restauration de l’atelier'
        );
    }

    private static function recordDifferences(
        int $id,
        int $userId,
        array $before,
        array $after,
        ?string $reason,
        ?string $effectiveDate
    ): void {
        $fields=[
            'libelle',
            'description',
            'type_atelier',
            'duree_standard_minutes',
            'capacite_max',
            'public_libelles'
        ];

        foreach($fields as $field){
            $old=(string)($before[$field] ?? '');
            $new=(string)($after[$field] ?? '');

            if($old===$new){
                continue;
            }

            self::log(
                $id,
                $userId,
                'modification',
                $field,
                $old,
                $new,
                $reason,
                $effectiveDate
            );
        }
    }

    private static function log(
        int $id,
        int $userId,
        string $action,
        ?string $field,
        ?string $oldValue,
        ?string $newValue,
        ?string $reason=null,
        ?string $effectiveDate=null
    ): void {
        $stmt=Database::connection()->prepare(
            "INSERT INTO atelier_historique(
                atelier_id,
                utilisateur_id,
                type_action,
                champ,
                ancienne_valeur,
                nouvelle_valeur,
                motif,
                date_effet
             ) VALUES(
                :atelier_id,
                :utilisateur_id,
                :type_action,
                :champ,
                :ancienne_valeur,
                :nouvelle_valeur,
                :motif,
                :date_effet
             )"
        );

        $stmt->execute([
            'atelier_id'=>$id,
            'utilisateur_id'=>$userId,
            'type_action'=>$action,
            'champ'=>$field,
            'ancienne_valeur'=>$oldValue,
            'nouvelle_valeur'=>$newValue,
            'motif'=>$reason,
            'date_effet'=>$effectiveDate ?: null,
        ]);
    }
}
