<?php

namespace WPDesk\FakturyKSEF\Xml;

use Exception;
use WPDesk\FakturyKSEF\AjaxResponse\AjaxResponse;
use WPDesk\FakturyKSEF\Api\Exceptions\KSeFAPIException;
use WPDesk\FakturyKSEF\Api\Exceptions\KSeFNetworkException;
use WPDesk\FakturyKSEF\Api\Repository\DocumentStateRepository;
use WPDesk\FakturyKSEF\Api\Services\DocumentService;
use WPDesk\FakturyKSEF\Renderer\KSeFRenderer;
use WPDesk\FakturyKSEF\Repository\PluginSettings;
use WPDesk\FakturyKSEF\Services\DownloadXMLUrlGenerator;
use WPDesk\FakturyKSEF\Services\EncryptionService;
use WPDesk\FakturyKSEF\Services\XMLGeneratorService;
use WPDeskKSEFVendor\WPDesk\PluginBuilder\Plugin\Hookable;
use WPDesk\CoreAlias\Document;

/**
 * Send document to KSEF.
 */
class SendDocument implements Hookable {

	// Nonce actions
	public const  NONCE_ACTION_SEND_DOCUMENT = 'ksef_send_document_action';
	public const  NONCE_ACTION_CHECK_STATUS  = 'ksef_check_status_action';
	private const NONCE_FIELD_NAME           = '_ajax_nonce';

	private PluginSettings $settings;

	private KSeFRenderer $renderer;

	private AjaxResponse $ajax_response;

	private DocumentService $document_service;

	private EncryptionService $encryption_service;

	private DocumentStateRepository $document_ksef_state_repository;

	private XMLGeneratorService $xml_generator_service;

	private DownloadXMLUrlGenerator $download_xml_url_generator;

	public function __construct(
		PluginSettings $settings,
		KSeFRenderer $renderer,
		AjaxResponse $ajax_response,
		DocumentService $document_service,
		EncryptionService $encryption_service,
		DocumentStateRepository $document_ksef_state_repository,
		XMLGeneratorService $xml_generator_service,
		DownloadXMLUrlGenerator $download_xml_url_generator
	) {
		$this->download_xml_url_generator     = $download_xml_url_generator;
		$this->xml_generator_service          = $xml_generator_service;
		$this->document_ksef_state_repository = $document_ksef_state_repository;
		$this->encryption_service             = $encryption_service;
		$this->settings                       = $settings;
		$this->renderer                       = $renderer;
		$this->ajax_response                  = $ajax_response;
		$this->document_service               = $document_service;
	}

	public function hooks() {
		add_action( 'wp_ajax_fiksef_send_document_to_ksef', [ $this, 'ajax_init_document_in_ksef' ] );
		add_action( 'wp_ajax_fiksef_check_document_status', [ $this, 'ajax_refresh_document_status' ] );
		if ( $this->settings->is_auto_sending_enabled() ) {
			add_action( 'fi/core/document/save', [ $this, 'send_document_to_ksef_by_id' ], 10, 3 );
			add_action( 'fiksef_retry_document_sending', [ $this, 'send_document_to_ksef_by_id' ], 10, 3 );
		}
	}

	public function send_document_to_ksef_by_id( $document, $meta, $document_id ) {
		$state = $this->document_ksef_state_repository->get( $document_id );

		do_action( 'fiksef_services_before_auto_document_sending', $document, $meta, $state );

		if ( $state->is_success() ) {
			return;
		}

		try {
			$state->clear_errors();

			$document_string = $this->xml_generator_service->generate_for_document_id( (int) $document_id );

			$verification_key = $this->encryption_service->generate_verification_key( $document_string );
			$state->set_verification_key( $verification_key );
			$this->document_ksef_state_repository->save( $document_id, $state );

			$response                  = $this->document_service->send_document( $document_string );
			$document_reference_number = $response['document_ref'];
			$session_reference_number  = $response['session_ref'];

			$status_response = $this->document_service->get_invoice_status( $session_reference_number, $document_reference_number );

			$processing_code = (int) ( $status_response['status']['code'] ?? 400 );

			for ( $i = 0; $i < 6; $i++ ) {
				switch ( $processing_code ) {
					case 100:
					case 150:
						sleep( 2 );
						$status_response = $this->document_service->get_invoice_status( $session_reference_number, $document_reference_number );
						$processing_code = (int) ( $status_response['status']['code'] ?? 400 );
						break;
					case 200:
						$ksef_ref = $status_response['referenceNumber'] ?? $document_reference_number;
						$state->mark_as_success( $ksef_ref );
						$state->set_raw_response( $status_response );
						$this->document_ksef_state_repository->save( $document_id, $state );
						break 2;
					default:
						$error_message = implode( ', ', $status_response['status']['details'] ?? [] );
						$state->add_error( $error_message );
						$this->document_ksef_state_repository->save( $document_id, $state );
						break 2;
				}
			}
		} catch ( KSeFNetworkException $ne ) {
			$state->clear_errors();
			$state->add_error( $ne->getMessage() );
			$state->set_network_error( true );
			$this->document_ksef_state_repository->save( $document_id, $state );
			return;
		} catch ( KSeFAPIException $ke ) {
			$state->clear_errors();
			foreach ( $ke->get_structured_errors() as $structured_error ) {
				$state->add_error( $structured_error['full_message'] );
			}
			$state->set_network_error( false );
			$this->document_ksef_state_repository->save( $document_id, $state );
			return;
		} catch ( Exception $e ) {
			$state->clear_errors();
			$state->add_error( $e->getMessage() );
			$state->set_network_error( true );
			$this->document_ksef_state_repository->save( $document_id, $state );
			return;
		}

		do_action( 'fiksef_services_after_auto_document_sending', $document, $meta );
	}

	/**
	 * Handles AJAX request to refresh KSeF document status.
	 */
	public function ajax_refresh_document_status() {

		check_ajax_referer( self::NONCE_ACTION_CHECK_STATUS, self::NONCE_FIELD_NAME );

		$document_reference_number = isset( $_POST['documentReferenceNumber'] ) ? sanitize_text_field( wp_unslash( $_POST['documentReferenceNumber'] ) ) : null;
		$session_reference_number  = isset( $_POST['sessionReferenceNumber'] ) ? sanitize_text_field( wp_unslash( $_POST['sessionReferenceNumber'] ) ) : null;
		$document_id               = isset( $_POST['documentId'] ) ? absint( $_POST['documentId'] ) : 0;
		$attempt                   = isset( $_POST['attempt'] ) ? absint( $_POST['attempt'] ) : 0;

		if ( ! $document_reference_number || ! $document_id || ! $session_reference_number ) {
			$this->ajax_response->send_error(
				$this->renderer->render_cannot_send_document( [ esc_html__( 'Missing required data.', 'ksef-for-flexible-invoices' ) ], esc_html__( 'Failed to refresh document status', 'ksef-for-flexible-invoices' ) ) .
				$this->renderer->render_metabox_action_buttons( $document_id, $this->download_xml_url_generator->generate_by_id( $document_id ) )
			);
			return;
		}

		try {
			$status_response = $this->document_service->get_invoice_status( $session_reference_number, $document_reference_number );

			$this->process_and_send_status_response( $status_response, $attempt, $document_id );
		} catch ( Exception $e ) {
			$this->ajax_response->send_error(
				$this->renderer->render_cannot_send_document( [ $e->getMessage() ], esc_html__( 'Failed to refresh document status', 'ksef-for-flexible-invoices' ) ) .
				$this->renderer->render_metabox_action_buttons( $document_id, $this->download_xml_url_generator->generate_by_id( $document_id ) )
			);
		}
	}

	private function process_and_send_status_response( array $status_data, int $attempt, int $document_id ): void { //phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
		$processing_code = (int) ( $status_data['status']['code'] ?? 400 );

		$state = $this->document_ksef_state_repository->get( $document_id );
		switch ( $processing_code ) {
			case 100:
			case 150:
				// Awaiting
				$this->ajax_response->send_awaiting(
					$this->renderer->render_ksef_document_status(
						esc_html( $status_data['status']['description'] ?? '' ),
						(string) $attempt
					),
					[ 'raw_status' => $status_data ]
				);
				break;
			case 200: // Success
				$ksef_ref_num = $status_data['referenceNumber'] ?? '';

				$state->mark_as_success( $ksef_ref_num );
				$state->set_raw_response( $status_data );
				$this->document_ksef_state_repository->save( $document_id, $state );

				$this->ajax_response->send_success(
					$this->renderer->render_document_sent_to_ksef( $ksef_ref_num ),
					[
						'raw_status'          => $status_data,
						'ksefReferenceNumber' => $ksef_ref_num,
					]
				);
				break;
			case 440: // Faktura istnieje już w ksef
				$ksef_ref_num = $status_data['status']['extensions']['originalKsefNumber'] ?? '';

				$state->mark_as_success( $ksef_ref_num );
				$state->set_raw_response( $status_data );
				$this->document_ksef_state_repository->save( $document_id, $state );

				$this->ajax_response->send_success(
					$this->renderer->render_document_already_exists_in_ksef( $ksef_ref_num ),
					[
						'raw_status'          => $status_data,
						'ksefReferenceNumber' => $ksef_ref_num,
					]
				);
				break;
			case 405: // Przetwarzanie anulowane
			case 410: // Nieprawidłowy zakres uprawnień
			case 415: // Brak możliwości wysyłania faktury z załącznikiem
			case 430: // Błąd weryfikacji pliku faktury
			case 435: // Błąd odszyfrowania pliku
			case 450: // Błąd weryfikacji semantyki dokumentu faktury
			case 500: // Nieznany błąd
			default: // Error or other status
				$error_message = implode( ', ', $status_data['status']['details'] ?? [] );
				if ( empty( $error_message ) ) {
					$error_message = $status_data['status']['description'] ?? __( 'Unknown KSeF error', 'ksef-for-flexible-invoices' );
				}
				$state->clear_errors();
				$state->add_error( $error_message );
				$this->document_ksef_state_repository->save( $document_id, $state );

				$this->ajax_response->send_error(
					$this->renderer->render_cannot_send_document( [ esc_html( $error_message ) ], esc_html__( 'Failed to send document', 'ksef-for-flexible-invoices' ) ) .
					$this->renderer->render_metabox_action_buttons( $document_id, $this->download_xml_url_generator->generate_by_id( $document_id ) ),
					[ 'raw_status' => $status_data ]
				);
		}
	}

	public function ajax_init_document_in_ksef() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( esc_html__( 'Permission denied', 'ksef-for-flexible-invoices' ), 403 );
		}
		check_ajax_referer( self::NONCE_ACTION_SEND_DOCUMENT, self::NONCE_FIELD_NAME );

		$document_id = isset( $_REQUEST['documentId'] ) ? absint( $_REQUEST['documentId'] ) : 0;

		if ( ! $document_id ) {
			$this->ajax_response->send_error(
				$this->renderer->render_cannot_send_document( [ esc_html__( 'Document ID is missing.', 'ksef-for-flexible-invoices' ) ] ) .
				$this->renderer->render_metabox_action_buttons( $document_id, $this->download_xml_url_generator->generate_by_id( $document_id ) )
			);

			return;
		}

		$state = $this->document_ksef_state_repository->get( $document_id );

		if ( $state->is_success() ) {
			$this->ajax_response->send_already_archived(
				$this->renderer->render_document_sent_to_ksef( esc_html( $state->get_ksef_number() ) )
			);

			return;
		}

		$state->clear_errors();

		try {
			$document_string = $this->xml_generator_service->generate_for_document_id( (int) $document_id );

			$verification_key = $this->encryption_service->generate_verification_key( $document_string );
			$state->set_verification_key( $verification_key );
			$this->document_ksef_state_repository->save( $document_id, $state );

			$response = $this->document_service->send_document( $document_string );

			$document_reference_number = $response['document_ref'];
			$session_reference_number  = $response['session_ref'];

			$this->ajax_response->send_awaiting(
				$this->renderer->render_ksef_document_status( '', '0' ),
				[
					'documentReferenceNumber' => $document_reference_number,
					'sessionReferenceNumber'  => $session_reference_number,
				]
			);

		} catch ( KSeFNetworkException $ne ) {
			$state->add_error( $ne->getMessage() );
			$state->set_network_error( true );
			$this->document_ksef_state_repository->save( $document_id, $state );

			$this->ajax_response->send_error(
				$this->renderer->render_cannot_send_document( [ $ne->getMessage() ], esc_html__( 'Network error', 'ksef-for-flexible-invoices' ) ) .
				$this->renderer->render_metabox_action_buttons( $document_id, $this->download_xml_url_generator->generate_by_id( $document_id ) )
			);
		} catch ( KSeFAPIException $ke ) {
			foreach ( $ke->get_structured_errors() as $structured_error ) {
				$state->add_error( $structured_error['full_message'] );
			}

			$state->set_network_error( false );
			$this->document_ksef_state_repository->save( $document_id, $state );

			$this->ajax_response->send_error(
				$this->renderer->render_cannot_send_document( $state->get_error_messages(), esc_html__( 'API returned errors', 'ksef-for-flexible-invoices' ) ) .
				$this->renderer->render_metabox_action_buttons( $document_id, $this->download_xml_url_generator->generate_by_id( $document_id ) )
			);
		} catch ( Exception $e ) {
			$state->add_error( $e->getMessage() );
			$state->set_network_error( true );
			$this->document_ksef_state_repository->save( $document_id, $state );

			$this->ajax_response->send_error(
				$this->renderer->render_cannot_send_document( [ $e->getMessage() ] ) .
				$this->renderer->render_metabox_action_buttons( $document_id, $this->download_xml_url_generator->generate_by_id( $document_id ) )
			);
		}
	}
}
