<?php
namespace App\Controllers;

use App\Core\Auth;
use App\Core\Controller;
use App\Core\Database;
use PDO;
use RuntimeException;

final class AccountAdminController extends Controller
{
    private PDO $pdo;

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

    public function index(): void
    {
        Auth::requireLogin();
        $this->requireAdmin();

        $users=$this->pdo->query(
            "SELECT
                u.id,u.nom,u.prenom,u.email,u.statut,u.actif,
                u.derniere_connexion_at,
                GROUP_CONCAT(r.libelle ORDER BY r.libelle SEPARATOR ', ') AS roles
             FROM utilisateurs u
             LEFT JOIN utilisateur_roles ur ON ur.utilisateur_id=u.id
             LEFT JOIN roles r
                    ON r.id=ur.role_id
                   AND r.deleted_at IS NULL
             WHERE u.deleted_at IS NULL
             GROUP BY u.id
             ORDER BY u.nom,u.prenom"
        )->fetchAll(PDO::FETCH_ASSOC);

        $this->view('accounts/index',[
            'pageTitle'=>'Comptes utilisateurs',
            'users'=>$users
        ]);
    }

    public function create(): void
    {
        Auth::requireLogin();
        $this->requireAdmin();

        $this->view('accounts/form',[
            'pageTitle'=>'Créer un compte',
            'account'=>null,
            'roles'=>$this->roles(),
            'selectedRoles'=>[],
            'communicationPermissions'=>$this->communicationPermissions(),
            'selectedCommunicationPermissions'=>[]
        ]);
    }

    public function store(): void
    {
        Auth::requireLogin();
        $this->requireAdmin();
        $this->verifyCsrf();

        $nom=mb_strtoupper(trim((string)($_POST['nom'] ?? '')));
        $prenom=$this->normalizeName((string)($_POST['prenom'] ?? ''));
        $email=mb_strtolower(trim((string)($_POST['email'] ?? '')));
        $password=(string)($_POST['mot_de_passe'] ?? '');
        $roles=array_map('intval',(array)($_POST['roles'] ?? []));
        $communicationPermissions=array_map('intval',(array)($_POST['communication_permissions'] ?? []));

        if($nom==='' || $prenom==='' || !filter_var($email,FILTER_VALIDATE_EMAIL)){
            throw new RuntimeException('Nom, prénom et adresse e-mail valides obligatoires.');
        }

        if(mb_strlen($password)<10){
            throw new RuntimeException('Le mot de passe doit contenir au moins 10 caractères.');
        }

        $organisationId=$this->currentOrganisationId();

        $this->pdo->beginTransaction();

        try{
            $stmt=$this->pdo->prepare(
                "INSERT INTO utilisateurs(
                    organisation_id,nom,prenom,email,mot_de_passe,
                    statut,actif,created_at,updated_at
                 ) VALUES(
                    :organisation_id,:nom,:prenom,:email,:mot_de_passe,
                    'actif',1,NOW(),NOW()
                 )"
            );
            $stmt->execute([
                'organisation_id'=>$organisationId,
                'nom'=>$nom,
                'prenom'=>$prenom,
                'email'=>$email,
                'mot_de_passe'=>password_hash($password,PASSWORD_DEFAULT)
            ]);

            $userId=(int)$this->pdo->lastInsertId();
            $this->saveRoles($userId,$roles);
            $this->saveCommunicationPermissions($userId,$communicationPermissions);

            $this->pdo->commit();
        }catch(\Throwable $e){
            if($this->pdo->inTransaction()){
                $this->pdo->rollBack();
            }
            throw $e;
        }

        header('Location: /public/administration/comptes');
        exit;
    }

    public function edit(): void
    {
        Auth::requireLogin();
        $this->requireAdmin();

        $id=(int)($_GET['id'] ?? 0);
        $stmt=$this->pdo->prepare(
            "SELECT id,nom,prenom,email,statut,actif
             FROM utilisateurs
             WHERE id=:id AND deleted_at IS NULL"
        );
        $stmt->execute(['id'=>$id]);
        $account=$stmt->fetch(PDO::FETCH_ASSOC);
        $selectedCommunicationPermissions=$this->selectedCommunicationPermissions($id);

        if(!$account){
            http_response_code(404);
            exit('Compte introuvable.');
        }

        $stmt=$this->pdo->prepare(
            "SELECT role_id FROM utilisateur_roles WHERE utilisateur_id=:id"
        );
        $stmt->execute(['id'=>$id]);

        $this->view('accounts/form',[
            'pageTitle'=>'Modifier un compte',
            'account'=>$account,
            'roles'=>$this->roles(),
            'selectedRoles'=>array_map('intval',$stmt->fetchAll(PDO::FETCH_COLUMN)),
            'communicationPermissions'=>$this->communicationPermissions(),
            'selectedCommunicationPermissions'=>$selectedCommunicationPermissions
        ]);
    }

    public function update(): void
    {
        Auth::requireLogin();
        $this->requireAdmin();
        $this->verifyCsrf();

        $id=(int)($_POST['id'] ?? 0);
        $nom=mb_strtoupper(trim((string)($_POST['nom'] ?? '')));
        $prenom=$this->normalizeName((string)($_POST['prenom'] ?? ''));
        $email=mb_strtolower(trim((string)($_POST['email'] ?? '')));
        $password=(string)($_POST['mot_de_passe'] ?? '');
        $roles=array_map('intval',(array)($_POST['roles'] ?? []));
        $communicationPermissions=array_map('intval',(array)($_POST['communication_permissions'] ?? []));

        if($id<=0 || $nom==='' || $prenom==='' || !filter_var($email,FILTER_VALIDATE_EMAIL)){
            throw new RuntimeException('Données de compte invalides.');
        }

        $this->pdo->beginTransaction();

        try{
            $sql="UPDATE utilisateurs
                  SET nom=:nom,prenom=:prenom,email=:email,updated_at=NOW()";
            $params=['id'=>$id,'nom'=>$nom,'prenom'=>$prenom,'email'=>$email];

            if($password!==''){
                if(mb_strlen($password)<10){
                    throw new RuntimeException(
                        'Le nouveau mot de passe doit contenir au moins 10 caractères.'
                    );
                }
                $sql.=",mot_de_passe=:mot_de_passe";
                $params['mot_de_passe']=password_hash($password,PASSWORD_DEFAULT);
            }

            $sql.=" WHERE id=:id AND deleted_at IS NULL";
            $stmt=$this->pdo->prepare($sql);
            $stmt->execute($params);

            $this->saveRoles($id,$roles);
            $this->saveCommunicationPermissions($id,$communicationPermissions);
            $this->pdo->commit();
        }catch(\Throwable $e){
            if($this->pdo->inTransaction()){
                $this->pdo->rollBack();
            }
            throw $e;
        }

        header('Location: /public/administration/comptes');
        exit;
    }

    public function toggle(): void
    {
        Auth::requireLogin();
        $this->requireAdmin();
        $this->verifyCsrf();

        $id=(int)($_POST['id'] ?? 0);
        $current=$this->currentUserId();

        if($id<=0 || $id===$current){
            throw new RuntimeException('Ce compte ne peut pas être désactivé ici.');
        }

        $stmt=$this->pdo->prepare(
            "UPDATE utilisateurs
             SET
                actif=IF(actif=1,0,1),
                statut=IF(actif=1,'inactif','actif'),
                updated_at=NOW()
             WHERE id=:id AND deleted_at IS NULL"
        );
        $stmt->execute(['id'=>$id]);

        header('Location: /public/administration/comptes');
        exit;
    }

    private function roles(): array
    {
        return $this->pdo->query(
            "SELECT id,code,libelle
             FROM roles
             WHERE actif=1 AND deleted_at IS NULL
             ORDER BY libelle"
        )->fetchAll(PDO::FETCH_ASSOC);
    }

    private function saveRoles(int $userId,array $roles): void
    {
        $stmt=$this->pdo->prepare(
            "DELETE FROM utilisateur_roles WHERE utilisateur_id=:user_id"
        );
        $stmt->execute(['user_id'=>$userId]);

        if(!$roles){
            return;
        }

        $insert=$this->pdo->prepare(
            "INSERT INTO utilisateur_roles(utilisateur_id,role_id)
             SELECT :user_id,id
             FROM roles
             WHERE id=:role_id AND actif=1 AND deleted_at IS NULL"
        );

        foreach(array_unique($roles) as $roleId){
            $insert->execute([
                'user_id'=>$userId,
                'role_id'=>$roleId
            ]);
        }
    }

    private function communicationPermissions(): array
    {
        return $this->pdo->query(
            "SELECT id,code,libelle FROM permissions
             WHERE actif=1 AND code IN(
                'communications.view','communications.create',
                'communications.validate','communications.send.group',
                'communications.directory.all','communications.directory.own',
                'communications.recipients.search','communications.templates.edit'
             ) ORDER BY libelle"
        )->fetchAll(PDO::FETCH_ASSOC);
    }

    private function selectedCommunicationPermissions(int $userId): array
    {
        $q=$this->pdo->prepare(
            "SELECT up.permission_id FROM utilisateur_permissions up
             JOIN permissions p ON p.id=up.permission_id
             WHERE up.utilisateur_id=? AND up.accordee=1 AND p.module='communications'"
        );$q->execute([$userId]);return array_map('intval',$q->fetchAll(PDO::FETCH_COLUMN));
    }

    private function saveCommunicationPermissions(int $userId,array $permissionIds): void
    {
        $delete=$this->pdo->prepare(
            "DELETE up FROM utilisateur_permissions up JOIN permissions p ON p.id=up.permission_id
             WHERE up.utilisateur_id=? AND p.module='communications'"
        );$delete->execute([$userId]);
        if(!$permissionIds)return;
        $insert=$this->pdo->prepare(
            "INSERT INTO utilisateur_permissions(utilisateur_id,permission_id,accordee)
             SELECT ?,id,1 FROM permissions WHERE id=? AND module='communications' AND actif=1"
        );foreach(array_unique($permissionIds) as $permissionId)$insert->execute([$userId,$permissionId]);
    }

    private function requireAdmin(): void
    {
        $userId=$this->currentUserId();
        $stmt=$this->pdo->prepare(
            "SELECT COUNT(*)
             FROM utilisateur_roles ur
             INNER JOIN roles r ON r.id=ur.role_id
             WHERE ur.utilisateur_id=:user_id
               AND r.code='super_admin'
               AND r.actif=1
               AND r.deleted_at IS NULL"
        );
        $stmt->execute(['user_id'=>$userId]);

        if((int)$stmt->fetchColumn()===0){
            http_response_code(403);
            exit('Accès refusé.');
        }
    }

    private function currentUserId(): int
    {
        foreach([
            $_SESSION['utilisateur_id'] ?? null,
            $_SESSION['user_id'] ?? null,
            $_SESSION['utilisateur']['id'] ?? null,
            $_SESSION['user']['id'] ?? null
        ] as $id){
            if(is_numeric($id) && (int)$id>0){
                return (int)$id;
            }
        }
        return 0;
    }

    private function currentOrganisationId(): int
    {
        $id=$this->currentUserId();
        $stmt=$this->pdo->prepare(
            "SELECT organisation_id FROM utilisateurs WHERE id=:id"
        );
        $stmt->execute(['id'=>$id]);
        return max(1,(int)$stmt->fetchColumn());
    }

    private function normalizeName(string $name): string
    {
        $name=mb_strtolower(trim($name));
        return mb_convert_case($name,MB_CASE_TITLE,'UTF-8');
    }

    private function verifyCsrf(): void
    {
        $token=(string)($_POST['_csrf'] ?? '');
        $session=(string)($_SESSION['_csrf'] ?? '');

        if($session!=='' && !hash_equals($session,$token)){
            throw new RuntimeException('Jeton de sécurité invalide.');
        }
    }
}
