#!/usr/bin/php -q
<?php
## IrigaNetworks
## ~~~~~~~~~~~~~
## Script d'audit : liste tous les documents de locataires par catégorie
## Croise les données BDD et le système de fichiers pour détecter les anomalies
##

function usage()
{
	global $Script;

	echo "$Script
DESCRIPTION :
	Affiche tous les documents de locataires (isResident=1) organisés par catégorie.
	Croise les données de la BDD avec le système de fichiers.

COMMANDES :
	--liste		: (defaut) Afficher les documents par catégorie

PARAMETRES :
	[--idLocataire=<int>]	: Filtrer sur un locataire particulier
	[--idCategorie=<int>]	: Filtrer sur une catégorie particulière
	[--anomalies]		: Afficher uniquement les anomalies (fichier absent ou mal placé)
	[--fix]			: Déplacer les fichiers mal catégorisés dans le bon dossier
	[--csv]			: Sortie au format CSV (séparateur ;)
	[--debug=<int>]		: 0=pas de debug, 1=niveau1

phpVersion : ".phpversion()."\n";
	exit(1);
}

## ----------------------------------------------------------------------------------------------------
## Décodage des paramètres d'appel
## ----------------------------------------------------------------------------------------------------
$Script = basename($_SERVER['argv'][0]);

$params     = array();
$paramsTxt  = "";
$mode       = "liste";

for ($i = 1; $i < $_SERVER['argc']; $i++) {
	$paramsTxt .= " " . $_SERVER['argv'][$i];

	if (preg_match("@^--(liste)$@", $_SERVER['argv'][$i], $regs))              { $mode                     = $regs[1]; continue; }
	if (preg_match("@^--idLocataire=(.+)$@", $_SERVER['argv'][$i], $regs))     { $params['idLocataire']    = (int) $regs[1]; continue; }
	if (preg_match("@^--idCategorie=(.+)$@", $_SERVER['argv'][$i], $regs))     { $params['idCategorie']    = (int) $regs[1]; continue; }
	if (preg_match("@^--anomalies$@", $_SERVER['argv'][$i], $regs))            { $params['anomalies']      = true; continue; }
	if (preg_match("@^--fix$@", $_SERVER['argv'][$i], $regs))                  { $params['fix']            = true; continue; }
	if (preg_match("@^--csv$@", $_SERVER['argv'][$i], $regs))                  { $params['csv']            = true; continue; }
	if (preg_match("@^--debug=(.+)$@", $_SERVER['argv'][$i], $regs))           { $params['debug']          = (int) $regs[1]; continue; }

	echo "Paramètre inconnu : " . $_SERVER['argv'][$i] . "\n";
	usage();
}

$debug  = isset($params['debug']) ? $params['debug'] : 0;
$isCsv  = isset($params['csv']) && $params['csv'];

if ($debug) {
	echo "## $Script $paramsTxt\n";
}

## ----------------------------------------------------------------------------------------------------
## Lecture de la configuration BDD
## ----------------------------------------------------------------------------------------------------
$rootDir = realpath(__DIR__ . '/..');

$bddName   = "";
$bddHost   = "";
$bddUser   = "";
$bddPasswd = "";

if ($debug) {
	echo "## BDD : host=$bddHost dbname=$bddName user=$bddUser\n";
}

## ----------------------------------------------------------------------------------------------------
## Connexion BDD
## ----------------------------------------------------------------------------------------------------
try {
	$dsn = "mysql:host={$bddHost};dbname={$bddName};charset=utf8";
	$pdo = new PDO($dsn, $bddUser, $bddPasswd, array(
		PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
		PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
	));
} catch (PDOException $e) {
	echo "Erreur de connexion BDD : " . $e->getMessage() . "\n";
	exit(1);
}

## ----------------------------------------------------------------------------------------------------
## Mode : liste
## ----------------------------------------------------------------------------------------------------
if ($mode == "liste") {

	## Requête principale
	## ~~~~~~~~~~~~~~~~~~
	## On récupère tous les documents de locataires (isResident=1) avec :
	## - leur catégorie (via categorie_document)
	## - le locataire (via document_locataire → reservations → locataires, ou document_dp → dp)
	$sql = "
		SELECT
			d.id                       AS idDocument,
			d.nom                      AS nomFichier,
			d.libelle                  AS libelle,
			d.idCategorie              AS idCategorie,
			COALESCE(cd.nom, 'Sans catégorie')  AS nomCategorie,
			d.typeDoc                  AS typeDoc,
			d.dateCreation             AS dateCreation,
			d.deleted                  AS deleted,
			dl.idReservation           AS idReservation,
			r.idLocataire              AS idLocataire,
			CONCAT(l.prenom, ' ', l.nom) AS nomLocataire,
			NULL                       AS idDp,
			'resa'                     AS source
		FROM documents d
		LEFT JOIN categorie_document cd ON cd.id = d.idCategorie
		LEFT JOIN document_locataire dl ON dl.idDocument = d.id
		LEFT JOIN reservations r        ON r.id = dl.idReservation
		LEFT JOIN locataires l          ON l.id = r.idLocataire
		WHERE COALESCE(d.deleted, 0) = 0
		  AND dl.idReservation IS NOT NULL

		UNION ALL

		SELECT
			d.id                       AS idDocument,
			d.nom                      AS nomFichier,
			d.libelle                  AS libelle,
			d.idCategorie              AS idCategorie,
			COALESCE(cd.nom, 'Sans catégorie')  AS nomCategorie,
			d.typeDoc                  AS typeDoc,
			d.dateCreation             AS dateCreation,
			d.deleted                  AS deleted,
			NULL                       AS idReservation,
			ddp.idDp                   AS idLocataire,
			CONCAT(l.prenom, ' ', l.nom) AS nomLocataire,
			ddp.idDp                   AS idDp,
			'dp'                       AS source
		FROM documents d
		LEFT JOIN categorie_document cd ON cd.id = d.idCategorie
		LEFT JOIN document_dp ddp       ON ddp.idDocument = d.id
		LEFT JOIN domiciliations domi   ON domi.id = ddp.idDp
		LEFT JOIN locataires l          ON l.id = domi.idLocataire
		WHERE COALESCE(d.deleted, 0) = 0
		  AND ddp.idDp IS NOT NULL
	";

	$where  = array();
	$sqlParams = array();

	if (!empty($params['idLocataire'])) {
		## On filtre sur idLocataire après le UNION via sous-requête
		$sql = "SELECT * FROM ($sql) AS docs_union WHERE idLocataire = :idLocataire";
		$sqlParams[':idLocataire'] = $params['idLocataire'];
	}

	if (!empty($params['idCategorie'])) {
		if (!empty($params['idLocataire'])) {
			$sql .= " AND idCategorie = :idCategorie";
		} else {
			$sql = "SELECT * FROM ($sql) AS docs_union WHERE idCategorie = :idCategorie";
		}
		$sqlParams[':idCategorie'] = $params['idCategorie'];
	}

	$sql .= " ORDER BY nomCategorie ASC, idLocataire ASC, nomFichier ASC";

	if ($debug) {
		echo "## SQL : $sql\n";
	}

	$stmt = $pdo->prepare($sql);
	$stmt->execute($sqlParams);
	$rows = $stmt->fetchAll();

	$publicDir = $rootDir . '/public/docs/locataires';

	## ----------------------------------------------------------------------------------------------------
	## Vérification filesystem et construction du résultat
	## ----------------------------------------------------------------------------------------------------
	$resultats      = array(); // [nomCategorie][idLocataire] => array of docs
	$totalDocs      = 0;
	$totalAnomalies = 0;
	$totalFixes     = 0;
	$compteurEtat   = array(); // compteur par état

	foreach ($rows as $row) {
		$nomCategorie = $row['nomCategorie'];
		$idLocataire  = $row['idLocataire'];
		$idCategorie  = $row['idCategorie'];
		$nomFichier   = $row['nomFichier'];

		## Vérification fichier sur le filesystem
		$cheminAttendu  = null;
		$fichierPresent = false;
		$fichiersErrones = array(); // présents dans d'autres catégories

		if ($idLocataire && $nomFichier && $row['typeDoc'] == 'statique') {
			$uploadsDir    = $publicDir . '/' . $idLocataire . '/uploads';
			$cheminAttendu = $uploadsDir . '/' . $idCategorie . '/' . $nomFichier;
			$fichierPresent = is_file($cheminAttendu);

			if (!$fichierPresent && is_dir($uploadsDir)) {
				## Chercher le fichier dans les autres sous-dossiers
				$subDirs = scandir($uploadsDir);
				foreach ($subDirs as $subDir) {
					if ($subDir === '.' || $subDir === '..') {
						continue;
					}
					$cheminAlternatif = $uploadsDir . '/' . $subDir . '/' . $nomFichier;
					if (is_file($cheminAlternatif)) {
						$fichiersErrones[] = $subDir; // numéro de catégorie où le fichier a été trouvé
					}
				}
			}
		} elseif ($row['typeDoc'] != 'statique') {
			## Doc dynamique (généré) : pas de fichier physique attendu
			$fichierPresent = true; // on ne signale pas d'anomalie
		}

		$anomalie = '';
		$deplace = false;
		if ($row['typeDoc'] == 'statique') {
			if (!$nomFichier) {
				$anomalie = 'NOM_VIDE';
			} elseif (!$fichierPresent && empty($fichiersErrones)) {
				$anomalie = 'FICHIER_ABSENT';
				$totalAnomalies++;
			} elseif (!$fichierPresent && !empty($fichiersErrones)) {
				$anomalie = 'MAUVAISE_CATEGORIE:' . implode(',', $fichiersErrones);
				$totalAnomalies++;

				## --fix : déplacer le fichier dans le bon dossier de catégorie
				if (!empty($params['fix'])) {
					$ancienChemin = $uploadsDir . '/' . $fichiersErrones[0] . '/' . $nomFichier;
					$dossierCible = $uploadsDir . '/' . $idCategorie;
					if (!is_dir($dossierCible)) {
						## Récupérer les droits d'un dossier frère existant
						$permRef = 0755;
						$ownerRef = null;
						$groupRef = null;
						$subDirsRef = scandir($uploadsDir);
						foreach ($subDirsRef as $sd) {
							if ($sd === '.' || $sd === '..') continue;
							$sdPath = $uploadsDir . '/' . $sd;
							if (is_dir($sdPath)) {
								$permRef = fileperms($sdPath) & 0777;
								$ownerRef = fileowner($sdPath);
								$groupRef = filegroup($sdPath);
								break;
							}
						}
						$oldUmask = umask(0);
						mkdir($dossierCible, $permRef, true);
						umask($oldUmask);
						if ($ownerRef !== null) @chown($dossierCible, $ownerRef);
						if ($groupRef !== null) @chgrp($dossierCible, $groupRef);
						if (!is_dir($dossierCible)) {
							echo "## ERREUR mkdir : impossible de créer $dossierCible\n";
						}
					}
					$dest = $dossierCible . '/' . $nomFichier;
					if (@rename($ancienChemin, $dest)) {
						$deplace = true;
						$fichierPresent = true;
						$totalFixes++;
					} else {
						$err = error_get_last();
						$errMsg = $err ? $err['message'] : 'erreur inconnue';
						echo "## ERREUR rename : $ancienChemin -> $dest : $errMsg\n";
					}
				}
			}
		}

		## Compteur par état
		$etatDoc = $anomalie ? $anomalie : 'OK';
		if (strpos($etatDoc, 'MAUVAISE_CATEGORIE:') === 0) $etatDoc = 'MAUVAISE_CATEGORIE';
		if (!isset($compteurEtat[$etatDoc])) $compteurEtat[$etatDoc] = 0;
		$compteurEtat[$etatDoc]++;

		if (isset($params['anomalies']) && $params['anomalies'] && !$anomalie) {
			continue;
		}

		$resultats[$nomCategorie][] = array(
			'idDocument'    => $row['idDocument'],
			'idLocataire'   => $idLocataire,
			'nomLocataire'  => $row['nomLocataire'],
			'idReservation' => $row['idReservation'],
			'idDp'          => $row['idDp'],
			'source'        => $row['source'],
			'nomFichier'    => $nomFichier,
			'libelle'       => $row['libelle'],
			'typeDoc'       => $row['typeDoc'],
			'dateCreation'  => $row['dateCreation'],
			'cheminAttendu' => $cheminAttendu,
			'fichierPresent'=> $fichierPresent,
			'anomalie'      => $anomalie,
			'deplace'       => $deplace,
		);
		$totalDocs++;
	}

	## ----------------------------------------------------------------------------------------------------
	## Affichage
	## ----------------------------------------------------------------------------------------------------
	if ($isCsv) {
		## En-tête CSV
		echo implode(';', array(
			'categorie',
			'idDocument',
			'idLocataire',
			'nomLocataire',
			'idReservation',
			'idDp',
			'source',
			'nomFichier',
			'libelle',
			'typeDoc',
			'dateCreation',
			'fichierPresent',
			'anomalie',
		)) . "\n";

		foreach ($resultats as $nomCategorie => $docs) {
			foreach ($docs as $doc) {
				echo implode(';', array(
					csvEscape($nomCategorie),
					$doc['idDocument'],
					$doc['idLocataire'],
					csvEscape($doc['nomLocataire']),
					$doc['idReservation'],
					$doc['idDp'],
					$doc['source'],
					csvEscape($doc['nomFichier']),
					csvEscape($doc['libelle']),
					$doc['typeDoc'],
					$doc['dateCreation'],
					$doc['fichierPresent'] ? 'oui' : 'non',
					$doc['anomalie'],
				)) . "\n";
			}
		}
	} else {
		## Affichage texte
		echo "\n";
		echo "################################################################################\n";
		echo "## Documents de locataires par catégorie\n";
		if (!empty($params['idLocataire'])) {
			echo "## Filtre locataire : " . $params['idLocataire'] . "\n";
		}
		if (!empty($params['idCategorie'])) {
			echo "## Filtre catégorie : " . $params['idCategorie'] . "\n";
		}
		echo "################################################################################\n\n";

		foreach ($resultats as $nomCategorie => $docs) {
			$nbCat = count($docs);
			echo "┌─────────────────────────────────────────────────────────────────────────\n";
			echo "│ CATÉGORIE : $nomCategorie  ($nbCat document(s))\n";
			echo "├─────────────────────────────────────────────────────────────────────────\n";

			foreach ($docs as $doc) {
				$statutFichier = $doc['fichierPresent'] ? '[OK] ' : '[!!] ';
				$locInfo = "loc#" . $doc['idLocataire'] . " " . $doc['nomLocataire'];
				if ($doc['source'] == 'resa') {
					$locInfo .= " (resa#" . $doc['idReservation'] . ")";
				} else {
					$locInfo .= " (dp#" . $doc['idDp'] . ")";
				}

				echo "│  " . $statutFichier . "doc#" . $doc['idDocument'] . "  $locInfo\n";
				echo "│      Libellé    : " . $doc['libelle'] . "\n";
				echo "│      Fichier    : " . ($doc['nomFichier'] ?: '(aucun)') . "\n";
				echo "│      Type       : " . $doc['typeDoc'] . "\n";
				echo "│      Créé le    : " . $doc['dateCreation'] . "\n";
				if ($doc['anomalie']) {
					echo "│      ANOMALIE   : " . $doc['anomalie'] . "\n";
					if ($doc['cheminAttendu']) {
						echo "│      Chemin BDD : " . $doc['cheminAttendu'] . "\n";
					}
					if ($doc['deplace']) {
						echo "│      >>> CORRIGÉ : fichier déplacé dans le bon dossier\n";
					}
				}
				echo "│\n";
			}
			echo "└─────────────────────────────────────────────────────────────────────────\n\n";
		}

		echo "################################################################################\n";
		echo "## Total : $totalDocs document(s)";
		if ($totalAnomalies > 0) {
			echo "  |  Anomalies : $totalAnomalies";
		}
		if ($totalFixes > 0) {
			echo "  |  Corrigés : $totalFixes";
		}
		echo "\n";
		echo "##\n";
		echo "## Répartition par état :\n";
		ksort($compteurEtat);
		foreach ($compteurEtat as $etat => $nb) {
			echo "##   $etat : $nb\n";
		}
		echo "################################################################################\n";
	}

	exit(0);
}

## Sécurité de l'échappement CSV
function csvEscape($value)
{
	if (strpos($value, ';') !== false || strpos($value, '"') !== false || strpos($value, "\n") !== false) {
		return '"' . str_replace('"', '""', $value) . '"';
	}
	return $value;
}

usage();
