<?php
/**
 * =============================================================================
 *  Import storico segnalazioni NIS2  →  AgileHub/Nexus
 * =============================================================================
 *
 *  Migra le righe di `nis2_agile_db.feedback_reports` (e relative
 *  attachments inline base64) verso `nexus_ticket_db.tickets` /
 *  `ticket_attachments`, preservando created_at originale e idempotente
 *  rispetto a re-run grazie a `tickets.external_ref`.
 *
 *  Mapping:
 *    feedback_reports.id          →  tickets.external_ref ('nis2:<id>')
 *    feedback_reports.tipo        →  tickets.kind   (vedi $KIND_MAP)
 *    feedback_reports.priorita    →  tickets.priority (alta→HIGH, media→MEDIUM, bassa→LOW)
 *    feedback_reports.status      →  tickets.status   (vedi $STATUS_MAP)
 *    feedback_reports.descrizione →  tickets.operative_summary (+ subject = first 200)
 *    feedback_reports.user_email  →  tickets.caller_email
 *    feedback_reports.user_role   →  tickets.reporter_role
 *    feedback_reports.page_url    →  tickets.page_url
 *    feedback_reports.ai_*        →  tickets.ai_*
 *    feedback_reports.attachment  →  ticket_attachments.data (base64, LONGTEXT)
 *    + sempre: requested_product='NIS2', requested_product_label='NIS2 Agile',
 *              channel='FORM', tenant_id=$NEXUS_TENANT_ID
 *
 *  Tabella `feedback_audit_log` (se presente) → ticket_messages role='SYSTEM'
 *  Tabella `notifications`: NON migrata (decisione esplicita per non flooddare
 *  la bell di AgileHub con notifiche storiche già lette).
 *
 *  Uso:
 *    php import-feedback-to-nexus.php             # dry-run di default
 *    php import-feedback-to-nexus.php --commit    # esecuzione vera
 *    php import-feedback-to-nexus.php --rollback  # cancella tutto ciò che è stato importato (filtro external_ref LIKE 'nis2:%')
 *    php import-feedback-to-nexus.php --limit=10  # solo prime 10 righe (utile per smoke test)
 *
 *  Variabili d'ambiente:
 *    SRC_DB_HOST   default 172.25.0.1
 *    SRC_DB_USER   default nis2_user
 *    SRC_DB_PASS   default Nis2Dev2026!
 *    SRC_DB_NAME   default nis2_agile_db
 *
 *    NEXUS_DB_HOST default 172.25.0.1
 *    NEXUS_DB_USER default agile_services
 *    NEXUS_DB_PASS *required*
 *    NEXUS_DB_NAME default nexus_ticket_db
 *
 *    NEXUS_TENANT_ID *required*  (id numerico del tenant 'alltax' in agile_auth_db.tenants)
 * =============================================================================
 */

declare(strict_types=1);

// ----------------------------- CLI ARGS -----------------------------------
$args = array_slice($argv, 1);
$dryRun   = !in_array('--commit',   $args, true);
$rollback =  in_array('--rollback', $args, true);
$limit    = null;
foreach ($args as $a) {
    if (preg_match('/^--limit=(\d+)$/', $a, $m)) { $limit = (int)$m[1]; }
}

// ----------------------------- ENV ----------------------------------------
$SRC = [
    'host' => getenv('SRC_DB_HOST')   ?: '172.25.0.1',
    'user' => getenv('SRC_DB_USER')   ?: 'nis2_user',
    'pass' => getenv('SRC_DB_PASS')   ?: 'Nis2Dev2026!',
    'db'   => getenv('SRC_DB_NAME')   ?: 'nis2_agile_db',
];
$DST = [
    'host' => getenv('NEXUS_DB_HOST') ?: '172.25.0.1',
    'user' => getenv('NEXUS_DB_USER') ?: 'agile_services',
    'pass' => getenv('NEXUS_DB_PASS') ?: '',
    'db'   => getenv('NEXUS_DB_NAME') ?: 'nexus_ticket_db',
];
$NEXUS_TENANT_ID = (int) (getenv('NEXUS_TENANT_ID') ?: 0);

if (!$rollback && $NEXUS_TENANT_ID <= 0) {
    fwrite(STDERR, "ERROR: env NEXUS_TENANT_ID mancante. Esegui prima seed-nexus-tenant.sql e annota l'id.\n");
    exit(2);
}
if (!$rollback && empty($DST['pass'])) {
    fwrite(STDERR, "ERROR: env NEXUS_DB_PASS mancante.\n");
    exit(2);
}

// ----------------------------- LOG ----------------------------------------
$logDir  = __DIR__ . '/../tmp';
if (!is_dir($logDir)) { mkdir($logDir, 0775, true); }
$logPath = $logDir . '/import-feedback-' . date('Ymd-His') . '.log';
$logFp   = fopen($logPath, 'w');
function logLine(string $line): void {
    global $logFp;
    $stamp = date('H:i:s');
    fwrite($logFp, "[$stamp] $line\n");
    echo "[$stamp] $line\n";
}

logLine('=== Import feedback NIS2 → Nexus ===');
logLine('Mode:       ' . ($rollback ? 'ROLLBACK' : ($dryRun ? 'DRY-RUN' : 'COMMIT')));
logLine('Tenant id:  ' . $NEXUS_TENANT_ID);
logLine('Limit:      ' . ($limit ?? 'none'));
logLine('Source:     ' . "{$SRC['user']}@{$SRC['host']}/{$SRC['db']}");
logLine('Target:     ' . "{$DST['user']}@{$DST['host']}/{$DST['db']}");

// ----------------------------- PDO ----------------------------------------
$pdoOpts = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
    $src = new PDO("mysql:host={$SRC['host']};dbname={$SRC['db']};charset=utf8mb4",
                   $SRC['user'], $SRC['pass'], $pdoOpts);
    $dst = new PDO("mysql:host={$DST['host']};dbname={$DST['db']};charset=utf8mb4",
                   $DST['user'], $DST['pass'], $pdoOpts);
} catch (PDOException $e) {
    logLine('FATAL DB connect: ' . $e->getMessage());
    exit(3);
}

// ----------------------------- ROLLBACK -----------------------------------
if ($rollback) {
    logLine('Rollback: cancello tutti i ticket con external_ref LIKE "nis2:%"');
    if ($dryRun) {
        $n = (int) $dst->query(
            "SELECT COUNT(*) FROM tickets WHERE external_ref LIKE 'nis2:%'"
        )->fetchColumn();
        logLine("[DRY-RUN] eliminerebbe $n ticket (e relativi attachments / messages via FK CASCADE)");
        logLine("Per eseguire davvero: aggiungi --commit");
        exit(0);
    }
    $dst->beginTransaction();
    $deleted = $dst->exec("DELETE FROM tickets WHERE external_ref LIKE 'nis2:%'");
    $dst->commit();
    logLine("Rollback: $deleted ticket eliminati");
    exit(0);
}

// ----------------------------- DETECT OPTIONAL COLUMNS --------------------
function tableHasColumn(PDO $pdo, string $table, string $col): bool {
    $stmt = $pdo->prepare(
        "SELECT 1 FROM information_schema.COLUMNS
          WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?"
    );
    $stmt->execute([$table, $col]);
    return (bool) $stmt->fetchColumn();
}
function tableExists(PDO $pdo, string $table): bool {
    $stmt = $pdo->prepare(
        "SELECT 1 FROM information_schema.TABLES
          WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?"
    );
    $stmt->execute([$table]);
    return (bool) $stmt->fetchColumn();
}

$hasAuditLog        = tableExists($src, 'feedback_audit_log');
$hasRiskScore       = tableHasColumn($src, 'feedback_reports', 'risk_score');
$hasNotaAdmin       = tableHasColumn($src, 'feedback_reports', 'nota_admin');
$hasStatusVerified  = false;
try {
    $row = $src->query(
        "SELECT COLUMN_TYPE FROM information_schema.COLUMNS
          WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'feedback_reports'
            AND COLUMN_NAME = 'status'"
    )->fetch();
    $hasStatusVerified = $row && stripos($row['COLUMN_TYPE'], "'verificato'") !== false;
} catch (PDOException $e) {}

logLine('Source schema: feedback_audit_log=' . ($hasAuditLog ? 'yes' : 'no')
        . ', risk_score=' . ($hasRiskScore ? 'yes' : 'no')
        . ', nota_admin=' . ($hasNotaAdmin ? 'yes' : 'no')
        . ', status verificato=' . ($hasStatusVerified ? 'yes' : 'no'));

// ----------------------------- MAPPING TABLES ------------------------------
$KIND_MAP = [
    'bug'           => 'COMPLAINT',
    'ux'            => 'SUPPORT_REQUEST',
    'funzionalita'  => 'INFO_REQUEST',
    'domanda'       => 'INFO_REQUEST',
    'altro'         => 'OTHER',
];
$PRIORITY_MAP = [
    'alta'  => 'HIGH',
    'media' => 'MEDIUM',
    'bassa' => 'LOW',
];
$STATUS_MAP = [
    'aperto'         => 'OPEN',
    'in_lavorazione' => 'IN_PROGRESS',
    'risolto'        => 'RESOLVED',
    'chiuso'         => 'CLOSED',
    'verificato'     => 'CLOSED',
];

// ----------------------------- READ SOURCE --------------------------------
$sql = "SELECT * FROM feedback_reports ORDER BY id ASC";
if ($limit) { $sql .= " LIMIT " . (int)$limit; }
$rows = $src->query($sql)->fetchAll();
logLine('Source rows fetched: ' . count($rows));

if (empty($rows)) {
    logLine('Niente da migrare. Esco.');
    exit(0);
}

// Pre-load existing external_refs in target (single roundtrip)
$existing = [];
foreach ($dst->query(
    "SELECT external_ref FROM tickets WHERE external_ref LIKE 'nis2:%'"
)->fetchAll(PDO::FETCH_COLUMN) as $ref) {
    $existing[$ref] = true;
}
logLine('Already imported in target: ' . count($existing));

// ----------------------------- INSERT PREP --------------------------------
$insTicket = $dst->prepare("
    INSERT INTO tickets
      (tenant_id, caller_phone, caller_name, caller_email,
       requested_product, requested_product_label,
       subject, operative_summary,
       status, queue, priority, channel, kind,
       external_ref, page_url, reporter_role,
       ai_categoria, ai_priorita, ai_suggerimento, ai_response, ai_processed,
       lang,
       created_at, updated_at)
    VALUES
      (:tenant_id, :caller_phone, :caller_name, :caller_email,
       :requested_product, :requested_product_label,
       :subject, :operative_summary,
       :status, :queue, :priority, :channel, :kind,
       :external_ref, :page_url, :reporter_role,
       :ai_categoria, :ai_priorita, :ai_suggerimento, :ai_response, :ai_processed,
       :lang,
       :created_at, :updated_at)
");
$insAttachment = $dst->prepare("
    INSERT INTO ticket_attachments
      (ticket_id, tenant_id, filename, mime_type, size, data, created_at)
    VALUES
      (:ticket_id, :tenant_id, :filename, :mime_type, :size, :data, :created_at)
");
$insMessage = $dst->prepare("
    INSERT INTO ticket_messages
      (ticket_id, tenant_id, role, content, metadata, created_at)
    VALUES
      (:ticket_id, :tenant_id, 'SYSTEM', :content, :metadata, :created_at)
");

// ----------------------------- LOOP ---------------------------------------
$counters = ['ok' => 0, 'skip' => 0, 'err' => 0, 'attach' => 0, 'audit' => 0];

if (!$dryRun) { $dst->beginTransaction(); }

foreach ($rows as $r) {
    $extRef = 'nis2:' . (int)$r['id'];
    if (isset($existing[$extRef])) {
        $counters['skip']++;
        logLine("[SKIP duplicate] $extRef");
        continue;
    }

    $tipo     = $r['tipo']     ?? 'altro';
    $priorita = $r['priorita'] ?? 'media';
    $status   = $r['status']   ?? 'aperto';

    $kind     = $KIND_MAP[$tipo]         ?? 'OTHER';
    $priority = $PRIORITY_MAP[$priorita] ?? 'MEDIUM';
    $statusN  = $STATUS_MAP[$status]     ?? 'OPEN';

    $descr   = (string) ($r['descrizione'] ?? '');
    $subject = mb_substr(trim(preg_replace('/\s+/', ' ', $descr)), 0, 200);
    if ($subject === '') { $subject = "[NIS2 legacy #{$r['id']}]"; }

    // Append nota_admin alla operative_summary se presente
    $opSummary = $descr;
    if ($hasNotaAdmin && !empty($r['nota_admin'])) {
        $opSummary .= "\n\n--- Nota admin originale ---\n" . $r['nota_admin'];
    }
    if ($hasRiskScore && isset($r['risk_score']) && $r['risk_score'] !== null) {
        $opSummary .= "\n\n[risk_score originale: " . $r['risk_score'] . "]";
    }

    // Map ai_priorita italiano → ENUM Nexus
    $aiPrio = null;
    if (!empty($r['ai_priorita'])) {
        $aiPrio = $PRIORITY_MAP[$r['ai_priorita']] ?? null;
    }

    $params = [
        ':tenant_id'               => $NEXUS_TENANT_ID,
        ':caller_phone'            => '',                       // non disponibile in feedback_reports
        ':caller_name'             => $r['user_email'] ?? null, // fallback: nessun caller_name in source
        ':caller_email'            => $r['user_email'] ?? null,
        ':requested_product'       => 'NIS2',
        ':requested_product_label' => 'NIS2 Agile',
        ':subject'                 => $subject,
        ':operative_summary'       => mb_substr($opSummary, 0, 1990),
        ':status'                  => $statusN,
        ':queue'                   => 'SUPPORT',
        ':priority'                => $priority,
        ':channel'                 => 'FORM',
        ':kind'                    => $kind,
        ':external_ref'            => $extRef,
        ':page_url'                => $r['page_url']        ?? null,
        ':reporter_role'           => $r['user_role']       ?? null,
        ':ai_categoria'            => $r['ai_categoria']    ?? null,
        ':ai_priorita'             => $aiPrio,
        ':ai_suggerimento'         => $r['ai_suggerimento'] ?? null,
        ':ai_response'             => $r['ai_risposta']     ?? null,
        ':ai_processed'            => isset($r['ai_processed']) ? (int)$r['ai_processed'] : 0,
        ':lang'                    => 'it',
        ':created_at'              => $r['created_at']      ?? date('Y-m-d H:i:s'),
        ':updated_at'              => $r['updated_at']      ?? ($r['created_at'] ?? date('Y-m-d H:i:s')),
    ];

    if ($dryRun) {
        $counters['ok']++;
        logLine("[DRY-RUN OK] $extRef  kind=$kind status=$statusN created={$params[':created_at']}");
        continue;
    }

    try {
        $insTicket->execute($params);
        $newId = (int) $dst->lastInsertId();
        $counters['ok']++;
        logLine("[OK] $extRef → ticket #$newId");

        // Attachment inline base64
        if (!empty($r['attachment'])) {
            $att     = (string) $r['attachment'];
            $mime    = 'image/png';
            $rawB64  = $att;
            if (preg_match('/^data:([^;]+);base64,(.*)$/', $att, $m)) {
                $mime   = $m[1];
                $rawB64 = $m[2];
            }
            $size = (int) (strlen($rawB64) * 3 / 4);
            $insAttachment->execute([
                ':ticket_id'  => $newId,
                ':tenant_id'  => $NEXUS_TENANT_ID,
                ':filename'   => 'legacy_' . (int)$r['id'] . '.png',
                ':mime_type'  => $mime,
                ':size'       => $size,
                ':data'       => $att,
                ':created_at' => $params[':created_at'],
            ]);
            $counters['attach']++;
        }

        // feedback_audit_log → ticket_messages role=SYSTEM
        if ($hasAuditLog) {
            $stmt = $src->prepare(
                "SELECT * FROM feedback_audit_log WHERE report_id = ? ORDER BY id ASC"
            );
            $stmt->execute([(int)$r['id']]);
            foreach ($stmt->fetchAll() as $ev) {
                $content = sprintf(
                    "[legacy audit] action=%s to_status=%s actor=%s",
                    $ev['action']    ?? '',
                    $ev['to_status'] ?? '',
                    $ev['actor_type']?? ''
                );
                $insMessage->execute([
                    ':ticket_id'  => $newId,
                    ':tenant_id'  => $NEXUS_TENANT_ID,
                    ':content'    => $content,
                    ':metadata'   => $ev['details'] ?? '{}',
                    ':created_at' => $ev['created_at'] ?? $params[':created_at'],
                ]);
                $counters['audit']++;
            }
        }
    } catch (PDOException $e) {
        $counters['err']++;
        logLine("[ERR] $extRef → " . $e->getMessage());
        // continua: gli altri possono passare. Fail-safe: log e via.
    }
}

if (!$dryRun) {
    if ($counters['err'] > 0) {
        $dst->rollBack();
        logLine('Errori rilevati: ROLLBACK transazione, nessuna riga scritta.');
        logLine("Counters: " . json_encode($counters));
        exit(1);
    }
    $dst->commit();
}

logLine('=== Riepilogo ===');
logLine("OK:          {$counters['ok']}");
logLine("SKIP:        {$counters['skip']}");
logLine("ERR:         {$counters['err']}");
logLine("Attachments: {$counters['attach']}");
logLine("Audit msgs:  {$counters['audit']}");
logLine("Log file:    $logPath");
exit($counters['err'] > 0 ? 1 : 0);
