<?php

namespace App\Livewire\Settings;

use App\Models\AnnoTeocratico;
use App\Models\Assegnazione;
use App\Models\Campagna;
use App\Models\Proclamatore;
use App\Models\Setting;
use App\Models\Territorio;
use App\Models\Tipologia;
use App\Models\User;
use App\Models\Zona;
use Barryvdh\DomPDF\Facade\Pdf;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
use Livewire\WithFileUploads;

class XmlExchange extends Component
{
    use WithFileUploads;

    public $sqlDump;
    public $xmlImport;
    public array $importStats = [];
    public array $importIssues = [];

    public function convertLegacySqlToXml()
    {
        $this->validate([
            'sqlDump' => ['required', 'file', 'mimes:sql,txt'],
        ]);

        $content = file_get_contents($this->sqlDump->getRealPath());
        $dataset = $this->legacySqlToDataset($content ?: '');
        $xml = $this->datasetToXml($dataset, 'legacy-sql-conversion');

        return response()->streamDownload(function () use ($xml) {
            echo $xml;
        }, 'termanager-conversion.xml', ['Content-Type' => 'application/xml; charset=UTF-8']);
    }

    public function importXmlIntoApp(): void
    {
        $this->importStats = [];
        $this->importIssues = [];

        $this->validate([
            'xmlImport' => ['required', 'file', 'mimes:xml,txt'],
        ]);

        $content = file_get_contents($this->xmlImport->getRealPath());
        if (! $content) {
            $this->addError('xmlImport', 'File XML non valido.');
            return;
        }

        $xml = @simplexml_load_string($content);
        if (! $xml) {
            $this->addError('xmlImport', 'Impossibile leggere il file XML.');
            return;
        }

        $actorId = auth()->id() ?? User::query()->value('id');
        if (! $actorId) {
            $this->addError('xmlImport', 'Serve almeno un utente nel sistema per importare i dati.');
            return;
        }

        $stats = [
            'zone_importate' => 0,
            'tipologie_importate' => 0,
            'proclamatori_importati' => 0,
            'anni_importati' => 0,
            'campagne_importate' => 0,
            'territori_importati' => 0,
            'assegnazioni_importate' => 0,
            'duplicate_territori' => 0,
            'assegnazioni_saltate' => 0,
        ];

        DB::transaction(function () use ($xml, $actorId, &$stats) {
            Assegnazione::query()->delete();
            Campagna::query()->delete();
            Territorio::query()->withTrashed()->forceDelete();
            Proclamatore::query()->withTrashed()->forceDelete();
            Tipologia::query()->delete();
            Zona::query()->delete();
            AnnoTeocratico::query()->delete();
            Setting::query()->delete();

            $settingsNode = $xml->settings;
            if ($settingsNode) {
                Setting::instance()->update([
                    'congregazione_nome' => (string) ($settingsNode->congregazione_nome ?? ''),
                    'giorni_giacenza_da_assegnare' => (int) ($settingsNode->giorni_giacenza_da_assegnare ?? 120),
                    'giorni_giacenza_prioritari' => (int) ($settingsNode->giorni_giacenza_prioritari ?? 180),
                    'giorni_per_smarrito' => (int) ($settingsNode->giorni_per_smarrito ?? 120),
                    'home_limit_list' => (int) ($settingsNode->home_limit_list ?? 10),
                    'audit_retention_days' => (int) ($settingsNode->audit_retention_days ?? 730),
                    'setup_completed' => true,
                ]);
            }

            $zoneMap = [];
            foreach (($xml->zones->zone ?? []) as $zoneNode) {
                $z = Zona::create([
                    'nome' => (string) $zoneNode->nome,
                    'attivo' => ((int) ($zoneNode->attivo ?? 1)) === 1,
                ]);
                $stats['zone_importate']++;
                $legacyId = (string) ($zoneNode['legacy_id'] ?? '');
                if ($legacyId !== '') {
                    $zoneMap[$legacyId] = $z->id;
                }
            }

            $tipologiaMap = [];
            foreach (($xml->tipologie->tipologia ?? []) as $tipologiaNode) {
                $t = Tipologia::create([
                    'nome' => (string) $tipologiaNode->nome,
                    'attivo' => ((int) ($tipologiaNode->attivo ?? 1)) === 1,
                ]);
                $stats['tipologie_importate']++;
                $legacyId = (string) ($tipologiaNode['legacy_id'] ?? '');
                if ($legacyId !== '') {
                    $tipologiaMap[$legacyId] = $t->id;
                }
            }

            $proclamatoreMap = [];
            foreach (($xml->proclamatori->proclamatore ?? []) as $proclamatoreNode) {
                $p = Proclamatore::create([
                    'nome' => (string) $proclamatoreNode->nome,
                    'cognome' => (string) $proclamatoreNode->cognome,
                    'attivo' => ((int) ($proclamatoreNode->attivo ?? 1)) === 1,
                ]);
                $stats['proclamatori_importati']++;
                $legacyId = (string) ($proclamatoreNode['legacy_id'] ?? '');
                if ($legacyId !== '') {
                    $proclamatoreMap[$legacyId] = $p->id;
                }
            }

            $annoMap = [];
            foreach (($xml->anni_teocratici->anno ?? []) as $annoNode) {
                $label = (string) $annoNode->label;
                $anno = AnnoTeocratico::create([
                    'label' => $label,
                    'start_date' => (string) $annoNode->start_date,
                    'end_date' => (string) $annoNode->end_date,
                ]);
                $stats['anni_importati']++;
                $legacyId = (string) ($annoNode['legacy_id'] ?? '');
                if ($legacyId !== '') {
                    $annoMap[$legacyId] = $anno->id;
                }
            }

            $campagnaMap = [];
            foreach (($xml->campagne->campagna ?? []) as $campagnaNode) {
                $campagna = Campagna::create([
                    'descrizione' => (string) $campagnaNode->descrizione,
                    'start_date' => (string) $campagnaNode->start_date,
                    'end_date' => (string) $campagnaNode->end_date,
                ]);
                $stats['campagne_importate']++;
                $legacyId = (string) ($campagnaNode['legacy_id'] ?? '');
                if ($legacyId !== '') {
                    $campagnaMap[$legacyId] = $campagna->id;
                }
            }

            $territorioMap = [];
            $territoriNumeroVisti = [];
            foreach (($xml->territori->territorio ?? []) as $territorioNode) {
                $legacyZonaId = (string) ($territorioNode->legacy_zona_id ?? '');
                $legacyTipologiaId = (string) ($territorioNode->legacy_tipologia_id ?? '');
                $numero = trim((string) $territorioNode->numero);
                $legacyId = (string) ($territorioNode['legacy_id'] ?? '');

                if ($numero === '' || isset($territoriNumeroVisti[$numero])) {
                    $stats['duplicate_territori']++;
                    $this->pushImportIssue('territorio', $legacyId, 'Territorio duplicato o numero vuoto (numero=' . ($numero !== '' ? $numero : 'vuoto') . ')');
                    continue;
                }

                if (!isset($zoneMap[$legacyZonaId], $tipologiaMap[$legacyTipologiaId])) {
                    $this->pushImportIssue('territorio', $legacyId, 'Riferimento zona/tipologia non trovato (zona=' . $legacyZonaId . ', tipologia=' . $legacyTipologiaId . ')');
                    continue;
                }

                $territorio = Territorio::create([
                    'numero' => $numero,
                    'zona_id' => $zoneMap[$legacyZonaId],
                    'tipologia_id' => $tipologiaMap[$legacyTipologiaId],
                    'confini' => (string) ($territorioNode->confini ?? ''),
                    'note' => (string) ($territorioNode->note ?? ''),
                    'attivo' => ((int) ($territorioNode->attivo ?? 1)) === 1,
                    'prioritario' => ((int) ($territorioNode->prioritario ?? 0)) === 1,
                ]);

                $territoriNumeroVisti[$numero] = true;
                $stats['territori_importati']++;

                if ($legacyId !== '') {
                    $territorioMap[$legacyId] = $territorio->id;
                }
            }

            foreach (($xml->assegnazioni->assegnazione ?? []) as $assegnazioneNode) {
                $legacyTerritorio = (string) ($assegnazioneNode->legacy_territorio_id ?? '');
                $legacyProclamatore = (string) ($assegnazioneNode->legacy_proclamatore_id ?? '');
                $legacyAnno = (string) ($assegnazioneNode->legacy_anno_id ?? '');
                $legacyId = (string) ($assegnazioneNode['legacy_id'] ?? '');
                $assignedAt = $this->normalizeDateForDb((string) ($assegnazioneNode->assigned_at ?? ''), false);
                $returnedAt = $this->normalizeDateForDb((string) ($assegnazioneNode->returned_at ?? ''), true);

                if (!isset($territorioMap[$legacyTerritorio], $proclamatoreMap[$legacyProclamatore], $annoMap[$legacyAnno])) {
                    $stats['assegnazioni_saltate']++;
                    $this->pushImportIssue('assegnazione', $legacyId, 'Riferimenti mancanti (territorio=' . $legacyTerritorio . ', proclamatore=' . $legacyProclamatore . ', anno=' . $legacyAnno . ')');
                    continue;
                }

                if ($assignedAt === null) {
                    $stats['assegnazioni_saltate']++;
                    $this->pushImportIssue('assegnazione', $legacyId, 'Data assigned_at non valida o vuota');
                    continue;
                }

                $legacyCampagna = (string) ($assegnazioneNode->legacy_campagna_id ?? '');

                Assegnazione::create([
                    'territorio_id' => $territorioMap[$legacyTerritorio],
                    'proclamatore_id' => $proclamatoreMap[$legacyProclamatore],
                    'anno_teocratico_id' => $annoMap[$legacyAnno],
                    'assigned_at' => $assignedAt,
                    'returned_at' => $returnedAt,
                    'counted_in_campaign' => ((int) ($assegnazioneNode->counted_in_campaign ?? 0)) === 1,
                    'campaign_id' => $legacyCampagna !== '' && isset($campagnaMap[$legacyCampagna]) ? $campagnaMap[$legacyCampagna] : null,
                    'note' => (string) ($assegnazioneNode->note ?? ''),
                    'created_by' => $actorId,
                    'returned_by' => $returnedAt !== null ? $actorId : null,
                ]);
                $stats['assegnazioni_importate']++;
            }
        });

        $this->importStats = $stats;

        $message = 'Import XML completato con successo.';
        if ($stats['duplicate_territori'] > 0 || $stats['assegnazioni_saltate'] > 0) {
            $message .= ' Territori duplicati saltati: ' . $stats['duplicate_territori'] . '. Assegnazioni saltate: ' . $stats['assegnazioni_saltate'] . '.';
        }

        session()->flash('success', $message);
    }

    public function downloadImportLogPdf()
    {
        if (empty($this->importStats)) {
            session()->flash('error', 'Nessun log da scaricare. Esegui prima un import XML.');
            return;
        }

        $stats = $this->importStats;
        $issues = $this->importIssues;
        $generatedAt = now()->format('d/m/Y H:i:s');

        $html = view('pdf.import-log', compact('stats', 'issues', 'generatedAt'))->render();

        return response()->streamDownload(function () use ($html) {
            echo Pdf::loadHTML($html)
                ->setPaper('a4', 'portrait')
                ->output();
        }, 'import-log-' . now()->format('Ymd-His') . '.pdf', ['Content-Type' => 'application/pdf']);
    }

    public function exportCurrentAsXml()
    {
        $dataset = $this->currentDataset();
        $xml = $this->datasetToXml($dataset, 'current-app-export');

        return response()->streamDownload(function () use ($xml) {
            echo $xml;
        }, 'termanager-export.xml', ['Content-Type' => 'application/xml; charset=UTF-8']);
    }

    public function render()
    {
        return view('livewire.settings.xml-exchange');
    }

    private function currentDataset(): array
    {
        $settings = Setting::instance();

        return [
            'settings' => [
                'congregazione_nome' => (string) ($settings->congregazione_nome ?? ''),
                'giorni_giacenza_da_assegnare' => (int) ($settings->giorni_giacenza_da_assegnare ?? 120),
                'giorni_giacenza_prioritari' => (int) ($settings->giorni_giacenza_prioritari ?? 180),
                'giorni_per_smarrito' => (int) ($settings->giorni_per_smarrito ?? 120),
                'home_limit_list' => (int) ($settings->home_limit_list ?? 10),
                'audit_retention_days' => (int) ($settings->audit_retention_days ?? 730),
            ],
            'zones' => Zona::query()->orderBy('id')->get(['id', 'nome', 'attivo'])->toArray(),
            'tipologie' => Tipologia::query()->orderBy('id')->get(['id', 'nome', 'attivo'])->toArray(),
            'proclamatori' => Proclamatore::query()->withTrashed()->orderBy('id')->get(['id', 'nome', 'cognome', 'attivo'])->toArray(),
            'territori' => Territorio::query()->withTrashed()->orderBy('id')->get(['id', 'numero', 'zona_id', 'tipologia_id', 'confini', 'note', 'attivo', 'prioritario'])->toArray(),
            'anni_teocratici' => AnnoTeocratico::query()->orderBy('id')->get(['id', 'label', 'start_date', 'end_date'])->toArray(),
            'campagne' => Campagna::query()->orderBy('id')->get(['id', 'descrizione', 'start_date', 'end_date'])->toArray(),
            'assegnazioni' => Assegnazione::query()->orderBy('id')->get(['id', 'territorio_id', 'proclamatore_id', 'anno_teocratico_id', 'campaign_id', 'assigned_at', 'returned_at', 'counted_in_campaign', 'note'])->toArray(),
        ];
    }

    private function legacySqlToDataset(string $sql): array
    {
        $rows = $this->extractInsertRows($sql);

        $congregazione = $rows['congregazione'][0][1] ?? 'Congregazione';
        $impostazioni = $rows['impostazioni'][0] ?? [1, 120, 10, 120];

        $settings = [
            'congregazione_nome' => (string) $congregazione,
            'giorni_giacenza_da_assegnare' => (int) ($impostazioni[3] ?? 120),
            'giorni_giacenza_prioritari' => (int) ($impostazioni[1] ?? 180),
            'giorni_per_smarrito' => (int) ($impostazioni[3] ?? 120),
            'home_limit_list' => (int) ($impostazioni[2] ?? 10),
            'audit_retention_days' => 730,
        ];

        $zones = [];
        foreach (($rows['zona'] ?? []) as $r) {
            $zones[] = [
                'legacy_id' => (string) ($r[0] ?? ''),
                'nome' => (string) ($r[1] ?? ''),
                'attivo' => 1,
            ];
        }

        $tipologie = [];
        foreach (($rows['tipologia'] ?? []) as $r) {
            $tipologie[] = [
                'legacy_id' => (string) ($r[0] ?? ''),
                'nome' => (string) ($r[1] ?? ''),
                'attivo' => 1,
            ];
        }

        $proclamatori = [];
        foreach (($rows['proclamatore'] ?? []) as $r) {
            $proclamatori[] = [
                'legacy_id' => (string) ($r[0] ?? ''),
                'cognome' => (string) ($r[1] ?? ''),
                'nome' => (string) ($r[2] ?? ''),
                'attivo' => ((int) ($r[3] ?? 1)) === 1 ? 1 : 0,
            ];
        }

        $territori = [];
        foreach (($rows['territorio'] ?? []) as $r) {
            $territori[] = [
                'legacy_id' => (string) ($r[0] ?? ''),
                'numero' => (string) ($r[1] ?? ''),
                'legacy_tipologia_id' => (string) ($r[2] ?? ''),
                'legacy_zona_id' => (string) ($r[3] ?? ''),
                'confini' => (string) ($r[4] ?? ''),
                'note' => (string) ($r[5] ?? ''),
                'attivo' => ((int) ($r[8] ?? 1)) === 1 ? 1 : 0,
                'prioritario' => ((int) ($r[11] ?? 0)) > 0 ? 1 : 0,
            ];
        }

        $anni = [];
        foreach (($rows['annoServizio'] ?? []) as $r) {
            $label = (string) ($r[1] ?? '');
            [$startDate, $endDate] = $this->datesFromLegacyAnnoLabel($label);
            $anni[] = [
                'legacy_id' => (string) ($r[0] ?? ''),
                'label' => str_replace(' - ', '-', $label),
                'start_date' => $startDate,
                'end_date' => $endDate,
            ];
        }

        $campagne = [];
        foreach (($rows['campagna'] ?? []) as $r) {
            $campagne[] = [
                'legacy_id' => (string) ($r[0] ?? ''),
                'descrizione' => (string) ($r[1] ?? ''),
                'start_date' => $this->toDateOnly((string) ($r[2] ?? '')),
                'end_date' => $this->toDateOnly((string) ($r[3] ?? '')),
            ];
        }

        $assegnazioni = [];
        foreach (($rows['assegnazione'] ?? []) as $r) {
            $assegnazioni[] = [
                'legacy_id' => (string) ($r[0] ?? ''),
                'legacy_territorio_id' => (string) ($r[1] ?? ''),
                'legacy_proclamatore_id' => (string) ($r[2] ?? ''),
                'legacy_anno_id' => (string) ($r[9] ?? ''),
                'legacy_campagna_id' => $r[8] === null ? '' : (string) $r[8],
                'assigned_at' => $this->toDateOnly((string) ($r[4] ?? '')),
                'returned_at' => $this->toDateOnly((string) ($r[5] ?? '')),
                'counted_in_campaign' => ((int) ($r[7] ?? 0)) === 1 ? 1 : 0,
                'note' => '',
            ];
        }

        return [
            'settings' => $settings,
            'zones' => $zones,
            'tipologie' => $tipologie,
            'proclamatori' => $proclamatori,
            'territori' => $territori,
            'anni_teocratici' => $anni,
            'campagne' => $campagne,
            'assegnazioni' => $assegnazioni,
        ];
    }

    private function extractInsertRows(string $sql): array
    {
        $result = [];
        preg_match_all('/INSERT INTO `([^`]+)` VALUES\s*(.+?);/s', $sql, $matches, PREG_SET_ORDER);

        foreach ($matches as $match) {
            $table = $match[1];
            $valuesPayload = $match[2];
            $tuples = $this->splitSqlTuples($valuesPayload);

            foreach ($tuples as $tuple) {
                $result[$table][] = $this->parseSqlTuple($tuple);
            }
        }

        return $result;
    }

    private function splitSqlTuples(string $payload): array
    {
        $tuples = [];
        $inString = false;
        $escape = false;
        $depth = 0;
        $current = '';

        $len = strlen($payload);
        for ($i = 0; $i < $len; $i++) {
            $ch = $payload[$i];

            if ($inString) {
                $current .= $ch;
                if ($escape) {
                    $escape = false;
                    continue;
                }
                if ($ch === '\\\\') {
                    $escape = true;
                    continue;
                }
                if ($ch === "'") {
                    $inString = false;
                }
                continue;
            }

            if ($ch === "'") {
                $inString = true;
                $current .= $ch;
                continue;
            }

            if ($ch === '(') {
                if ($depth === 0) {
                    $current = '';
                }
                $depth++;
                continue;
            }

            if ($ch === ')') {
                $depth--;
                if ($depth === 0) {
                    $tuples[] = $current;
                    $current = '';
                    continue;
                }
            }

            if ($depth > 0) {
                $current .= $ch;
            }
        }

        return $tuples;
    }

    private function parseSqlTuple(string $tuple): array
    {
        $values = [];
        $inString = false;
        $escape = false;
        $current = '';

        $len = strlen($tuple);
        for ($i = 0; $i < $len; $i++) {
            $ch = $tuple[$i];

            if ($inString) {
                $current .= $ch;
                if ($escape) {
                    $escape = false;
                    continue;
                }
                if ($ch === '\\\\') {
                    $escape = true;
                    continue;
                }
                if ($ch === "'") {
                    $inString = false;
                }
                continue;
            }

            if ($ch === "'") {
                $inString = true;
                $current .= $ch;
                continue;
            }

            if ($ch === ',') {
                $values[] = $this->normalizeSqlValue($current);
                $current = '';
                continue;
            }

            $current .= $ch;
        }

        if ($current !== '') {
            $values[] = $this->normalizeSqlValue($current);
        }

        return $values;
    }

    private function normalizeSqlValue(string $raw)
    {
        $raw = trim($raw);
        if (strcasecmp($raw, 'NULL') === 0) {
            return null;
        }

        if (str_starts_with($raw, "'") && str_ends_with($raw, "'")) {
            $v = substr($raw, 1, -1);
            $v = str_replace(["\\\\", "\\'", '\\"', '\\n', '\\r', '\\t'], ['\\', "'", '"', "\n", "\r", "\t"], $v);
            return html_entity_decode($v, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        }

        if (is_numeric($raw)) {
            return str_contains($raw, '.') ? (float) $raw : (int) $raw;
        }

        return $raw;
    }

    private function datesFromLegacyAnnoLabel(string $label): array
    {
        if (preg_match('/(\d{4})\s*-\s*(\d{4})/', $label, $m)) {
            return [$m[1] . '-09-01', $m[2] . '-08-31'];
        }

        $now = Carbon::now();
        return [$now->copy()->startOfYear()->toDateString(), $now->copy()->endOfYear()->toDateString()];
    }

    private function toDateOnly(string $dateTime): string
    {
        if ($dateTime === '') {
            return '';
        }
        return substr($dateTime, 0, 10);
    }

    private function normalizeDateForDb(string $raw, bool $nullable): ?string
    {
        $raw = trim($raw);
        if ($raw === '' || $raw === '0000-00-00' || $raw === '0000-00-00 00:00:00') {
            return $nullable ? null : null;
        }

        try {
            return Carbon::parse($raw)->toDateString();
        } catch (\Throwable $e) {
            return $nullable ? null : null;
        }
    }

    private function pushImportIssue(string $entity, string $legacyId, string $reason): void
    {
        if (count($this->importIssues) >= 300) {
            return;
        }

        $this->importIssues[] = [
            'entity' => $entity,
            'legacy_id' => $legacyId !== '' ? $legacyId : '-',
            'reason' => $reason,
        ];
    }

    private function datasetToXml(array $dataset, string $source): string
    {
        $xml = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><termanager2/>');
        $xml->addAttribute('version', '1.0');
        $xml->addAttribute('source', $source);
        $xml->addAttribute('generated_at', now()->toIso8601String());

        $settings = $xml->addChild('settings');
        foreach ($dataset['settings'] as $key => $value) {
            $settings->addChild($key, htmlspecialchars((string) $value, ENT_QUOTES | ENT_XML1, 'UTF-8'));
        }

        $zonesNode = $xml->addChild('zones');
        foreach ($dataset['zones'] as $zone) {
            $node = $zonesNode->addChild('zone');
            if (isset($zone['legacy_id'])) {
                $node->addAttribute('legacy_id', (string) $zone['legacy_id']);
            }
            $node->addChild('nome', htmlspecialchars((string) ($zone['nome'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
            $node->addChild('attivo', (string) ((int) ($zone['attivo'] ?? 1)));
        }

        $tipologieNode = $xml->addChild('tipologie');
        foreach ($dataset['tipologie'] as $tipologia) {
            $node = $tipologieNode->addChild('tipologia');
            if (isset($tipologia['legacy_id'])) {
                $node->addAttribute('legacy_id', (string) $tipologia['legacy_id']);
            }
            $node->addChild('nome', htmlspecialchars((string) ($tipologia['nome'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
            $node->addChild('attivo', (string) ((int) ($tipologia['attivo'] ?? 1)));
        }

        $proclamatoriNode = $xml->addChild('proclamatori');
        foreach ($dataset['proclamatori'] as $proclamatore) {
            $node = $proclamatoriNode->addChild('proclamatore');
            if (isset($proclamatore['legacy_id'])) {
                $node->addAttribute('legacy_id', (string) $proclamatore['legacy_id']);
            }
            $node->addChild('nome', htmlspecialchars((string) ($proclamatore['nome'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
            $node->addChild('cognome', htmlspecialchars((string) ($proclamatore['cognome'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
            $node->addChild('attivo', (string) ((int) ($proclamatore['attivo'] ?? 1)));
        }

        $territoriNode = $xml->addChild('territori');
        foreach ($dataset['territori'] as $territorio) {
            $node = $territoriNode->addChild('territorio');
            if (isset($territorio['legacy_id'])) {
                $node->addAttribute('legacy_id', (string) $territorio['legacy_id']);
            }
            $node->addChild('numero', htmlspecialchars((string) ($territorio['numero'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
            $node->addChild('legacy_zona_id', (string) ($territorio['legacy_zona_id'] ?? ($territorio['zona_id'] ?? '')));
            $node->addChild('legacy_tipologia_id', (string) ($territorio['legacy_tipologia_id'] ?? ($territorio['tipologia_id'] ?? '')));
            $node->addChild('confini', htmlspecialchars((string) ($territorio['confini'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
            $node->addChild('note', htmlspecialchars((string) ($territorio['note'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
            $node->addChild('attivo', (string) ((int) ($territorio['attivo'] ?? 1)));
            $node->addChild('prioritario', (string) ((int) ($territorio['prioritario'] ?? 0)));
        }

        $anniNode = $xml->addChild('anni_teocratici');
        foreach ($dataset['anni_teocratici'] as $anno) {
            $node = $anniNode->addChild('anno');
            if (isset($anno['legacy_id'])) {
                $node->addAttribute('legacy_id', (string) $anno['legacy_id']);
            }
            $node->addChild('label', htmlspecialchars((string) ($anno['label'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
            $node->addChild('start_date', (string) ($anno['start_date'] ?? ''));
            $node->addChild('end_date', (string) ($anno['end_date'] ?? ''));
        }

        $campagneNode = $xml->addChild('campagne');
        foreach ($dataset['campagne'] as $campagna) {
            $node = $campagneNode->addChild('campagna');
            if (isset($campagna['legacy_id'])) {
                $node->addAttribute('legacy_id', (string) $campagna['legacy_id']);
            }
            $node->addChild('descrizione', htmlspecialchars((string) ($campagna['descrizione'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
            $node->addChild('start_date', (string) ($campagna['start_date'] ?? ''));
            $node->addChild('end_date', (string) ($campagna['end_date'] ?? ''));
        }

        $assegnazioniNode = $xml->addChild('assegnazioni');
        foreach ($dataset['assegnazioni'] as $assegnazione) {
            $node = $assegnazioniNode->addChild('assegnazione');
            if (isset($assegnazione['legacy_id'])) {
                $node->addAttribute('legacy_id', (string) $assegnazione['legacy_id']);
            }
            $node->addChild('legacy_territorio_id', (string) ($assegnazione['legacy_territorio_id'] ?? ($assegnazione['territorio_id'] ?? '')));
            $node->addChild('legacy_proclamatore_id', (string) ($assegnazione['legacy_proclamatore_id'] ?? ($assegnazione['proclamatore_id'] ?? '')));
            $node->addChild('legacy_anno_id', (string) ($assegnazione['legacy_anno_id'] ?? ($assegnazione['anno_teocratico_id'] ?? '')));
            $node->addChild('legacy_campagna_id', (string) ($assegnazione['legacy_campagna_id'] ?? ($assegnazione['campaign_id'] ?? '')));
            $node->addChild('assigned_at', (string) ($assegnazione['assigned_at'] ?? ''));
            $node->addChild('returned_at', (string) ($assegnazione['returned_at'] ?? ''));
            $node->addChild('counted_in_campaign', (string) ((int) ($assegnazione['counted_in_campaign'] ?? 0)));
            $node->addChild('note', htmlspecialchars((string) ($assegnazione['note'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
        }

        return $xml->asXML() ?: '';
    }
}
