<?php

namespace WPDesk\FakturyKSEF\Services;

use DOMDocument;
use Exception;
use WPDesk\FakturyKSEF\Helpers\XmlErrors;
use WPDesk\FakturyKSEF\Repository\PluginSettings;
use WPDesk\FakturyKSEF\Xml\DocumentData\CorrectionDocumentData;
use WPDesk\FakturyKSEF\Xml\DocumentData\DocumentData;
use WPDesk\FakturyKSEF\Xml\Validators\XsdValidator;
use WPDesk\FakturyKSEF\Xml\XmlFactory;

/**
 * Service responsible for generating KSeF XML based on document ID.
 */
class XMLGeneratorService {

	private $plugins_access;

	private PluginSettings $settings;

	private DocumentKSeFSettings $document_ksef_settings;

	private InitialDataValidation $initial_data_validator;

	private XmlFactory $xml_factory;

	public function __construct(
		$plugins_access,
		PluginSettings $settings,
		DocumentKSeFSettings $document_ksef_settings,
		InitialDataValidation $initial_data_validator,
		XmlFactory $xml_factory
	) {
		$this->xml_factory            = $xml_factory;
		$this->initial_data_validator = $initial_data_validator;
		$this->document_ksef_settings = $document_ksef_settings;
		$this->plugins_access         = $plugins_access;
		$this->settings               = $settings;
	}

	/**
	 * Generates KSeF XML string for a given document ID.
	 */
	public function generate_for_document_id( int $document_id ): string {
		$invoice_document = $this->get_invoice_document_object( $document_id );
		if ( ! $invoice_document ) {
			throw new Exception( esc_html__( 'Could not retrieve document object.', 'ksef-for-flexible-invoices' ) );
		}

		$validation_reasons = $this->initial_data_validator->validate( $invoice_document );
		if ( ! empty( $validation_reasons ) ) {
			throw new Exception( implode( ', ', $validation_reasons ) );
		}

		$ksef_xml_document = $this->build_and_validate_ksef_xml( $invoice_document );

		return $ksef_xml_document->saveXML();
	}

	public function generate_for_document_id_without_validation( int $document_id ): string {
		$invoice_document = $this->get_invoice_document_object( $document_id );
		if ( ! $invoice_document ) {
			throw new Exception( esc_html__( 'Could not retrieve document object.', 'ksef-for-flexible-invoices' ) );
		}

		$ksef_xml_document = $this->build_ksef_xml( $invoice_document );

		return $ksef_xml_document->saveXML();
	}

	/**
	 * Retrieve Flexible Invoices document object.
	 */
	private function get_invoice_document_object( int $document_id ) {
		$document_factory_creator = $this->plugins_access->get_document_factory()->get_document_creator( $document_id );
		if ( ! $document_factory_creator ) {
			return null;
		}
		return $document_factory_creator->get_document();
	}

	/**
	 * Builds the KSeF XML document and validates it against XSD.
	 */
	private function build_and_validate_ksef_xml( $document ): DOMDocument {
		$dom_document = $this->build_ksef_xml( $document );

		$xsd_validator = new XsdValidator( 'Invoice' );
		$errors        = $xsd_validator->schema_validate( $dom_document );

		if ( $errors ) {
			$error_message = XmlErrors::display_errors( $errors );
			// translators: %s error message
			throw new Exception( sprintf( esc_html__( 'XML schema validation failed: %s', 'ksef-for-flexible-invoices' ), $error_message ) );
		}
		return $dom_document;
	}

	private function build_ksef_xml( $document ): DOMDocument {

		if ( $document->get_type() === 'correction' ) {
			return $this->xml_factory->create_correction(
				new CorrectionDocumentData(
					$document,
					$this->get_invoice_document_object( $document->get_corrected_id() ),
					$this->plugins_access->get_settings_strategy(),
					$this->settings,
					$this->document_ksef_settings
				)
			);
		}
		return $this->xml_factory->create_invoice(
			new DocumentData(
				$document,
				$this->plugins_access->get_settings_strategy(),
				$this->settings,
				$this->document_ksef_settings
			)
		);
	}
}
