<?php

namespace WPDesk\FakturyKSEF\Xml;

use DOMDocument;
use DOMElement;

class ArrayToXmlConverter {

	private string $default_namespace_prefix;

	private string $namespace_uri;

	public const ATTRIBUTES_KEY = '_attributes';
	public const VALUE_KEY      = '_value';

	public function __construct( string $default_namespace_prefix = 'tns', string $namespace_uri = '' ) {
		$this->default_namespace_prefix = $default_namespace_prefix;
		$this->namespace_uri            = $namespace_uri;
	}

	public function build_xml( DOMDocument $dom, DOMElement $parent, array $data ): void {
		foreach ( $data as $key => $value ) {
			if ( $value === null || $value === '' || ( is_array( $value ) && empty( $value ) ) ) {
				continue;
			}
			if ( is_array( $value ) && $this->is_numeric_array( $value ) ) {
				foreach ( $value as $item ) {
					$this->process_node( $dom, $parent, $key, $item );
				}
			} else {
				$this->process_node( $dom, $parent, $key, $value );
			}
		}
	}

	private function process_node( DOMDocument $dom, DOMElement $parent, $key, $value ): void {
		$node_name = $this->get_node_name( (string) $key );

		if ( is_array( $value ) ) {
			$has_attributes = isset( $value[ self::ATTRIBUTES_KEY ] );
			$has_value      = isset( $value[ self::VALUE_KEY ] );

			if ( $has_attributes || $has_value ) {
				$node = $this->create_element( $dom, $node_name, $has_value ? (string) $value[ self::VALUE_KEY ] : null );

				if ( $has_attributes && is_array( $value[ self::ATTRIBUTES_KEY ] ) ) {
					foreach ( $value[ self::ATTRIBUTES_KEY ] as $attr_name => $attr_value ) {
						$node->setAttribute( $attr_name, (string) $attr_value );
					}
				}

				$children = array_diff_key(
					$value,
					[
						self::ATTRIBUTES_KEY => true,
						self::VALUE_KEY      => true,
					]
				);
				if ( ! empty( $children ) ) {
					$this->build_xml( $dom, $node, $children );
				}

				$parent->appendChild( $node );
			} else {
				$node = $this->create_element( $dom, $node_name );
				$this->build_xml( $dom, $node, $value );
				$parent->appendChild( $node );
			}
		} else {
			$node = $this->create_element( $dom, $node_name, (string) $value );
			$parent->appendChild( $node );
		}
	}

	private function get_node_name( string $key ): string {
		if ( strpos( $key, ':' ) !== false ) {
			return $key;
		}

		if ( $this->default_namespace_prefix !== '' ) {
			return $this->default_namespace_prefix . ':' . $key;
		}

		return $key;
	}

	private function create_element( DOMDocument $dom, string $name, ?string $value = null ): DOMElement {
		$element = $this->namespace_uri
			? $dom->createElementNS( $this->namespace_uri, $name )
			: $dom->createElement( $name );

		if ( $value !== null && $value !== '' ) {
			$element->appendChild( $dom->createTextNode( $value ) );
		}

		return $element;
	}

	private function is_numeric_array( array $array ): bool {
		foreach ( array_keys( $array ) as $key ) {
			if ( ! is_int( $key ) ) {
				return false;
			}
		}

		return true;
	}
}
