// Questo file fa parte di MyTown.
// MyTown è software libero: puoi ridistribuirlo e/o modificarlo secondo
// i termini della GNU Affero General Public License come pubblicata dalla
// Free Software Foundation, versione 3.
// Distribuito senza ALCUNA GARANZIA; vedi la GNU AGPL v3 per i dettagli.
// Copyright (C) Kondor Consulting & Services S.r.l.
// SPDX-License-Identifier: AGPL-3.0-or-later

import { Component, NgZone } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription, forkJoin } from 'rxjs';
import { ActionSheetController, ModalController } from '@ionic/angular';
import { loadScript } from '@paypal/paypal-js';
import { ConfigService } from '../../providers/config.service';
import { BookingService } from '../../providers/booking.service';
import { PagamentoService } from '../../providers/pagamento.service';
import { RiscossioneService } from '../../providers/riscossione.service';
import { LayoutService } from '../../providers/layout.service';
import { FatturaService } from '../../providers/fattura.service';
import { Booking, BookingItem, BookingMenuCategory, BookingMenuItem, BookingPayment } from '../../models/booking.model';
import { ActivePaymentConfig, PaymentRequest, PayType } from '../../models/payment.model';
import { DatiFiscali, ModuloFattura, RichiestaFatturaPayload, RigaFattura, datiFiscaliCompleti } from '../../models/fattura.model';
import { ModalInvoiceRequestComponent } from '../../components/modal-invoice-request/modal-invoice-request.component';

declare var XPay: any;

@Component({
	selector: 'app-dettaglio-prenotazione',
	templateUrl: 'dettaglio-prenotazione.page.html',
	styleUrls: ['dettaglio-prenotazione.page.scss'],
	standalone: false,
})
export class DettaglioPrenotazionePage {
	cmp_id = 0;
	prt_id = 0;
	bkb_id = 0;
	selectedTab = 'dettaglio';

	booking: Booking | null = null;
	resourceType = '';
	resourceTypeId = 0;
	items: BookingItem[] = [];
	payments: BookingPayment[] = [];

	// Menu
	menuCategories: BookingMenuCategory[] = [];
	menuItemsByCategory: Record<number, BookingMenuItem[]> = {};
	collapsedCategories: Record<number, boolean> = {};
	showCartModal = false;
	showHistoryModal = false;

	paymentForm: Partial<BookingPayment> = {};

	// ── Richiesta fattura (checkout) ─────────────────────────────────
	requestInvoice = false;
	datiFiscali: DatiFiscali | null = null;

	showPaymentModal = false;          // Dialog 1 — registra pagamento (items + amount + note)
	showCheckoutModal = false;         // Dialog 2 — checkout (CASH / NEXI / PAYPAL)
	checkoutAmount = 0;
	checkoutType: 'BOOKING' | 'ORDER' = 'BOOKING';
	showPaymentHistory = false;
	paymentItemSelected: boolean[] = [];
	paymentItemQty: number[] = [];

	// ── Checkout state (replica CheckoutComponent web) ───────────────
	paymentTypes: PayType[] = [];
	selectedTypeCode = '';
	view_paypal = false;
	click_pay = false;
	nexiScriptReady = false;
	activeConfigs: ActivePaymentConfig[] = [];
	configsForSelectedType: ActivePaymentConfig[] = [];
	selectedPccId = 0;

	// ── Riscossione NFC (manager) ────────────────────────────────────
	/** Solo per utenti ADMIN della company → vede il pulsante "Riscuoti via NFC" */
	isManager = false;
	/** True quando il modale "Avvicina la carta…" è aperto */
	showNfcModal = false;
	/** Stato corrente del flusso NFC, per messaggio in UI */
	nfcStatus: 'waiting' | 'detected' | 'saving' | 'done' | 'error' | 'unsupported' = 'waiting';
	nfcMessage = '';

	private paramsSubscription!: Subscription;
	private querySubscription!: Subscription;

	constructor(
		private activatedRoute: ActivatedRoute,
		private actionSheetCtrl: ActionSheetController,
		public configService: ConfigService,
		private bookingService: BookingService,
		private pagamentoService: PagamentoService,
		private riscossioneService: RiscossioneService,
		private layoutService: LayoutService,
		private ngZone: NgZone,
		private modalCtrl: ModalController,
		private fatturaService: FatturaService,
	) {}

	ngOnInit() {
		this.paramsSubscription = this.activatedRoute.params.subscribe((params: Record<string, string>) => {
			if (params['cmp_id']) this.cmp_id = +params['cmp_id'];
			if (params['prt_id']) this.prt_id = +params['prt_id'];
			if (params['bkb_id']) this.bkb_id = +params['bkb_id'];
		});
		this.querySubscription = this.activatedRoute.queryParams.subscribe((qp: Record<string, string>) => {
			if (qp['tab']) this.selectedTab = qp['tab'];
		});
	}

	ngOnDestroy() {
		this.paramsSubscription?.unsubscribe();
		this.querySubscription?.unsubscribe();
	}

	ionViewWillEnter() {
		this.layoutService.setPageConfig({
			showBack: true,
			backButtonHref: `/portale/${this.prt_id}/company/${this.cmp_id}/service/prenotazioni`,
		});
		this.isManager = this.configService.isUserAuthorizedAs('ADMIN');
		this.loadBooking();
		this.loadCompanyPaymentConfig();
		if (this.selectedTab === 'ordine') this.loadOrderTab();
		if (this.selectedTab === 'cassa')  { this.loadPayments(); this.loadItems(); }
	}

	/** Pre-carica le config di pagamento attive della company (per abilitare/nascondere i pulsanti) */
	loadCompanyPaymentConfig() {
		if (this.cmp_id <= 0) return;
		this.pagamentoService.getActiveConfigsByCompany(this.cmp_id).subscribe({
			next: (cfgs) => { this.activeConfigs = cfgs || []; },
			error: () => { this.activeConfigs = []; },
		});
	}

	/**
	 * Lista filtrata dei paymentTypes da mostrare nel checkout:
	 *   - CASH: sempre incluso (i contanti non richiedono configurazione)
	 *   - NEXI / PAYPAL / altri: solo se la company ha una activeConfig per quel provider
	 *
	 * Il match è case-sensitive su `pcc_provider === pt_code`.
	 */
	get filteredPaymentTypes(): PayType[] {
		return (this.paymentTypes || []).filter(pt => {
			if (pt.pt_code === 'CASH') return true;
			return this.activeConfigs.some(c => c.pcc_provider === pt.pt_code);
		});
	}

	/**
	 * NFC visibile solo se:
	 *   - utente è manager (ADMIN)
	 *   - la company ha una activeConfig con provider 'SUMUP' (o 'NFC')
	 *     → quando in futuro arriverà l'integrazione SumUp Tap to Pay reale,
	 *       la stessa config abiliterà il flusso production. Per ora il manager
	 *       può aggiungere una config SUMUP fittizia per attivare il mock.
	 */
	get canUseNfc(): boolean {
		if (!this.isManager) return false;
		return this.activeConfigs.some(c => c.pcc_provider === 'SUMUP' || c.pcc_provider === 'NFC');
	}

	ionViewWillLeave() {
		// Cleanup NFC nel caso il manager esca col modale ancora aperto
		this.riscossioneService.dispose();
	}

	// ── Caricamento prenotazione + tipo risorsa ───────────────
	loadBooking() {
		this.bookingService.GetBookingById(this.bkb_id).subscribe({
			next: (res) => {
				this.booking = res;
				if (res.bkrt_type) this.resourceType = res.bkrt_type;
				this.bookingService.GetResourceTypes(this.cmp_id).subscribe({
					next: (types) => {
						if (!types?.length) return;
						// 1) Se il backend ha restituito bkrt_type, prendi il tipo direttamente
						if (this.resourceType) {
							const t = types.find(x => x.bkrt_type === this.resourceType);
							if (t) {
								this.resourceTypeId = t.bkrt_id;
								if (this.selectedTab === 'ordine' && this.menuCategories.length === 0) this.loadMenu();
								return;
							}
						}
						// 2) Fallback: deduci dal bkb_resource_id facendo il giro su tutte le risorse
						if (!res.bkb_resource_id) return;
						forkJoin(types.map(t => this.bookingService.GetResources(this.cmp_id, t.bkrt_id))).subscribe({
							next: (results) => {
								for (let i = 0; i < results.length; i++) {
									const found = (results[i] || []).find(x => x.bkr_id === res.bkb_resource_id);
									if (found) {
										this.resourceType = types[i].bkrt_type;
										this.resourceTypeId = types[i].bkrt_id;
										if (this.selectedTab === 'ordine' && this.menuCategories.length === 0) this.loadMenu();
										break;
									}
								}
							},
							error: () => {},
						});
					},
					error: () => {},
				});
			},
			error: (err) => this.configService.presentToastError(err),
		});
	}

	onTabChange() {
		if (this.selectedTab === 'ordine') this.loadOrderTab();
		if (this.selectedTab === 'cassa') { this.loadPayments(); this.loadItems(); }
	}

	// ── Ordini già salvati ────────────────────────────────────
	loadItems() {
		this.bookingService.GetBookingItems(this.bkb_id).subscribe({
			next: (res) => (this.items = res || []),
			error: (err) => this.configService.presentToastError(err),
		});
	}

	// ── Tab Ordine: articoli salvati + catalogo menu ──────────
	loadOrderTab() {
		this.loadItems();
		if (this.menuCategories.length === 0) this.loadMenu();
	}

	loadMenu() {
		// Replica logica portale Angular: carica categorie da TUTTI i type della company
		// e concatena. Le categorie non sono necessariamente legate al type del booking.
		this.bookingService.GetResourceTypes(this.cmp_id).subscribe({
			next: (types) => {
				const activeTypes = (types || []).filter(t => t.bkrt_is_active);
				if (!activeTypes.length) return;
				forkJoin(activeTypes.map(t => this.bookingService.GetMenuCategories(this.cmp_id, t.bkrt_id))).subscribe({
					next: (catResults) => {
						const allCategories = catResults.flat().filter(c => c.bkmc_is_active);
						if (!allCategories.length) return;
						forkJoin(allCategories.map(c => this.bookingService.GetMenuItems(this.cmp_id, c.bkmc_id))).subscribe({
							next: (itemResults) => {
								this.menuCategories = allCategories;
								this.menuItemsByCategory = {};
								allCategories.forEach((cat, i) => {
									this.menuItemsByCategory[cat.bkmc_id] = (itemResults[i] || [])
										.filter(item => item.bkmi_is_active)
										.map(item => ({ ...item, selectedQuantity: 1 }));
								});
							},
							error: (err) => this.configService.presentToastError(err),
						});
					},
					error: (err) => this.configService.presentToastError(err),
				});
			},
			error: (err) => this.configService.presentToastError(err),
		});
	}

	toggleCategory(id: number) {
		this.collapsedCategories[id] = !this.collapsedCategories[id];
	}

	// ── Stato articoli (via bkbi_notes) ──────────────────────
	get cartItems(): BookingItem[] {
		return this.items.filter(i => i.bkbi_notes === 'CART');
	}

	/** Storico ordini inviati (esclude le voci ancora nel carrello) */
	get orderedItems(): BookingItem[] {
		return this.items.filter(i => i.bkbi_notes !== 'CART');
	}

	/** Tutte le voci menu collegate al booking — usato in cassa (replica web orderItems) */
	get allOrderItems(): BookingItem[] {
		return this.items;
	}

	/** Split paid/unpaid: parsa le note dei pagamenti ORDER (\x01items\x01...\x01\x01) */
	private _computeOrderSplit(): { paid: BookingItem[]; unpaid: BookingItem[] } {
		const orderPayments = this.paymentsOrder;
		if (orderPayments.length === 0) {
			return { paid: [], unpaid: [...this.allOrderItems] };
		}
		const paidQtyMap: { [id: number]: number } = {};
		let hasItemData = false;
		for (const pay of orderPayments) {
			const match = (pay.bkp_notes || '').match(/^\x01items\x01(.*?)\x01\x01/s);
			if (!match) continue;
			try {
				const entries: { id: number; qty: number }[] = JSON.parse(match[1]);
				hasItemData = true;
				for (const e of entries) paidQtyMap[e.id] = (paidQtyMap[e.id] ?? 0) + e.qty;
			} catch {}
		}
		if (!hasItemData) {
			return { paid: [], unpaid: [...this.allOrderItems] };
		}
		const paid: BookingItem[] = [];
		const unpaid: BookingItem[] = [];
		for (const item of this.allOrderItems) {
			const qtyPaid = Math.min(paidQtyMap[item.bkbi_id] ?? 0, item.bkbi_quantity);
			const qtyUnpaid = item.bkbi_quantity - qtyPaid;
			const unitPrice = Number(item.bkbi_total_price) / item.bkbi_quantity;
			if (qtyPaid > 0)   paid.push({ ...item, bkbi_quantity: qtyPaid,   bkbi_total_price: unitPrice * qtyPaid });
			if (qtyUnpaid > 0) unpaid.push({ ...item, bkbi_quantity: qtyUnpaid, bkbi_total_price: unitPrice * qtyUnpaid });
		}
		return { paid, unpaid };
	}

	get paidOrderItems(): BookingItem[]   { return this._computeOrderSplit().paid; }
	get unpaidOrderItems(): BookingItem[] { return this._computeOrderSplit().unpaid; }

	/** Totale di TUTTE le voci menu (replica web totalMenuItems) */
	get totalMenuItems(): number {
		return this.allOrderItems.reduce((s, i) => s + Number(i.bkbi_total_price), 0);
	}

	private _itemBaseStatus(notes: string): string {
		if (!notes) return 'ORDERED';
		return notes.split(':')[0]; // 'ORDERED:CASH' → 'ORDERED'
	}

	private _itemPaymentMethod(notes: string): string | null {
		const parts = notes?.split(':');
		return parts?.length > 1 ? parts[1] : null;
	}

	getItemStatusColor(notes: string): string {
		switch (this._itemBaseStatus(notes)) {
			case 'CART':    return 'medium';
			case 'ORDERED': return 'warning';
			case 'PAID':    return 'success';
			case 'PARTIAL': return 'tertiary';
			default:        return 'medium';
		}
	}

	getItemStatusLabel(notes: string): string {
		const base = this._itemBaseStatus(notes);
		const method = this._itemPaymentMethod(notes);
		const methodLabel = method ? ` (${this.getPaymentMethodLabel(method)})` : '';
		switch (base) {
			case 'CART':    return 'In carrello';
			case 'ORDERED': return `Ordinato${methodLabel}`;
			case 'PAID':    return 'Pagato';
			case 'PARTIAL': return 'Pagato parz.';
			default:        return notes || 'Ordinato';
		}
	}

	// ── Carrello: salva su DB con stato CART ──────────────────
	addToCart(item: BookingMenuItem) {
		const qty = item.selectedQuantity || 1;
		const existing = this.cartItems.find(c => c.bkbi_menu_item_id === item.bkmi_id);
		if (existing) {
			const updated = { ...existing, bkbi_quantity: existing.bkbi_quantity + qty };
			updated.bkbi_total_price = updated.bkbi_quantity * updated.bkbi_unit_price;
			this.bookingService.UpdateBookingItem(updated).subscribe({
				next: () => { this.configService.presentToastSuccess('Quantità aggiornata'); this.loadItems(); },
				error: (err) => this.configService.presentToastError(err),
			});
		} else {
			this.bookingService.InsertBookingItem({
				bkbi_id: 0,
				bkbi_booking_id: this.bkb_id,
				bkbi_menu_item_id: item.bkmi_id,
				bkbi_quantity: qty,
				bkbi_unit_price: item.bkmi_price,
				bkbi_total_price: qty * item.bkmi_price,
				bkbi_notes: 'CART',
				bkmi_name: item.bkmi_name,
				bkmc_name: item.bkmc_name,
			}).subscribe({
				next: () => { this.configService.presentToastSuccess('Aggiunto al carrello'); this.loadItems(); },
				error: (err) => this.configService.presentToastError(err),
			});
		}
		item.selectedQuantity = 1;
	}

	removeFromCart(item: BookingItem) {
		this.bookingService.DeleteBookingItem(item.bkbi_id).subscribe({
			next: () => this.loadItems(),
			error: (err) => this.configService.presentToastError(err),
		});
	}

	// CART → ORDERED (con scelta metodo di pagamento)
	async saveOrder() {
		if (!this.cartItems.length) return;
		const sheet = await this.actionSheetCtrl.create({
			header: 'Metodo di pagamento',
			subHeader: `Totale: € ${this.totaleCarrello.toFixed(2)}`,
			buttons: [
				{ text: 'Contanti',  icon: 'cash-outline',         data: 'CASH' },
				{ text: 'Carta',     icon: 'card-outline',         data: 'CARD' },
				{ text: 'Bonifico',  icon: 'swap-horizontal-outline', data: 'TRANSFER' },
				{ text: 'Solo ordine (paga dopo)', icon: 'receipt-outline', data: null },
				{ text: 'Annulla',   role: 'cancel',               icon: 'close-outline' },
			],
		});
		await sheet.present();
		const { data, role } = await sheet.onDidDismiss();
		if (role === 'cancel') return;
		this._doSaveOrder(data as string | null);
	}

	private _doSaveOrder(paymentMethod: string | null) {
		const toOrder = [...this.cartItems];
		const noteStatus = paymentMethod ? `ORDERED:${paymentMethod}` : 'ORDERED';
		const nextSeq = this.orderedItems.reduce((max, i) => Math.max(max, i.bkbi_send_seq || 0), 0) + 1;
		let done = 0;
		toOrder.forEach(item => {
			const updated = { ...item, bkbi_notes: noteStatus, bkbi_send_seq: nextSeq };
			this.bookingService.UpdateBookingItem(updated).subscribe({
				next: () => {
					done++;
					if (done === toOrder.length) {
						this.configService.presentToastSuccess('Ordine inviato');
						this.showCartModal = false;
						this.loadItems();
					}
				},
				error: (err) => this.configService.presentToastError(err),
			});
		});
	}

	get totaleCarrello(): number {
		return this.cartItems.reduce((s, i) => s + i.bkbi_total_price, 0);
	}

	// ── Pagamenti ─────────────────────────────────────────────
	loadPayments() {
		this.bookingService.GetPaymentsByBooking(this.bkb_id).subscribe({
			next: (res) => (this.payments = res || []),
			error: (err) => this.configService.presentToastError(err),
		});
	}

	get totaleOrdine(): number {
		return this.orderedItems.reduce((acc, i) => acc + i.bkbi_total_price, 0);
	}

	get totalePagato(): number {
		return this.payments.reduce((acc, p) => acc + p.bkp_amount, 0);
	}

	get paymentsBooking(): BookingPayment[] {
		return this.payments.filter(p => p.bkp_type === 'BOOKING');
	}

	get paymentsOrder(): BookingPayment[] {
		return this.payments.filter(p => p.bkp_type === 'ORDER');
	}

	get pagatoBooking(): number {
		return this.paymentsBooking.reduce((acc, p) => acc + p.bkp_amount, 0);
	}

	get pagatoOrder(): number {
		return this.paymentsOrder.reduce((acc, p) => acc + p.bkp_amount, 0);
	}

	/** Prezzo "fisso" della prenotazione (es. spiaggia) — escludendo ciò che è già negli items.
	 *  Replica web: `Math.max(0, bkb_total_amount - totalMenuItems)`. Se il backend aggiorna
	 *  bkb_total_amount sommando items, prezzoBase risulta 0 (= solo ordini da pagare). */
	get prezzoBase(): number {
		if (!this.booking) return 0;
		return Math.max(0, (this.booking.bkb_total_amount || 0) - this.totalMenuItems);
	}

	get residuoBooking(): number {
		return Math.max(0, this.prezzoBase - this.pagatoBooking);
	}

	get residuoOrder(): number {
		return Math.max(0, this.totalMenuItems - this.pagatoOrder);
	}

	get totaleConOrdini(): number {
		return (this.booking?.bkb_total_amount || 0) + this.totaleOrdine;
	}

	get residuo(): number {
		return Math.max(0, this.totaleConOrdini - this.totalePagato);
	}

	/** Residuo della cassa che si sta pagando (prenotazione o ordini). */
	private get residuoCassaCorrente(): number {
		return this.paymentForm?.bkp_type === 'ORDER' ? this.residuoOrder : this.residuoBooking;
	}

	/** La fattura è emettibile solo se il pagamento salda l'intero residuo della cassa
	 *  (niente fattura su pagamenti parziali). */
	get puoFatturare(): boolean {
		const res = this.residuoCassaCorrente;
		const importo = (this.paymentForm?.bkp_amount as number) || this.checkoutAmount || 0;
		return res <= 0 ? true : (importo + 0.005) >= res;
	}

	get isBeach(): boolean { return this.resourceType === 'BEACH'; }

	/** Etichetta da mostrare al posto di bkr_name quando la prenotazione raggruppa ≥2 tavoli e ha un alias. */
	get resourceDisplayName(): string {
		const b = this.booking;
		if (!b) return '';
		if ((b.bkb_resource_ids?.length ?? 0) >= 2 && b.bkb_resource_alias) return b.bkb_resource_alias;
		return b.bkr_name || '';
	}

	/** True quando la prenotazione raggruppa ≥2 tavoli ed è impostato un alias logico. */
	get hasResourceAliasCluster(): boolean {
		const b = this.booking;
		return !!(b && (b.bkb_resource_ids?.length ?? 0) >= 2 && b.bkb_resource_alias);
	}

	getStatusColor(status: string): string {
		switch (status) {
			case 'PENDING':   return 'warning';
			case 'CONFIRMED': return 'success';
			case 'COMPLETED': return 'medium';
			case 'CANCELLED': return 'danger';
			default:          return 'medium';
		}
	}

	getStatusLabel(status: string): string {
		switch (status) {
			case 'PENDING':   return 'In attesa';
			case 'CONFIRMED': return 'Confermata';
			case 'COMPLETED': return 'Completata';
			case 'CANCELLED': return 'Annullata';
			default:          return status;
		}
	}

	getPaymentVisibleNotes(notes: string): string {
		if (!notes) return '';
		const match = notes.match(/^\x01items\x01.*?\x01\x01([\s\S]*)$/);
		return match ? match[1].trim() : notes;
	}

	getPaymentItemsSummary(notes: string): string {
		if (!notes) return '';
		const match = notes.match(/^\x01items\x01(.*?)\x01\x01/s);
		if (!match) return '';
		try {
			const paid: { id: number; qty: number }[] = JSON.parse(match[1]);
			return paid.map(p => {
				const item = this.items.find(i => i.bkbi_id === p.id);
				return item ? `${item.bkmi_name} x${p.qty}` : `#${p.id} x${p.qty}`;
			}).join(', ');
		} catch { return ''; }
	}

	getPaymentMethodLabel(method: string): string {
		switch (method) {
			case 'CASH':     return 'Contanti';
			case 'CARD':     return 'Carta';
			case 'TRANSFER': return 'Bonifico';
			default:         return method;
		}
	}

	cambiaStato(nuovoStato: string) {
		this.bookingService.UpdateBookingStatus(this.bkb_id, nuovoStato).subscribe({
			next: () => { this.configService.presentToastSuccess('Stato aggiornato'); this.loadBooking(); },
			error: (err) => this.configService.presentToastError(err),
		});
	}

	getPaidQtyForItem(bkbi_id: number): number {
		let total = 0;
		for (const p of this.paymentsOrder) {
			const match = p.bkp_notes?.match(/^\x01items\x01(.*?)\x01\x01/s);
			if (!match) continue;
			try {
				const items: { id: number; qty: number }[] = JSON.parse(match[1]);
				const found = items.find(i => i.id === bkbi_id);
				if (found) total += found.qty;
			} catch {}
		}
		return total;
	}

	getRemainingQty(item: BookingItem): number {
		return Math.max(0, item.bkbi_quantity - this.getPaidQtyForItem(item.bkbi_id));
	}

	getRemainingAmount(item: BookingItem): number {
		return this.getRemainingQty(item) * item.bkbi_unit_price;
	}

	openPaymentModal(type: 'BOOKING' | 'ORDER') {
		const amount = type === 'BOOKING' ? this.residuoBooking : this.residuoOrder;
		this.paymentForm = { bkp_booking_id: this.bkb_id, bkp_method: 'CASH', bkp_type: type, bkp_amount: amount, bkp_notes: '' };
		if (type === 'ORDER') {
			this.paymentItemSelected = this.unpaidOrderItems.map(() => true);
			this.paymentItemQty = this.unpaidOrderItems.map(i => this.getRemainingQty(i));
		}
		this.showPaymentModal = true;
	}

	closePaymentModal() { this.showPaymentModal = false; }

	onPaymentItemToggle() {
		this._recalcOrderAmount();
	}

	onPaymentItemQtyChange(index: number, qty: number) {
		const item = this.unpaidOrderItems[index];
		const max = item ? this.getRemainingQty(item) : 1;
		this.paymentItemQty[index] = Math.max(1, Math.min(qty || 1, max));
		this._recalcOrderAmount();
	}

	private _recalcOrderAmount() {
		let total = 0;
		for (let i = 0; i < this.unpaidOrderItems.length; i++) {
			if (this.paymentItemSelected[i]) {
				total += this.unpaidOrderItems[i].bkbi_unit_price * this.paymentItemQty[i];
			}
		}
		this.paymentForm.bkp_amount = total;
	}

	// ─────────────────────────────────────────────────────────────────
	// CHECKOUT (replica CheckoutComponent del portale Angular)
	// Dialog 1 (registra) → Dialog 2 (checkout) con scelta CASH/NEXI/PAYPAL
	// ─────────────────────────────────────────────────────────────────

	/** Dialog 1 → Dialog 2: chiude la registrazione e apre il checkout. Non salva niente. */
	procediAlPagamento() {
		if (!this.paymentForm.bkp_amount || this.paymentForm.bkp_amount <= 0) {
			this.configService.presentToastError('Inserire un importo valido');
			return;
		}
		this.checkoutAmount = this.paymentForm.bkp_amount;
		this.checkoutType = (this.paymentForm.bkp_type as 'BOOKING' | 'ORDER') || 'BOOKING';
		this.showPaymentModal = false;
		this._openCheckout();
	}

	private _openCheckout() {
		this.click_pay = false;
		this.view_paypal = false;
		this.paymentTypes = [];
		this.activeConfigs = [];
		this.configsForSelectedType = [];
		this.selectedPccId = 0;
		this.selectedTypeCode = '';
		this._loadNexiScript();
		this.showCheckoutModal = true;
		this.pagamentoService.getPaymentTypes().subscribe({
			next: (types) => {
				this.paymentTypes = types || [];
				if (this.cmp_id > 0) {
					this.pagamentoService.getActiveConfigsByCompany(this.cmp_id).subscribe({
						next: (cfgs) => { this.activeConfigs = cfgs || []; this._initFirstSelection(); },
						error: () => { this.activeConfigs = []; this._initFirstSelection(); },
					});
				} else {
					this._initFirstSelection();
				}
			},
			error: () => {
				this.showCheckoutModal = false;
				this.configService.presentToastError('Servizio di pagamento non disponibile');
			},
		});
	}

	closeCheckoutModal() {
		this.showCheckoutModal = false;
		this.click_pay = false;
	}

	private _initFirstSelection() {
		// Selezione iniziale: primo metodo della lista filtrata (CASH-first se presente)
		const list = this.filteredPaymentTypes;
		if (list.length > 0) {
			this.selectedTypeCode = list[0].pt_code;
			this.onTypeChange();
		}
	}

	onTypeChange() {
		this.view_paypal = this.selectedTypeCode === 'PAYPAL';
		this.click_pay = false;
		this.configsForSelectedType = this.activeConfigs.filter((c) => c.pcc_provider === this.selectedTypeCode);
		if (this.configsForSelectedType.length === 1)      this.selectedPccId = this.configsForSelectedType[0].pcc_id;
		else if (this.configsForSelectedType.length === 0) this.selectedPccId = 0;
		else                                                this.selectedPccId = 0;
		if (this.view_paypal && this.checkoutAmount > 0 && (this.selectedPccId > 0 || this.configsForSelectedType.length === 0)) {
			this._initPayPal();
		}
	}

	onConfigChange() {
		if (this.view_paypal && this.checkoutAmount > 0 && this.selectedPccId > 0) this._initPayPal();
	}

	doPayment() {
		if (this.click_pay) return;
		this.click_pay = true;
		switch (this.selectedTypeCode) {
			case 'CASH': this._callPaymentConfig('CASH'); break;
			case 'NEXI': this._callPaymentConfig('NEXI'); break;
			case 'NFC':  this._startRiscossioneNfc(); break;
			// PayPal: gestito dai bottoni nativi
		}
	}

	// ── Riscossione NFC (manager) ──────────────────────────────────
	/**
	 * Avvia il flusso di riscossione NFC:
	 *   1. apre il modale "Avvicina la carta…"
	 *   2. avvia il listener NFC
	 *   3. su tap rilevato → pay/config(CASH) → pay/save(COMPLETED) → InsertPayment
	 */
	private async _startRiscossioneNfc(): Promise<void> {
		this.showNfcModal = true;
		this.nfcStatus = 'waiting';
		this.nfcMessage = 'Avvicina la carta o un tag NFC al dispositivo…';

		const result = await this.riscossioneService.startListening();

		if (result.kind === 'unsupported') {
			this.nfcStatus = 'unsupported';
			this.nfcMessage = 'NFC non supportato su questo dispositivo';
			this.click_pay = false;
			return;
		}
		if (result.kind === 'disabled') {
			this.nfcStatus = 'error';
			this.nfcMessage = 'NFC disattivato — attivalo dalle impostazioni del telefono';
			this.click_pay = false;
			return;
		}
		if (result.kind === 'cancelled') {
			this.showNfcModal = false;
			this.click_pay = false;
			return;
		}
		if (result.kind === 'error') {
			this.nfcStatus = 'error';
			this.nfcMessage = `Errore NFC: ${result.message}`;
			this.click_pay = false;
			return;
		}

		// Tap rilevato → procedi col pagamento CASH lato server
		this.nfcStatus = 'detected';
		this.nfcMessage = 'Carta rilevata, registrazione pagamento…';
		const tagRef = result.tagId || 'NFC';

		const req: PaymentRequest = {
			type_code: 'CASH',
			amount: this.checkoutAmount,
			module: 'BOOKING',
			entity_id: this.bkb_id,
			entity_ref: `NFC:${tagRef}`,
		};
		this.pagamentoService.getPaymentConfig(req).subscribe({
			next: (uid) => {
				this.nfcStatus = 'saving';
				this.pagamentoService.savePayment({ uid_transaction: uid as string, state: 'COMPLETED' }).subscribe({
					next: () => {
						this.nfcStatus = 'done';
						this.nfcMessage = 'Pagamento riscosso con successo';
						// Chiude il modal dopo breve animazione di conferma
						setTimeout(() => {
							this.showNfcModal = false;
							this._registraPagamentoBooking();
						}, 800);
					},
					error: () => {
						this.nfcStatus = 'error';
						this.nfcMessage = 'Errore nel salvataggio del pagamento';
						this.click_pay = false;
					},
				});
			},
			error: () => {
				this.nfcStatus = 'error';
				this.nfcMessage = 'Errore di configurazione pagamento';
				this.click_pay = false;
			},
		});
	}

	cancelNfc(): void {
		this.riscossioneService.cancel();
	}

	closeNfcModal(): void {
		this.riscossioneService.cancel();
		this.showNfcModal = false;
		this.click_pay = false;
	}

	private _callPaymentConfig(typeCode: string) {
		const req: PaymentRequest = {
			type_code: typeCode,
			amount: this.checkoutAmount,
			module: 'BOOKING',
			entity_id: this.bkb_id,
		};
		if (typeCode !== 'CASH' && this.selectedPccId > 0) req.pcc_id = this.selectedPccId;

		this.pagamentoService.getPaymentConfig(req).subscribe({
			next: (raw) => {
				if (typeCode === 'CASH') {
					this._executeSavePayment(raw as string, 'COMPLETED', null);
				} else if (typeCode === 'NEXI') {
					const cfg = typeof raw === 'string' ? JSON.parse(raw) : raw;
					this._callPagaXpay(cfg);
				}
			},
			error: () => {
				this.click_pay = false;
				this.configService.presentToastError('Errore di configurazione pagamento');
			},
		});
	}

	// ── NEXI XPay ─────────────────────────────────────────────────────
	private _loadNexiScript() {
		if (!this.configService.nexi_script) return;
		const existing = document.getElementById('nexi-xpay-script');
		if (existing) {
			const ready = typeof (window as any)['XPay'] !== 'undefined'
				&& typeof (window as any)['XPay'].openLightbox === 'function';
			if (ready) { this.nexiScriptReady = true; return; }
			existing.remove();
		}
		const script = document.createElement('script');
		script.id = 'nexi-xpay-script';
		script.src = this.configService.nexi_script;
		script.onload = () => this.ngZone.run(() => { this.nexiScriptReady = true; });
		script.onerror = () => this.ngZone.run(() => this.configService.presentToastError('Script Nexi non disponibile'));
		document.head.appendChild(script);
	}

	private _callPagaXpay(config: any) {
		if (!this.nexiScriptReady || typeof XPay === 'undefined' || typeof XPay.openLightbox !== 'function') {
			this.click_pay = false;
			this.configService.presentToastError('Script Nexi ancora in caricamento, riprova');
			return;
		}
		try {
			if (typeof XPay.init === 'function') XPay.init();
			const env = config?.baseConfig?.enviroment || 'INTEG';
			XPay.setEnvironment(env);
			XPay.initLightbox(config);
			XPay.openLightbox();
		} catch {
			this.click_pay = false;
			this.configService.presentToastError('Impossibile aprire il gateway Nexi');
			return;
		}
		window.addEventListener('XPay_Payment_Result', (event: any) => {
			const detail = event.detail;
			this.ngZone.run(() => {
				switch (detail.esito) {
					case 'OK':
						this._executeSavePayment(detail.codTrans, 'COMPLETED', JSON.stringify(detail));
						break;
					case 'ANNULLO':
						this.click_pay = false;
						this.configService.presentToastError('Pagamento annullato');
						break;
					default:
						this.click_pay = false;
						this.configService.presentToastError(`Pagamento non riuscito [${detail.codiceEsito}] ${detail.messaggio}`);
				}
			});
		}, { once: true });
	}

	// ── PayPal ────────────────────────────────────────────────────────
	private _initPayPal() {
		const req: PaymentRequest = {
			type_code: 'PAYPAL',
			amount: this.checkoutAmount,
			module: 'BOOKING',
			entity_id: this.bkb_id,
		};
		if (this.selectedPccId > 0) req.pcc_id = this.selectedPccId;

		this.pagamentoService.getPaymentConfig(req).subscribe({
			next: (raw) => {
				const config: any = typeof raw === 'string' ? JSON.parse(raw) : raw;
				const container = document.getElementById('paypal-button-container');
				if (container) container.innerHTML = '';

				loadScript({ clientId: this.configService.paypal_clientId, currency: 'EUR' })
					.then((paypal: any) => {
						paypal.Buttons({
							style: { label: 'pay', layout: 'horizontal', shape: 'rect', tagline: false },
							createOrder: (_d: any, actions: any) => {
								return actions.order.create({
									intent: config.intent,
									purchase_units: [{
										reference_id: config.purchase_unit[0].reference_id,
										custom_id:    config.purchase_unit[0].custom_id,
										amount: {
											value:         config.purchase_unit[0].amount.value,
											currency_code: config.purchase_unit[0].amount.currency_code,
										},
									}],
								}).then((orderId: string) => {
									const codTrans = config.purchase_unit[0].reference_id;
									this.pagamentoService.linkPayPalOrder(codTrans, orderId).subscribe({ error: () => {} });
									return orderId;
								});
							},
							onApprove: (_d: any, actions: any) => actions.order.capture().then((details: any) => {
								this.ngZone.run(() => this._executeSavePayment(
									details.purchase_units[0].reference_id,
									'COMPLETED',
									JSON.stringify(details),
								));
							}),
							onCancel: () => this.ngZone.run(() => this.configService.presentToastError('Pagamento PayPal annullato')),
							onError: (err: any) => this.ngZone.run(() => {
								console.error('PayPal error', err);
								this.configService.presentToastError('Errore PayPal');
							}),
						}).render('#paypal-button-container').catch((err: any) => console.error('PayPal render error', err));
					})
					.catch((err: any) => console.error('PayPal load error', err));
			},
			error: () => this.configService.presentToastError('Errore configurazione PayPal'),
		});
	}

	// ── Salvataggio finale: pay/save + InsertPayment ─────────────────
	private _executeSavePayment(uid: string, state: string, responseJson: string | null) {
		const req: PaymentRequest = {
			uid_transaction: uid,
			state,
			response_json: responseJson ?? undefined,
		};
		this.pagamentoService.savePayment(req).subscribe({
			next: () => {
				if (state === 'COMPLETED') this._registraPagamentoBooking();
				else {
					this.click_pay = false;
					this.configService.presentToastError(`Pagamento ${state}`);
				}
			},
			error: () => {
				this.click_pay = false;
				this.configService.presentToastError('Errore nel salvataggio del pagamento');
			},
		});
	}

	/** Salva il pagamento anche sulla tabella bk_payment con note codificate items per ORDER */
	private _registraPagamentoBooking() {
		let notes = this.paymentForm.bkp_notes || '';
		if (this.paymentForm.bkp_type === 'ORDER') {
			const itemData = this.unpaidOrderItems
				.map((item, i) => this.paymentItemSelected[i] ? { id: item.bkbi_id, qty: this.paymentItemQty[i] } : null)
				.filter(x => x !== null);
			notes = `\x01items\x01${JSON.stringify(itemData)}\x01\x01${notes}`;
		}
		const payment = { ...this.paymentForm, bkp_method: this._methodFromTypeCode(this.selectedTypeCode), bkp_notes: notes };
		this.bookingService.InsertPayment(payment).subscribe({
			next: () => {
				this.configService.presentToastSuccess('Pagamento registrato');
				this.showCheckoutModal = false;
				this.click_pay = false;
				// Pagamento confermato -> se richiesta, crea la fattura (invio a SDI via scheduler).
				this._creaFatturaSeRichiesta();
				if (this.paymentForm.bkp_type === 'ORDER') {
					this.unpaidOrderItems.forEach((item, i) => {
						if (!this.paymentItemSelected[i]) return;
						const paidQty = this.paymentItemQty[i];
						const remaining = this.getRemainingQty(item) - paidQty;
						const newStatus = remaining <= 0 ? 'PAID' : 'PARTIAL';
						this.bookingService.UpdateBookingItem({ ...item, bkbi_notes: newStatus }).subscribe();
					});
				}
				this.loadPayments();
				this.loadItems();
				this.loadBooking();
			},
			error: (err) => { this.click_pay = false; this.configService.presentToastError(err); },
		});
	}

	private _methodFromTypeCode(code: string): 'CASH' | 'CARD' | 'TRANSFER' | 'OTHER' {
		switch (code) {
			case 'CASH':   return 'CASH';
			case 'NEXI':   return 'CARD';
			case 'PAYPAL': return 'OTHER';
			default:       return 'CASH';
		}
	}

	// ── Richiesta fattura ────────────────────────────────────────────

	/**
	 * Attivazione del toggle "Richiedi fattura": raccoglie i dati fiscali al momento della richiesta.
	 * Se il cliente non è completo (manca P.IVA per azienda / CF per privato o il recapito SDI),
	 * apre la modale e li richiede; se l'utente annulla, il toggle viene disattivato.
	 */
	onToggleRichiediFattura() {
		if (!this.requestInvoice) {
			this.datiFiscali = null;
			return;
		}
		this.fatturaService.getMyFiscalData(this.cmp_id).subscribe(async (d) => {
			if (d && datiFiscaliCompleti(d)) {
				this.datiFiscali = d;
				return;
			}
			const modal = await this.modalCtrl.create({
				component: ModalInvoiceRequestComponent,
				componentProps: { cmp_id: this.cmp_id, prefill: d },
			});
			await modal.present();
			const { data, role } = await modal.onDidDismiss();
			if (role === 'confirm' && data) this.datiFiscali = data as DatiFiscali;
			else this.requestInvoice = false; // annullato -> niente fattura
		});
	}

	/** Riapre la modale dati fiscali precompilata per correggerli prima del pagamento. */
	async modificaDatiFiscali() {
		const modal = await this.modalCtrl.create({
			component: ModalInvoiceRequestComponent,
			componentProps: { cmp_id: this.cmp_id, prefill: this.datiFiscali },
		});
		await modal.present();
		const { data, role } = await modal.onDidDismiss();
		if (role === 'confirm' && data) this.datiFiscali = data as DatiFiscali;
	}

	/** A pagamento confermato: crea la richiesta di fattura (fattura attiva DA_INVIARE). */
	private _creaFatturaSeRichiesta() {
		if (!this.requestInvoice || !this.datiFiscali) return;
		if (!this.puoFatturare) return; // pagamento parziale: niente fattura
		// Cassa ordini -> fattura ordini separata (BOOKING_ORDER, righe menu dal backend);
		// cassa prenotazione -> fattura prenotazione (BOOKING, riga unica col totale base).
		const isOrder = this.paymentForm?.bkp_type === 'ORDER';
		const modulo: ModuloFattura = isOrder ? 'BOOKING_ORDER' : 'BOOKING';
		const aliquota = 22;
		const gross = this.checkoutAmount || (this.paymentForm.bkp_amount as number) || 0;
		const righe: RigaFattura[] = [
			{
				descrizione: isOrder ? 'Ordine' : (this.resourceType ? `Prenotazione ${this.resourceType}` : 'Prenotazione'),
				quantita: 1,
				prezzoUnitario: this._nettoDaLordo(gross, aliquota),
				aliquotaIVA: aliquota,
			},
		];
		const oggi = new Date().toISOString().substring(0, 10);
		const payload: RichiestaFatturaPayload = {
			cmp_id: this.cmp_id,
			module: modulo,
			entity_id: this.bkb_id,
			datiFiscali: this.datiFiscali,
			righe,
			pagamento: { metodoSdi: this._mpDaTypeCode(this.selectedTypeCode), pagata: true, dataPagamento: oggi },
		};
		this.fatturaService.requestInvoice(payload).subscribe({
			next: () => this.configService.presentToastSuccess('Fattura richiesta: sarà inviata a breve'),
			error: () => this.configService.presentToastError('Errore nella richiesta fattura'),
		});
	}

	/** Scorporo IVA: da importo lordo (incassato) a imponibile unitario. */
	private _nettoDaLordo(lordo: number, aliquota: number): number {
		const netto = lordo / (1 + aliquota / 100);
		return Math.round(netto * 100) / 100;
	}

	/** Codice ModalitaPagamento SDI derivato dal tipo pagamento. */
	private _mpDaTypeCode(code: string): string {
		switch (code) {
			case 'NEXI':   return 'MP08';
			case 'PAYPAL': return 'MP08';
			case 'CASH':   return 'MP01';
			default:       return 'MP05';
		}
	}
}
