<?php
/**
 * Analyse de conflits d'un lot de réservations (import CSV & copie inter-subnet).
 * Extrait de subnet_import.php pour partage entre l'import CSV et la copie inter-subnet.
 */
class ReservationImportAnalyzer
{
    const STATUSES = ['clean','skip','ip_conflict','mac_conflict','invalid','pool','reserved'];

    /** Normalise un MAC en minuscules séparés par ':'. */
    public static function normalizeMac(string $raw): string {
        $hex = strtolower(preg_replace('/[^0-9a-fA-F]/', '', $raw));
        return implode(':', str_split($hex, 2));
    }

    /** Vérifie qu'une IP est dans le pool dynamique d'un subnet. */
    public static function inPool(string $ip, array $subnet): bool {
        if (!$subnet['has_dynamic_pool'] || !$subnet['range_start'] || !$subnet['range_end']) {
            return false;
        }
        $n = ip2long($ip);
        return $n >= ip2long($subnet['range_start']) && $n <= ip2long($subnet['range_end']);
    }

    /**
     * Analyse les conflits d'un tableau de lignes CSV brutes.
     *
     * @param array      $rawRows       Lignes issues de parseCsv()
     * @param array|null $defaultSubnet Subnet par défaut (null si import global)
     * @param array      $subnetsByName Tous les subnets indexés par name (lowercase)
     * @param bool       $hasSubnetCol  Le CSV contient-il une colonne subnet ?
     */
    public static function analyzeRows(array $rawRows, ?array $defaultSubnet, array $subnetsByName, bool $hasSubnetCol): array {
        // Cache des réservations existantes par kea_subnet_id
        $existingCache = [];
        $getExisting   = function(int $keaId) use (&$existingCache): array {
            if (!isset($existingCache[$keaId])) {
                $rows = Reservation::getBySubnet($keaId);
                $byIp  = [];
                $byMac = [];
                foreach ($rows as $r) {
                    $byIp[$r['ip_address']] = $r;
                    $byMac[$r['mac']]       = $r;
                }
                $existingCache[$keaId] = ['byIp' => $byIp, 'byMac' => $byMac];
            }
            return $existingCache[$keaId];
        };

        $analyzed = [];

        foreach ($rawRows as $r) {
            $macRaw        = $r['mac_raw'];
            $ip            = $r['ip'];
            $host          = $r['hostname'];
            $desc          = $r['description'];
            $lineNum       = $r['line'];
            $csvSubnetName = $r['subnet_name'] ?? '';
            $gateway       = $r['gateway']    ?? '';
            $routesRaw     = $r['routes_raw'] ?? '';
            $shortLease    = (int)($r['short_lease'] ?? 0);
            $noGateway     = (int)($r['no_gateway'] ?? 0);
            $group     = trim($r['group_name'] ?? '');
            // no_gateway l'emporte : on ignore une gateway éventuellement renseignée
            if ($noGateway) $gateway = '';

            // ── Résolution du subnet ───────────────────────────────────────────────
            $subnet = null;
            if ($hasSubnetCol) {
                if ($csvSubnetName === '') {
                    $analyzed[] = ['line'=>$lineNum,'mac'=>$macRaw,'ip'=>$ip,'hostname'=>$host,'description'=>$desc,
                        'gateway'=>$gateway,'routes_raw'=>$routesRaw,'short_lease'=>$shortLease,'no_gateway'=>$noGateway,'group_name'=>$group,
                        'status'=>'invalid','note'=>'Colonne subnet vide',
                        'subnet_id'=>null,'subnet_name'=>'','kea_subnet_id'=>null,
                        'mac_host_id'=>null,'ip_taken_by'=>null,'mac_existing_ip'=>null];
                    continue;
                }
                $key = strtolower($csvSubnetName);
                if (!isset($subnetsByName[$key])) {
                    $analyzed[] = ['line'=>$lineNum,'mac'=>$macRaw,'ip'=>$ip,'hostname'=>$host,'description'=>$desc,
                        'gateway'=>$gateway,'routes_raw'=>$routesRaw,'short_lease'=>$shortLease,'no_gateway'=>$noGateway,'group_name'=>$group,
                        'status'=>'invalid','note'=>'Subnet inconnu : « ' . $csvSubnetName . ' »',
                        'subnet_id'=>null,'subnet_name'=>$csvSubnetName,'kea_subnet_id'=>null,
                        'mac_host_id'=>null,'ip_taken_by'=>null,'mac_existing_ip'=>null];
                    continue;
                }
                $subnet = $subnetsByName[$key];
            } elseif ($defaultSubnet) {
                $subnet = $defaultSubnet;
            } else {
                $analyzed[] = ['line'=>$lineNum,'mac'=>$macRaw,'ip'=>$ip,'hostname'=>$host,'description'=>$desc,
                    'gateway'=>$gateway,'routes_raw'=>$routesRaw,'short_lease'=>$shortLease,'no_gateway'=>$noGateway,'group_name'=>$group,
                    'status'=>'invalid','note'=>'Subnet non déterminé (colonne subnet absente du CSV)',
                    'subnet_id'=>null,'subnet_name'=>'','kea_subnet_id'=>null,
                    'mac_host_id'=>null,'ip_taken_by'=>null,'mac_existing_ip'=>null];
                continue;
            }

            $keaSubnetId  = (int)$subnet['kea_subnet_id'];
            $subnetAppId  = (int)$subnet['id'];
            $subnetLabel  = $subnet['name'];

            // ── Normalisation MAC ──────────────────────────────────────────────────
            $mac = '';
            if (!empty($macRaw)) {
                $hexOnly = preg_replace('/[^0-9a-fA-F]/', '', $macRaw);
                if (strlen($hexOnly) === 12) {
                    $mac = self::normalizeMac($macRaw);
                }
            }

            // ── Validation format ──────────────────────────────────────────────────
            if ($mac === '' || !isValidMac($mac)) {
                $analyzed[] = ['line'=>$lineNum,'mac'=>$macRaw,'ip'=>$ip,'hostname'=>$host,'description'=>$desc,
                    'gateway'=>$gateway,'routes_raw'=>$routesRaw,'short_lease'=>$shortLease,'no_gateway'=>$noGateway,'group_name'=>$group,
                    'status'=>'invalid','note'=>'Format MAC invalide',
                    'subnet_id'=>$subnetAppId,'subnet_name'=>$subnetLabel,'kea_subnet_id'=>$keaSubnetId,
                    'mac_host_id'=>null,'ip_taken_by'=>null,'mac_existing_ip'=>null];
                continue;
            }
            // IP vide autorisée si short_lease = 1 ET le subnet a un pool short-lease configuré
            $hasShortLeasePool = !empty($subnet['short_lease_pool_start']);
            if ($ip === '' && $shortLease && $hasShortLeasePool) {
                // IP dynamique — pas de validation de format ni de conflit IP
            } elseif (!isValidIp($ip)) {
                $analyzed[] = ['line'=>$lineNum,'mac'=>$mac,'ip'=>$ip,'hostname'=>$host,'description'=>$desc,
                    'gateway'=>$gateway,'routes_raw'=>$routesRaw,'short_lease'=>$shortLease,'no_gateway'=>$noGateway,'group_name'=>$group,
                    'status'=>'invalid','note'=>'Format IP invalide',
                    'subnet_id'=>$subnetAppId,'subnet_name'=>$subnetLabel,'kea_subnet_id'=>$keaSubnetId,
                    'mac_host_id'=>null,'ip_taken_by'=>null,'mac_existing_ip'=>null];
                continue;
            }
            if (empty($host)) {
                $analyzed[] = ['line'=>$lineNum,'mac'=>$mac,'ip'=>$ip,'hostname'=>$host,'description'=>$desc,
                    'gateway'=>$gateway,'routes_raw'=>$routesRaw,'short_lease'=>$shortLease,'no_gateway'=>$noGateway,'group_name'=>$group,
                    'status'=>'invalid','note'=>'Hostname obligatoire',
                    'subnet_id'=>$subnetAppId,'subnet_name'=>$subnetLabel,'kea_subnet_id'=>$keaSubnetId,
                    'mac_host_id'=>null,'ip_taken_by'=>null,'mac_existing_ip'=>null];
                continue;
            }
            if (!preg_match('/^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?$/', $host)) {
                $analyzed[] = ['line'=>$lineNum,'mac'=>$mac,'ip'=>$ip,'hostname'=>$host,'description'=>$desc,
                    'gateway'=>$gateway,'routes_raw'=>$routesRaw,'short_lease'=>$shortLease,'no_gateway'=>$noGateway,'group_name'=>$group,
                    'status'=>'invalid','note'=>'Hostname invalide (lettres, chiffres, tirets)',
                    'subnet_id'=>$subnetAppId,'subnet_name'=>$subnetLabel,'kea_subnet_id'=>$keaSubnetId,
                    'mac_host_id'=>null,'ip_taken_by'=>null,'mac_existing_ip'=>null];
                continue;
            }
            // Vérifications CIDR et pool uniquement si une IP est fournie
            if ($ip !== '') {
            if (!ipInCidr($ip, $subnet['cidr'])) {
                $analyzed[] = ['line'=>$lineNum,'mac'=>$mac,'ip'=>$ip,'hostname'=>$host,'description'=>$desc,
                    'gateway'=>$gateway,'routes_raw'=>$routesRaw,'short_lease'=>$shortLease,'no_gateway'=>$noGateway,'group_name'=>$group,
                    'status'=>'invalid','note'=>'IP hors subnet ' . $subnet['cidr'],
                    'subnet_id'=>$subnetAppId,'subnet_name'=>$subnetLabel,'kea_subnet_id'=>$keaSubnetId,
                    'mac_host_id'=>null,'ip_taken_by'=>null,'mac_existing_ip'=>null];
                continue;
            }
            if (self::inPool($ip, $subnet)) {
                $analyzed[] = ['line'=>$lineNum,'mac'=>$mac,'ip'=>$ip,'hostname'=>$host,'description'=>$desc,
                    'gateway'=>$gateway,'routes_raw'=>$routesRaw,'short_lease'=>$shortLease,'no_gateway'=>$noGateway,'group_name'=>$group,
                    'status'=>'pool','note'=>'IP dans le pool dynamique (' . $subnet['range_start'] . '–' . $subnet['range_end'] . ')',
                    'subnet_id'=>$subnetAppId,'subnet_name'=>$subnetLabel,'kea_subnet_id'=>$keaSubnetId,
                    'mac_host_id'=>null,'ip_taken_by'=>null,'mac_existing_ip'=>null];
                continue;
            }
            } // fin vérifications IP non vide

            // ── Analyse conflits ───────────────────────────────────────────────────
            $existing = $getExisting($keaSubnetId);
            $macEntry = $existing['byMac'][$mac] ?? null;
            $ipEntry  = ($ip !== '') ? ($existing['byIp'][$ip] ?? null) : null;

            if ($macEntry && $ipEntry && $macEntry['host_id'] === $ipEntry['host_id']) {
                $analyzed[] = ['line'=>$lineNum,'mac'=>$mac,'ip'=>$ip,'hostname'=>$host,'description'=>$desc,
                    'gateway'=>$gateway,'routes_raw'=>$routesRaw,'short_lease'=>$shortLease,'no_gateway'=>$noGateway,'group_name'=>$group,
                    'status'=>'skip','note'=>'Déjà existant (identique)',
                    'subnet_id'=>$subnetAppId,'subnet_name'=>$subnetLabel,'kea_subnet_id'=>$keaSubnetId,
                    'mac_host_id'=>null,'ip_taken_by'=>null,'mac_existing_ip'=>null];

            } elseif ($ipEntry) {
                $analyzed[] = ['line'=>$lineNum,'mac'=>$mac,'ip'=>$ip,'hostname'=>$host,'description'=>$desc,
                    'gateway'=>$gateway,'routes_raw'=>$routesRaw,'short_lease'=>$shortLease,'no_gateway'=>$noGateway,'group_name'=>$group,
                    'status'=>'ip_conflict','note'=>'IP déjà utilisée par ' . $ipEntry['mac'],
                    'subnet_id'=>$subnetAppId,'subnet_name'=>$subnetLabel,'kea_subnet_id'=>$keaSubnetId,
                    'ip_taken_by'    => $ipEntry['mac'],
                    'mac_host_id'    => $macEntry ? (int)$macEntry['host_id'] : null,
                    'mac_existing_ip'=> $macEntry ? $macEntry['ip_address'] : null];

            } elseif ($macEntry) {
                $analyzed[] = ['line'=>$lineNum,'mac'=>$mac,'ip'=>$ip,'hostname'=>$host,'description'=>$desc,
                    'gateway'=>$gateway,'routes_raw'=>$routesRaw,'short_lease'=>$shortLease,'no_gateway'=>$noGateway,'group_name'=>$group,
                    'status'=>'mac_conflict','note'=>'MAC déjà réservée avec l\'IP ' . $macEntry['ip_address'],
                    'subnet_id'=>$subnetAppId,'subnet_name'=>$subnetLabel,'kea_subnet_id'=>$keaSubnetId,
                    'ip_taken_by'    => null,
                    'mac_host_id'    => (int)$macEntry['host_id'],
                    'mac_existing_ip'=> $macEntry['ip_address']];

            } else {
                $analyzed[] = ['line'=>$lineNum,'mac'=>$mac,'ip'=>$ip,'hostname'=>$host,'description'=>$desc,
                    'gateway'=>$gateway,'routes_raw'=>$routesRaw,'short_lease'=>$shortLease,'no_gateway'=>$noGateway,'group_name'=>$group,
                    'status'=>'clean','note'=>'Nouveau',
                    'subnet_id'=>$subnetAppId,'subnet_name'=>$subnetLabel,'kea_subnet_id'=>$keaSubnetId,
                    'mac_host_id'=>null,'ip_taken_by'=>null,'mac_existing_ip'=>null];
            }
        }
        return $analyzed;
    }
}
