<?php

namespace WPDesk\FakturyKSEF\Xml\Mapper\FA3;

use DateTime;
use DateTimeZone;
use LogicException;
use WPDesk\CoreAlias\DocumentSeller;
use WPDesk\CoreAlias\InvoicesIntegration;
use WPDesk\FakturyKSEF\Helpers\Invoice;
use WPDesk\FakturyKSEF\Xml\ArrayToXmlConverter;
use WPDesk\FakturyKSEF\Xml\DataMapper;
use WPDesk\FakturyKSEF\Xml\DocumentData\DocumentData;

class VatDataMapper {

	protected const FORM_CODE      = 'FA';
	protected const SYSTEM_CODE    = 'FA (3)';
	protected const SCHEMA_VERSION = '1-0E';
	protected const SYSTEM_INFO    = 'WPDesk Faktury WooCommerce';
	protected const COUNTRY_SLUG   = 'PL';
	protected const VAT_PREFIXES   = [
		'AT',
		'BE',
		'BG',
		'CY',
		'CZ',
		'DE',
		'DK',
		'EE',
		'EL',
		'ES',
		'FI',
		'FR',
		'GB',
		'HR',
		'HU',
		'IE',
		'IT',
		'LT',
		'LU',
		'LV',
		'MT',
		'NL',
		'PL',
		'PT',
		'RO',
		'SE',
		'SI',
		'SK',
		'XI',
		'CH',
		'NO',
		'IS',
		'LI',
		'GR',
	];

	public function map( DocumentData $document ): array {
		$faktura = [
			ArrayToXmlConverter::ATTRIBUTES_KEY => [
				'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
				'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
			],
			'Naglowek'                          => $this->map_header(),
			'Podmiot1'                          => $this->map_entity1( $document ),
			'Podmiot2'                          => $this->map_entity2( $document ),
		];

		if ( ! empty( trim( $document->get_recipient()->get_name() ) ) ) {
			$faktura['Podmiot3'] = $this->map_entity3( $document );
		}

		$faktura['Fa'] = $this->map_fa( $document );

		$footer = $this->map_footer( $document );
		if ( ! empty( $footer ) ) {
			$faktura['Stopka'] = $footer;
		}

		return apply_filters( 'fiksef_xml_data', [ 'Faktura' => $faktura ], $document );
	}

	protected function map_header(): array {
		$now = new DateTime( 'now', new DateTimeZone( 'UTC' ) );

		return [
			'KodFormularza'     => [
				ArrayToXmlConverter::ATTRIBUTES_KEY => [
					'kodSystemowy' => self::SYSTEM_CODE,
					'wersjaSchemy' => self::SCHEMA_VERSION,
				],
				ArrayToXmlConverter::VALUE_KEY      => self::FORM_CODE,
			],
			'WariantFormularza' => '3',
			'DataWytworzeniaFa' => $now->format( 'Y-m-d\TH:i:s\Z' ),
			'SystemInfo'        => self::SYSTEM_INFO,
		];
	}

	protected function map_entity1( DocumentData $document ): array {
		$vat_number   = $document->get_seller()->get_vat_number();
		$company_name = $document->get_seller()->get_name();
		$address      = $document->get_seller()->get_address() . ' ';

		return [
			'DaneIdentyfikacyjne' => [
				'NIP'   => $vat_number,
				'Nazwa' => $company_name,
			],
			'Adres'               => [
				'KodKraju' => self::COUNTRY_SLUG,
				'AdresL1'  => $address,
			],
		];
	}

	protected function map_entity2( DocumentData $document ): array {
		$vat_number   = $document->get_customer()->get_vat_number();
		$company_name = $document->get_customer()->get_name();
		$country      = $document->get_customer()->get_country();
		$address      = $document->get_customer()->get_street() . ' ';
		$address     .= $document->get_customer()->get_street2() ? $document->get_customer()->get_street2() . ' ' : '';
		$address     .= $document->get_customer()->get_city() . ' ';
		$address     .= $document->get_customer()->get_postcode();

		$id_data = $this->get_entity_id_data( $vat_number, $country, $company_name );

		return [
			'DaneIdentyfikacyjne' => $id_data,
			'Adres'               => [
				'KodKraju' => $country,
				'AdresL1'  => $address,
			],
			'JST'                 => '2',
			'GV'                  => '2',
		];
	}

	protected function map_entity3( DocumentData $document ): array {
		$vat_number = $document->get_recipient()->get_vat_number();
		$name       = $document->get_recipient()->get_name();
		$country    = $document->get_recipient()->get_country();
		$address    = $document->get_recipient()->get_street() . ' ';
		$address   .= $document->get_recipient()->get_street2() ? $document->get_recipient()->get_street2() . ' ' : '';
		$address   .= $document->get_recipient()->get_city() . ' ';
		$address   .= $document->get_recipient()->get_postcode();

		$id_data = $this->get_entity_id_data( $vat_number, $country, $name );

		return [
			'DaneIdentyfikacyjne' => $id_data,
			'Adres'               => [
				'KodKraju' => $country,
				'AdresL1'  => $address,
			],
			'Rola'                => apply_filters( 'fiksef_xmldata_entity3_role', '2', $document ),
		];
	}

	protected function map_fa( DocumentData $document ): array {
		$date_issue = $document->get_date_of_issue();
		$date_sale  = $document->get_date_of_sale();

		$fa = [
			'KodWaluty' => $document->get_currency(),
			'P_1'       => $date_issue,
			'P_2'       => $document->get_formatted_number(),
		];

		if ( ! empty( $date_sale ) && $date_issue !== $date_sale ) {
			$fa['P_6'] = $date_sale;
		}

		$fa = array_merge( $fa, $this->map_taxes_sums( $document->get_totals_by_taxes() ) );

		$fa['P_15']          = Invoice::string_as_money_no_currency( $document->get_total_gross() );
		$fa['Adnotacje']     = $this->map_annotations( $document, $document->get_totals_by_taxes() );
		$fa['RodzajFaktury'] = 'VAT';

		$fa['FaWiersz'] = $this->map_items( $document->get_items() );
		$fa['Platnosc'] = $this->map_payment_info( $document );

		$transaction_conditions = $this->map_transaction_conditions( $document );
		if ( ! empty( $transaction_conditions ) ) {
			$fa['WarunkiTransakcji'] = $transaction_conditions;
		}

		return $fa;
	}

	protected function map_taxes_sums( array $totals_by_taxes ): array {
		$sums = array_merge(
			$this->map_standard_rates_sums( $totals_by_taxes ),
			$this->map_zero_rates_sums( $totals_by_taxes ),
			$this->map_other_rates_sums( $totals_by_taxes )
		);

		return $sums;
	}

	private function map_standard_rates_sums( array $totals_by_taxes ): array {
		$sums = [];
		if ( isset( $totals_by_taxes['23'] ) ) {
			$sums['P_13_1'] = Invoice::string_as_money_no_currency( $totals_by_taxes['23']['total_net'] );
			$sums['P_14_1'] = Invoice::string_as_money_no_currency( $totals_by_taxes['23']['total_vat'] );
		}
		if ( isset( $totals_by_taxes['8'] ) ) {
			$sums['P_13_2'] = Invoice::string_as_money_no_currency( $totals_by_taxes['8']['total_net'] );
			$sums['P_14_2'] = Invoice::string_as_money_no_currency( $totals_by_taxes['8']['total_vat'] );
		}
		if ( isset( $totals_by_taxes['5'] ) ) {
			$sums['P_13_3'] = Invoice::string_as_money_no_currency( $totals_by_taxes['5']['total_net'] );
			$sums['P_14_3'] = Invoice::string_as_money_no_currency( $totals_by_taxes['5']['total_vat'] );
		}
		return $sums;
	}

	private function map_zero_rates_sums( array $totals_by_taxes ): array {
		$sums    = [];
		$kr_zero = $totals_by_taxes[ DataMapper::KR_ZERO_RATE ] ?? ( $totals_by_taxes['zero_rate_kr'] ?? null );
		if ( $kr_zero ) {
			$sums['P_13_6_1'] = Invoice::string_as_money_no_currency( $kr_zero['total_net'] );
		}

		$wdt_zero = $totals_by_taxes[ DataMapper::WDT_ZERO_RATE ] ?? ( $totals_by_taxes['zero_rate_wdt'] ?? null );
		if ( $wdt_zero ) {
			$sums['P_13_6_2'] = Invoice::string_as_money_no_currency( $wdt_zero['total_net'] );
		}

		$ex_zero = $totals_by_taxes[ DataMapper::EX_ZERO_RATE ] ?? ( $totals_by_taxes['zero_rate_ex'] ?? null );
		if ( $ex_zero ) {
			$sums['P_13_6_3'] = Invoice::string_as_money_no_currency( $ex_zero['total_net'] );
		}
		return $sums;
	}

	private function map_other_rates_sums( array $totals_by_taxes ): array {
		$sums = [];
		if ( isset( $totals_by_taxes['zw'] ) ) {
			$sums['P_13_7'] = Invoice::string_as_money_no_currency( $totals_by_taxes['zw']['total_net'] );
		}
		if ( isset( $totals_by_taxes['np I'] ) ) {
			$sums['P_13_8'] = Invoice::string_as_money_no_currency( $totals_by_taxes['np I']['total_net'] );
		}
		if ( isset( $totals_by_taxes['np II'] ) ) {
			$sums['P_13_9'] = Invoice::string_as_money_no_currency( $totals_by_taxes['np II']['total_net'] );
		}

		return $sums;
	}

	protected function map_annotations( DocumentData $document, array $totals_by_taxes ): array {
		$annotations = [
			'P_16'  => '2', // metoda kasowa
			'P_17'  => '2', // samofakturowanie
			'P_18'  => '2', // reverse charge
			'P_18A' => '2', // split payment
		];

		if ( isset( $totals_by_taxes['zw'] ) ) {
			$annotations['Zwolnienie'] = [
				'P_19'  => '1',
				'P_19C' => $document->get_exempt_reason(),
			];
		} else {
			$annotations['Zwolnienie'] = [
				'P_19N' => '1',
			];
		}

		$annotations['NoweSrodkiTransportu'] = [
			'P_22N' => '1',
		];

		$annotations['P_23'] = '2'; // procedura uproszczona

		$annotations['PMarzy'] = [
			'P_PMarzyN' => '1',
		];

		return $annotations;
	}

	protected function map_items( array $items ): array {
		$mapped_items = [];
		foreach ( $items as $id => $item ) {
			$invoice_line = [
				'NrWierszaFa' => (string) ( $id + 1 ),
				'P_7'         => $item['name'],
				'P_8A'        => str_replace( '.', '', $item['unit'] ),
				'P_8B'        => $item['quantity'],
				'P_9A'        => Invoice::string_as_money_no_currency( $item['net_price'] ),
			];

			if ( ! empty( $item['discount'] ) ) {
				$discount             = str_replace( ',', '.', trim( $item['discount'] ) );
				$invoice_line['P_10'] = Invoice::string_as_money_no_currency( (float) $discount );
			}

			$invoice_line['P_11'] = Invoice::string_as_money_no_currency( $item['net_price_sum'] );
			$invoice_line['P_12'] = $item['vat_type_ksef'];

			$mapped_items[] = $invoice_line;
		}

		return $mapped_items;
	}

	protected function map_payment_info( DocumentData $document ): array {
		$payment_ns = [];
		$is_paid    = $document->get_payment_status() === 'paid';

		if ( $is_paid ) {
			$date_paid                 = new DateTime( $document->get_date_of_pay() ?: 'now' );
			$payment_ns['Zaplacono']   = '1';
			$payment_ns['DataZaplaty'] = $date_paid->format( 'Y-m-d' );
		} else {
			$payment_ns['TerminPlatnosci'] = [
				'Termin' => $document->get_date_of_pay(),
			];
		}

		$payment_method               = ( new DataMapper() )->get_payment_method( $document->get_payment_method() );
		$payment_ns['FormaPlatnosci'] = $payment_method;

		if ( (int) $payment_method === 6 ) {
			$bank_account_nr = $document->get_seller()->get_bank_account_number();
			$bank_account_nr = str_replace( ' ', '', $bank_account_nr );

			if ( ! empty( $bank_account_nr ) ) {
				$payment_ns['RachunekBankowy'] = [
					'NrRB' => $bank_account_nr,
				];
			}
		}

		return $payment_ns;
	}

	protected function map_transaction_conditions( DocumentData $document ): array {
		$order_number = (string) $document->get_order_number();
		if ( ! empty( $order_number ) ) {
			return [
				'Zamowienia' => [
					'NrZamowienia' => $order_number,
				],
			];
		}
		return [];
	}

	protected function map_footer( DocumentData $document ): array {
		if ( function_exists( 'apply_filters' ) ) {
			$info = apply_filters( 'fiksef_xmldata_footer', $document->get_notes(), $document );
		} else {
			$info = $document->get_notes();
		}

		if ( ! empty( $info ) ) {
			return [
				'Informacje' => [
					'StopkaFaktury' => $info,
				],
			];
		}

		return [];
	}

	protected function clean_vat_number( string $vat_number ): string {
		$vat_number = strtoupper( trim( str_replace( ' ', '', $vat_number ) ) );

		foreach ( self::VAT_PREFIXES as $prefix ) {
			if ( strpos( $vat_number, $prefix ) === 0 ) {
				$vat_number = substr( $vat_number, strlen( $prefix ) );
				break;
			}
		}

		return $vat_number;
	}

	protected function get_entity_id_data( $vat_number, $country, $name ): array {
		$id_data = [];

		if ( $vat_number ) {
			if ( $country === 'PL' ) {
				$id_data['NIP'] = $this->clean_vat_number( $vat_number );
			} else {
				if ( ! InvoicesIntegration::is_pro() ) {
					$purchase_url = function_exists( 'get_locale' ) && get_locale() === 'pl_PL' ? 'https://wpdesk.link/faktury-woocomemrce-pro-ksef' : 'https://wpdesk.link/flexible-invoices-pro';
					throw new LogicException(
						sprintf(
						// translators: 1. Open link, 2. Close link
							esc_html__( 'You can send foreign invoices with %1$sFlexible Invoices PRO →%2$s', 'ksef-for-flexible-invoices' ),
							'<a href="' . $purchase_url . '" target="_blank">',
							'</a>'
						)
					);
				}

				if ( function_exists( 'WC' ) && isset( WC()->countries ) ) {
					$is_ue_country = in_array( $country, WC()->countries->get_european_union_countries(), true );
				} else {
					$is_ue_country = false;
				}

				if ( $is_ue_country ) {
					$id_data['KodUE']   = $country;
					$id_data['NrVatUE'] = $this->clean_vat_number( $vat_number );
				} else {
					$id_data['NrID'] = $vat_number;
				}
			}
		} else {
			$id_data['BrakID'] = '1';
		}

		$id_data['Nazwa'] = $name;
		return $id_data;
	}
}
