<?php

/**
 * XML format exporter.
 *
 * @package Leadite
 */
namespace Leadite\Export;

defined( 'ABSPATH' ) || exit;
class XmlExporter extends Exporter {
    public function format() {
        return 'xml';
    }

    public function mime_type() {
        return 'application/xml; charset=utf-8';
    }

    public function export( array $leads ) {
        $xml = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><leads></leads>');
        // Batch-fetch all meta in one query to avoid N+1.
        $meta_map = \Leadite\Storage\LeadsRepository::find_many( $leads );
        $lead_ids = array_map( function ( $l ) {
            return (int) $l->id;
        }, $leads );
        $tags_map = array();
        $notes_map = array();
        foreach ( $leads as $lead ) {
            $item = $xml->addChild( 'lead' );
            $item->addChild( 'id', (string) $lead->id );
            $item->addChild( 'source', self::sanitize_xml( (string) $lead->source ) );
            $item->addChild( 'form_id', self::sanitize_xml( (string) $lead->form_id ) );
            $item->addChild( 'form_name', self::sanitize_xml( (string) $lead->form_name ) );
            $item->addChild( 'status', self::sanitize_xml( (string) $lead->status ) );
            $item->addChild( 'ip', self::sanitize_xml( (string) $lead->ip ) );
            $item->addChild( 'lang', self::sanitize_xml( (string) $lead->lang ) );
            $item->addChild( 'created_at', (string) $lead->created_at );
            $lead_fields = ( isset( $meta_map[(int) $lead->id] ) ? $meta_map[(int) $lead->id]['fields'] : array() );
            $fields = $item->addChild( 'fields' );
            foreach ( $lead_fields as $f ) {
                $field = $fields->addChild( 'field' );
                $field->addChild( 'key', self::sanitize_xml( (string) $f->field_key ) );
                $field->addChild( 'label', self::sanitize_xml( (string) $f->field_label ) );
                $field->addChild( 'value', self::sanitize_xml( (string) $f->field_value ) );
                $field->addChild( 'type', self::sanitize_xml( (string) $f->field_type ) );
                $field->addChild( 'canonical', self::sanitize_xml( (string) $f->canonical ) );
            }
        }
        // Write the XML document straight to the output stream (no echo of raw markup).
        $xml->asXML( 'php://output' );
    }

    /**
     * Remove characters that are illegal in XML 1.0 documents.
     *
     * XML 1.0 ֲ§ 2.2 defines the legal character set as:
     *   #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
     *
     * Anything outside that range (C0 controls except TAB/LF/CR, DEL, C1
     * controls, surrogates, and the BOM-like #xFFFE/#xFFFF) is silently stripped
     * rather than escaped, because there is no valid XML escape for them.
     *
     * @param string $value UTF-8 string (may contain user-supplied content).
     * @return string Clean UTF-8 string safe for use in an XML 1.0 document.
     */
    protected static function sanitize_xml( $value ) {
        // Strip any character that cannot appear in a well-formed XML 1.0 document.
        // The regex matches characters NOT in the legal XML 1.0 character set.
        return (string) preg_replace( '/[^\\x09\\x0A\\x0D\\x20-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{10000}-\\x{10FFFF}]/u', '', (string) $value );
    }

}
