// 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, ViewChild } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription, forkJoin } from 'rxjs';
import { ActionSheetController, IonModal } from '@ionic/angular';
import { ConfigService } from '../../providers/config.service';
import { BookingService } from '../../providers/booking.service';
import { CompaniesService } from '../../providers/companies.service';
import { FavoritesService } from '../../providers/favorites.service';
import { LayoutService } from '../../providers/layout.service';
import { MessagingService } from '../../providers/messaging.service';
import { FatturaService } from '../../providers/fattura.service';
import { Booking, BookingItem, BookingResource, BookingResourceSlotOverride, BookingResourceType, BookingTimeSlot } from '../../models/booking.model';
import { ServiceCard } from '../../models/serviceCard.model';
import { CapacitorBarcodeScanner, CapacitorBarcodeScannerTypeHint } from '@capacitor/barcode-scanner';

@Component({
	selector: 'app-gestione-prenotazioni',
	templateUrl: 'gestione-prenotazioni.page.html',
	styleUrls: ['gestione-prenotazioni.page.scss'],
	standalone: false,
})
export class GestionePrenotazioniPage {
	@ViewChild('chatModal') chatModal!: IonModal;

	cmp_id = 0;
	prt_id = 0;

	activeBookings: Booking[] = [];
	pastBookings: Booking[] = [];
	cancelledBookings: Booking[] = [];
	loading = false;

	// Tab attiva
	activeTab: 'active' | 'past' | 'cancelled' = 'active';

	// ── Vista manager (utenti ADMIN della company) ───────────────────
	isManager = false;
	/** In modalità manager: nasconde hero + intestazione + action-row per dare più spazio ai tab. */
	managerHeaderCollapsed = true;
	managerTab: 'lista' | 'da-assegnare' | 'ordini' | 'mappa' = 'lista';
	companyBookings: Booking[] = [];
	managerFilters = {
		dateFrom: new Date().toISOString().substring(0, 10),
		dateTo:   new Date().toISOString().substring(0, 10),
		status: '',
		paymentStatus: '' as '' | 'PAID' | 'UNPAID' | 'PARTIAL',
		typeId: 0,        // 0 = tutte le risorse
		timeSlotId: 0,    // 0 = tutte le fasce
	};

	/** Tipi risorsa della company (per il filtro Risorsa) */
	managerResourceTypes: BookingResourceType[] = [];
	/** Fasce orarie del tipo selezionato (visibili solo se RESTAURANT) */
	managerTimeSlots: BookingTimeSlot[] = [];

	// ── Tab Ordini (replica web) ─────────────────────────────────────
	ordiniList: BookingItem[] = [];
	ordiniLoading = false;
	ordiniFilters = {
		dateFrom: new Date().toISOString().substring(0, 10),
		dateTo:   new Date().toISOString().substring(0, 10),
	};
	/** Filtro stato multi-select: famiglie di stati (default: in lavorazione/pronto) */
	readonly ordiniStatusFamilies: { key: 'CART' | 'ORDERED' | 'READY' | 'ELABORATE'; label: string; icon: string; color: string }[] = [
		{ key: 'CART',      label: 'Carrello', icon: 'cart-outline',              color: 'medium'   },
		{ key: 'ORDERED',   label: 'Ordini',   icon: 'receipt-outline',           color: 'warning'  },
		{ key: 'READY',     label: 'Pronti',   icon: 'restaurant-outline',        color: 'tertiary' },
		{ key: 'ELABORATE', label: 'Consegna', icon: 'checkmark-done-outline',    color: 'success'  },
	];
	ordiniStatusFilter = new Set<'CART' | 'ORDERED' | 'READY' | 'ELABORATE'>(['ORDERED', 'READY']);
	/** Filtro per tipo risorsa nel tab Ordini: '' = tutti, 'BEACH' = spiaggia, 'RESTAURANT' = ristorante */
	ordiniTypeFilter: '' | 'BEACH' | 'RESTAURANT' = '';
	/** Cache booking_id → bkrt_type per popolare gli items lato client */
	private _bookingTypeMap: Map<number, string> = new Map();

	// ── Tab Mappa ──────────────────────────────────────────────────────
	mappaDate: string = new Date().toISOString().substring(0, 10);
	mappaResourceTypes: BookingResourceType[] = [];
	mappaTypeId = 0;
	mappaResources: BookingResource[] = [];
	mappaBookings: Booking[] = [];
	mappaLoading = false;
	/** Fasce orarie del tipo selezionato (RESTAURANT) per filtrare la mappa */
	mappaTimeSlots: BookingTimeSlot[] = [];
	/** Slot attualmente selezionato per filtrare le prenotazioni mostrate (null = tutti) */
	selectedMappaSlotId: number | null = null;
	/** Override visivi (posizioni cluster) caricati dal backend per la data selezionata */
	mappaSlotOverrides: BookingResourceSlotOverride[] = [];

	// Company + hero
	company: any = null;
	heroImage: string | null = null;
	heroBackgroundClass: string = '';
	heroOpacity: number = 0.8;
	isFavorite: boolean = false;
	serviceDescription: string = '';
	activeCard: ServiceCard | null = null;

	// Chat modal
	chatMessage: string = '';
	chatSending: boolean = false;

	private paramsSubscription!: Subscription;

	constructor(
		private activatedRoute: ActivatedRoute,
		private router: Router,
		public configService: ConfigService,
		private bookingService: BookingService,
		private companiesService: CompaniesService,
		private favoritesService: FavoritesService,
		private actionSheetCtrl: ActionSheetController,
		private layoutService: LayoutService,
		private messagingService: MessagingService,
		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'];
		});
		this.initHeroImage();
	}

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

	ionViewWillEnter() {
		this.layoutService.setPageConfig({
			title: '',
			showBack: true,
			backButtonHref: '/servizi',
		});
		this.isManager = this.configService.isUserAuthorizedAs('ADMIN');
		this.loadCompanyInfo();
		if (this.isManager) {
			if (this.managerResourceTypes.length === 0) {
				this.loadManagerResourceTypes();
			} else {
				this.loadCompanyBookings();
			}
		} else {
			this.loadActiveBookings();
		}
	}

	private initHeroImage(): void {
		const card: ServiceCard | null = this.companiesService.activeServiceCard;
		if (card) {
			this.activeCard = card;
			this.serviceDescription = card.srv_description || '';
			this.isFavorite = this.favoritesService.isFavorite(card.srv_id, card.company?.id || 0);
			if (card.srv_show_hero_image && card.srv_background_image) {
				this.heroImage = card.srv_background_image;
				this.heroBackgroundClass = card.srv_background_class || '';
				this.heroOpacity = card.srv_background_opacity ?? 0.8;
			}
		}
	}

	getServiceDescription(): string {
		return this.serviceDescription || 'Gestisci le tue prenotazioni: ombrelloni, tavoli e servizi della struttura.';
	}

	loadCompanyInfo(): void {
		this.companiesService.GetCompany(this.cmp_id).subscribe({
			next: (data: any) => {
				this.company = data;
				if (data?.name) {
					this.layoutService.setPageConfig({ title: data.name });
				}
			},
		});
	}

	toggleManagerHeader(): void {
		this.managerHeaderCollapsed = !this.managerHeaderCollapsed;
	}

	toggleFavorite(): void {
		if (!this.activeCard) return;
		const srvId = this.activeCard.srv_id;
		const cmpId = this.activeCard.company?.id || 0;
		this.favoritesService.toggleFavorite(srvId, cmpId);
		this.isFavorite = this.favoritesService.isFavorite(srvId, cmpId);
	}

	doRefresh(event: any): void {
		this.loadCompanyInfo();
		if (this.isManager) this.loadCompanyBookings();
		else                this.loadActiveBookings();
		setTimeout(() => event.target.complete(), 800);
	}

	// ── Manager: caricamento prenotazioni dell'intera azienda ────────
	loadCompanyBookings() {
		this.loading = true;
		const f = this.managerFilters;
		this.bookingService.GetBookingList(
			this.cmp_id,
			f.dateFrom || undefined,
			f.dateTo || undefined,
			f.status || undefined,
			f.typeId || undefined,
			'bkb_date DESC',
			1,
			500,
		).subscribe({
			next: (res) => {
				this.loading = false;
				this.companyBookings = res || [];
			},
			error: (err) => {
				this.loading = false;
				this.configService.presentToastError(err);
			},
		});
	}

	onManagerFilterChange() {
		this.loadCompanyBookings();
	}

	/** Carica i tipi risorsa della company. Se esiste un tipo RESTAURANT lo preseleziona
	 *  e carica le fasce orarie (auto-default sulla più vicina all'orario corrente). */
	loadManagerResourceTypes() {
		this.bookingService.GetResourceTypes(this.cmp_id).subscribe({
			next: (types) => {
				this.managerResourceTypes = (types || []).filter(t => t.bkrt_is_active);
				const restaurantType = this.managerResourceTypes.find(t => t.bkrt_type === 'RESTAURANT');
				if (restaurantType && !this.managerFilters.typeId) {
					this.managerFilters.typeId = restaurantType.bkrt_id;
					this.loadManagerTimeSlots();
				} else {
					this.loadCompanyBookings();
				}
			},
			error: () => {
				this.managerResourceTypes = [];
				this.loadCompanyBookings();
			},
		});
	}

	/** Carica le fasce orarie del tipo selezionato (solo se RESTAURANT) e seleziona
	 *  automaticamente quella più vicina all'orario corrente. */
	loadManagerTimeSlots() {
		const selected = this.managerResourceTypes.find(t => t.bkrt_id === this.managerFilters.typeId);
		if (!selected || selected.bkrt_type !== 'RESTAURANT') {
			this.managerTimeSlots = [];
			this.managerFilters.timeSlotId = 0;
			this.loadCompanyBookings();
			return;
		}
		this.bookingService.GetTimeSlots(this.cmp_id, this.managerFilters.typeId).subscribe({
			next: (slots) => {
				this.managerTimeSlots = (slots || []).filter(s => s.bkts_is_active);
				const closest = this._findClosestTimeSlot(this.managerTimeSlots);
				this.managerFilters.timeSlotId = closest ? closest.bkts_id : 0;
				this.loadCompanyBookings();
			},
			error: () => {
				this.managerTimeSlots = [];
				this.managerFilters.timeSlotId = 0;
				this.loadCompanyBookings();
			},
		});
	}

	onManagerTypeChange() {
		this.managerFilters.timeSlotId = 0;
		this.loadManagerTimeSlots();
	}

	/** Slot la cui finestra contiene l'orario corrente, altrimenti quello con start_time più vicino */
	private _findClosestTimeSlot(slots: BookingTimeSlot[]): BookingTimeSlot | null {
		if (!slots || slots.length === 0) return null;
		const now = new Date();
		const nowMin = now.getHours() * 60 + now.getMinutes();
		const toMin = (t: string) => {
			if (!t) return 0;
			const parts = t.split(':');
			return (+parts[0] || 0) * 60 + (+parts[1] || 0);
		};
		const contains = slots.find(s => {
			const a = toMin(s.bkts_start_time);
			const b = toMin(s.bkts_end_time);
			return a <= nowMin && nowMin <= b;
		});
		if (contains) return contains;
		let best: BookingTimeSlot | null = null;
		let bestDiff = Number.MAX_SAFE_INTEGER;
		for (const s of slots) {
			const diff = Math.abs(toMin(s.bkts_start_time) - nowMin);
			if (diff < bestDiff) { bestDiff = diff; best = s; }
		}
		return best;
	}

	get isRestaurantTypeSelected(): boolean {
		const t = this.managerResourceTypes.find(rt => rt.bkrt_id === this.managerFilters.typeId);
		return t?.bkrt_type === 'RESTAURANT';
	}

	onManagerTabChange() {
		if (this.managerTab === 'ordini' && this.ordiniList.length === 0) this.getOrdini();
		if (this.managerTab === 'mappa' && this.mappaResourceTypes.length === 0) this.loadMappaResourceTypes();
	}

	// ── Tab Mappa: caricamento ──────────────────────────────────────
	loadMappaResourceTypes() {
		this.bookingService.GetResourceTypes(this.cmp_id).subscribe({
			next: (types) => {
				this.mappaResourceTypes = (types || []).filter(t => t.bkrt_is_active);
				if (this.mappaResourceTypes.length > 0 && !this.mappaTypeId) {
					this.mappaTypeId = this.mappaResourceTypes[0].bkrt_id;
				}
				this.loadMappaData();
			},
			error: (err) => this.configService.presentToastError(err),
		});
	}

	/** Tutte le risorse caricate (incluse passerelle/stabilimento/canvas_config) — passate al componente mappa */
	mappaResourcesAll: BookingResource[] = [];

	/** Override visivi posizione tavoli (cluster RESTAURANT) — in px nel sistema REF 900×540.
	 *  Disattivato per ora: usiamo le posizioni dal DB (uguali al web). */
	mappaDisplayPositions: Map<number, { x: number; y: number }> = new Map();

	loadMappaData() {
		if (!this.mappaTypeId) return;
		this.mappaLoading = true;
		const date = this.mappaDate;
		// Carica risorse + prenotazioni + override visivi (cluster computati dal web) in parallelo
		forkJoin({
			resources: this.bookingService.GetResources(this.cmp_id, this.mappaTypeId),
			bookings:  this.bookingService.GetBookingsByPeriod(this.cmp_id, date, date, this.mappaTypeId),
			overrides: this.bookingService.GetSlotOverrides(this.cmp_id, date),
		}).subscribe({
			next: ({ resources, bookings, overrides }) => {
				const all = resources || [];
				// Tutte le risorse passate al componente (include _PASSERELLA_, _STABILIMENTO_, _CANVAS_CONFIG_)
				this.mappaResourcesAll = all.filter(r => r.bkr_is_active);
				// Solo "vere" risorse per la logica interna (status, sort)
				const filtered = all.filter(r => r.bkr_is_active && !r.bkr_code?.startsWith('_'));
				this.mappaBookings = (bookings || []).filter(b => b.bkb_status !== 'CANCELLED');
				this.mappaResources = this._sortResourcesGrouped(filtered, this.mappaBookings);
				this.mappaSlotOverrides = overrides || [];
				this.loadMappaTimeSlots();
				// Costruisci displayPositions dagli override per lo slot selezionato
				this._buildDisplayPositionsFromOverrides();
				this.mappaLoading = false;
			},
			error: (err) => {
				this.mappaLoading = false;
				this.configService.presentToastError(err);
			},
		});
	}

	/**
	 * Costruisce la Map<resourceId, {x, y in px}> dagli override del backend per lo slot
	 * selezionato. Le posizioni nel DB sono in % (REF_WIDTH × REF_HEIGHT), le convertiamo
	 * in pixel come si aspetta il componente <app-resource-map>.
	 */
	private _buildDisplayPositionsFromOverrides(): void {
		const REF_W = 900;
		const REF_H = 540;
		const map = new Map<number, { x: number; y: number }>();
		if (this.selectedMappaSlotId) {
			for (const ov of this.mappaSlotOverrides) {
				if (ov.bkrso_slot_id !== this.selectedMappaSlotId) continue;
				if (ov.bkrso_override_x == null || ov.bkrso_override_y == null) continue;
				map.set(ov.bkrso_resource_id, {
					x: ov.bkrso_override_x / 100 * REF_W,
					y: ov.bkrso_override_y / 100 * REF_H,
				});
			}
		}
		this.mappaDisplayPositions = map;
	}

	/** Carica le fasce orarie del tipo selezionato e seleziona sempre la prima (obbligatoria) */
	loadMappaTimeSlots() {
		if (!this.mappaTypeId) return;
		this.bookingService.GetTimeSlots(this.cmp_id, this.mappaTypeId).subscribe({
			next: (slots) => {
				this.mappaTimeSlots = (slots || []).filter(s => s.bkts_is_active);
				// Fascia obbligatoria: se la selezione corrente non esiste più, prendi la prima
				const stillValid = this.selectedMappaSlotId &&
					this.mappaTimeSlots.some(s => s.bkts_id === this.selectedMappaSlotId);
				if (!stillValid && this.mappaTimeSlots.length > 0) {
					this.selectedMappaSlotId = this.mappaTimeSlots[0].bkts_id;
				}
			},
			error: () => { this.mappaTimeSlots = []; },
		});
	}

	onMappaSlotChange() {
		// Filtro slot è client-side. Riapplica gli override (cluster) della nuova fascia.
		this._buildDisplayPositionsFromOverrides();
	}

	/** mappaBookings filtrato per fascia oraria selezionata (se selectedMappaSlotId è valorizzato) */
	get filteredMappaBookings(): Booking[] {
		if (!this.selectedMappaSlotId) return this.mappaBookings;
		return this.mappaBookings.filter(b => b.bkb_time_slot_id === this.selectedMappaSlotId);
	}

	/** Set di ID risorse occupate (qualsiasi booking non-CANCELLED) per <app-resource-map>.
	 *  Considera SIA bkb_resource_id (primario) SIA bkb_resource_ids (tavoli accorpati
	 *  via bk_resource_assignment) → tutti i tavoli di un cluster risultano "occupati".
	 *  Filtrato per fascia oraria selezionata se selectedMappaSlotId è valorizzato. */
	get mappaBookedResourceIds(): Set<number> {
		const ids = new Set<number>();
		for (const b of this.filteredMappaBookings) {
			if (b.bkb_resource_id) ids.add(b.bkb_resource_id);
			if (b.bkb_resource_ids) for (const id of b.bkb_resource_ids) ids.add(id);
		}
		return ids;
	}

	/** Callback passata a <app-resource-map>: etichetta extra sotto la risorsa (nome cliente) */
	getMappaResourceLabel = (r: BookingResource): string | null => {
		const bks = this.getBookingsForResource(r);
		if (bks.length === 0) return null;
		return bks[0].bkb_customer_name || null;
	};

	/** Palette di colori deterministici per i gruppi prenotazione (RESTAURANT) */
	private readonly _bookingGroupPalette = [
		'#1976d2', '#e53935', '#43a047', '#fb8c00', '#8e24aa', '#00acc1',
		'#fbc02d', '#5e35b1', '#3949ab', '#d81b60', '#00897b', '#f4511e',
	];

	private _bookingGroupColor(bkbId: number): string {
		return this._bookingGroupPalette[bkbId % this._bookingGroupPalette.length];
	}

	/**
	 * Callback per <app-resource-map>: info gruppo prenotazione RESTAURANT.
	 * - bordo colorato deterministico per ogni bkb_id
	 * - label cliente + ospiti, mostrata solo sul "tavolo primario" (bkb_resource_id)
	 * - resourceAlias + isClusterMember: il primario di un cluster ≥2 tavoli mostra l'alias
	 *   al posto del nome tavolo; i tavoli secondari nascondono l'etichetta.
	 */
	getMappaBookingGroupInfo = (r: BookingResource): { groupColor: string; customerLabel: string; isPrimary: boolean; resourceAlias: string | null; isClusterMember: boolean } | null => {
		const bks = this.getBookingsForResource(r);
		if (bks.length === 0) return null;
		const bk = bks[0];
		const clusterSize = bk.bkb_resource_ids?.length ?? 0;
		const isClusterMember = clusterSize >= 2;
		return {
			groupColor: this._bookingGroupColor(bk.bkb_id),
			customerLabel: `${bk.bkb_customer_name || '—'}${bk.bkb_guests_count ? ' · ' + bk.bkb_guests_count + ' posti' : ''}`,
			isPrimary: bk.bkb_resource_id === r.bkr_id,
			resourceAlias: (isClusterMember && bk.bkb_resource_alias) ? bk.bkb_resource_alias : null,
			isClusterMember,
		};
	};

	/** Etichetta risorsa da mostrare nelle card prenotazione (alias se cluster ≥2 con alias, altrimenti bkr_name). */
	getBookingResourceLabel(b: Booking): string {
		if ((b.bkb_resource_ids?.length ?? 0) >= 2 && b.bkb_resource_alias) return b.bkb_resource_alias;
		return b.bkr_name || '';
	}

	/**
	 * Ordina le risorse alfanumericamente ma raggruppa quelle collegate dalla
	 * stessa prenotazione (bkb_resource_ids con più di un id). Ogni gruppo
	 * eredita come "chiave d'ordine" il nome del suo membro più basso.
	 */
	private _sortResourcesGrouped(resources: BookingResource[], bookings: Booking[]): BookingResource[] {
		const idToName = new Map<number, string>(resources.map(r => [r.bkr_id, r.bkr_name]));
		const primaryName = new Map<number, string>();
		const nameCmp = (a: string, b: string) => a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });
		for (const b of bookings) {
			const ids = (b.bkb_resource_ids && b.bkb_resource_ids.length > 0)
				? b.bkb_resource_ids
				: (b.bkb_resource_id ? [b.bkb_resource_id] : []);
			if (ids.length <= 1) continue;
			const groupNames = ids.map(id => idToName.get(id)).filter((n): n is string => !!n);
			if (groupNames.length === 0) continue;
			const primary = groupNames.slice().sort(nameCmp)[0];
			for (const id of ids) {
				const prev = primaryName.get(id);
				if (!prev || nameCmp(primary, prev) < 0) primaryName.set(id, primary);
			}
		}
		return resources.slice().sort((a, b) => {
			const pa = primaryName.get(a.bkr_id) ?? a.bkr_name;
			const pb = primaryName.get(b.bkr_id) ?? b.bkr_name;
			const cmp = nameCmp(pa, pb);
			if (cmp !== 0) return cmp;
			return nameCmp(a.bkr_name, b.bkr_name);
		});
	}

	onMappaTypeChange() {
		this.loadMappaData();
	}

	onMappaDateChange() {
		this.loadMappaData();
	}

	get selectedMappaType(): BookingResourceType | null {
		return this.mappaResourceTypes.find(t => t.bkrt_id === this.mappaTypeId) || null;
	}

	getBookingsForResource(r: BookingResource): Booking[] {
		return this.filteredMappaBookings.filter(b =>
			b.bkb_resource_id === r.bkr_id ||
			(b.bkb_resource_ids && b.bkb_resource_ids.includes(r.bkr_id))
		);
	}

	getResourceStatus(r: BookingResource): 'free' | 'busy' | 'pending' {
		const bks = this.getBookingsForResource(r);
		if (bks.length === 0) return 'free';
		if (bks.some(b => b.bkb_status === 'CONFIRMED' || b.bkb_status === 'COMPLETED')) return 'busy';
		return 'pending';
	}

	getResourceCapacity(r: BookingResource): number {
		return r.bkr_capacity || 0;
	}

	async showMappaResourceActions(r: BookingResource) {
		const bks = this.getBookingsForResource(r);

		// Una sola prenotazione → apri direttamente le azioni del booking
		if (bks.length === 1) {
			return this.showManagerBookingActions(bks[0]);
		}

		const buttons: any[] = [];
		if (bks.length === 0) {
			buttons.push({
				text: 'Risorsa libera',
				icon: 'checkmark-circle-outline',
				cssClass: 'free-info',
				handler: () => {},
			});
		} else {
			for (const b of bks) {
				const label = `${b.bkb_customer_name || '—'} · ${this.getStatusLabel(b.bkb_status)}`;
				buttons.push({
					text: label,
					icon: 'open-outline',
					handler: () => this.showManagerBookingActions(b),
				});
			}
		}
		buttons.push({ text: 'Chiudi', role: 'cancel', icon: 'chevron-down-outline' });
		const sheet = await this.actionSheetCtrl.create({
			header: r.bkr_name,
			subHeader: r.bkr_capacity > 0 ? `Capienza: ${r.bkr_capacity}` : undefined,
			buttons,
		});
		await sheet.present();
	}

	// ── Tab Ordini — replica web getOrdini() ──────────────────────────
	getOrdini() {
		this.ordiniLoading = true;
		// Backend non supporta multi-status: carichiamo tutti gli items del periodo e
		// filtriamo lato client tramite ordiniStatusFilter (vedi filteredOrdiniList).
		this.bookingService.GetAllBookingItems(
			this.cmp_id,
			this.ordiniFilters.dateFrom,
			this.ordiniFilters.dateTo,
			'',
			1,
			500,
		).subscribe({
			next: (items) => {
				const list = items || [];
				const uniqueBookingIds = Array.from(new Set(list.map(i => i.bkbi_booking_id)));
				if (uniqueBookingIds.length === 0) {
					this.ordiniList = list;
					this.ordiniLoading = false;
					return;
				}
				forkJoin(uniqueBookingIds.map(id => this.bookingService.GetBookingById(id))).subscribe({
					next: (bookings) => {
						this._bookingTypeMap = new Map();
						for (const b of bookings || []) {
							if (b && b.bkrt_type) this._bookingTypeMap.set(b.bkb_id, b.bkrt_type);
						}
						this.ordiniList = list.map(i => ({
							...i,
							bkrt_type: this._bookingTypeMap.get(i.bkbi_booking_id) || '',
						}));
						this.ordiniLoading = false;
					},
					error: () => {
						// Anche se la mappatura fallisce mostra comunque la lista
						this.ordiniList = list;
						this.ordiniLoading = false;
					},
				});
			},
			error: (err) => {
				this.ordiniLoading = false;
				this.configService.presentToastError(err);
			},
		});
	}

	onOrdiniFilterChange() {
		this.getOrdini();
	}

	clearOrdiniFilters() {
		const today = new Date().toISOString().substring(0, 10);
		this.ordiniFilters = { dateFrom: today, dateTo: today };
		this.ordiniTypeFilter = '';
		this.ordiniStatusFilter = new Set(['ORDERED', 'READY']);
		this.getOrdini();
	}

	/** Toggle di un filtro stato (chip multi-select) */
	toggleOrdiniStatus(key: 'CART' | 'ORDERED' | 'READY' | 'ELABORATE') {
		if (this.ordiniStatusFilter.has(key)) this.ordiniStatusFilter.delete(key);
		else                                   this.ordiniStatusFilter.add(key);
	}

	isOrdiniStatusSelected(key: 'CART' | 'ORDERED' | 'READY' | 'ELABORATE'): boolean {
		return this.ordiniStatusFilter.has(key);
	}

	/** Famiglia di appartenenza di uno status item (per multi-filter) */
	private _statusFamily(notes: string): 'CART' | 'ORDERED' | 'READY' | 'ELABORATE' | 'OTHER' {
		const s = this.parseItemNotes(notes).status;
		if (s === 'CART') return 'CART';
		if (['READY', 'PAID_READY', 'PARTIAL_READY'].includes(s)) return 'READY';
		if (['ELABORATE', 'PAID_ELABORATE', 'PARTIAL_ELABORATE'].includes(s)) return 'ELABORATE';
		if (['ORDERED', 'PAID', 'PARTIAL'].includes(s)) return 'ORDERED';
		return 'OTHER';
	}

	/** Formatta una data ISO/array in dd/MM HH:mm per la card ordini */
	formatOrdineDate(d: any): string {
		if (!d) return '';
		let dt: Date;
		if (Array.isArray(d)) {
			const [y, m, day, h = 0, min = 0] = d as number[];
			dt = new Date(y, m - 1, day, h, min);
		} else {
			dt = new Date(d);
		}
		if (isNaN(dt.getTime())) return '';
		const pad = (n: number) => String(n).padStart(2, '0');
		return `${pad(dt.getDate())}/${pad(dt.getMonth() + 1)} ${pad(dt.getHours())}:${pad(dt.getMinutes())}`;
	}

	/** Lista ordini filtrata per tipo risorsa (Spiaggia/Ristorante) e stato multi-select */
	get filteredOrdiniList(): BookingItem[] {
		return this.ordiniList.filter(i => {
			if (this.ordiniTypeFilter && i.bkrt_type !== this.ordiniTypeFilter) return false;
			if (this.ordiniStatusFilter.size > 0) {
				const fam = this._statusFamily(i.bkbi_notes);
				if (fam === 'OTHER') return false;
				if (!this.ordiniStatusFilter.has(fam)) return false;
			}
			return true;
		});
	}

	get ordiniCountBeach(): number {
		return this._countAfterStatus(i => i.bkrt_type === 'BEACH');
	}

	get ordiniCountRestaurant(): number {
		return this._countAfterStatus(i => i.bkrt_type === 'RESTAURANT');
	}

	getOrdiniCountByStatus(key: 'CART' | 'ORDERED' | 'READY' | 'ELABORATE'): number {
		return this.ordiniList.filter(i => {
			if (this.ordiniTypeFilter && i.bkrt_type !== this.ordiniTypeFilter) return false;
			return this._statusFamily(i.bkbi_notes) === key;
		}).length;
	}

	private _countAfterStatus(predicate: (i: BookingItem) => boolean): number {
		return this.ordiniList.filter(i => {
			if (!predicate(i)) return false;
			if (this.ordiniStatusFilter.size > 0) {
				const fam = this._statusFamily(i.bkbi_notes);
				if (fam === 'OTHER') return false;
				if (!this.ordiniStatusFilter.has(fam)) return false;
			}
			return true;
		}).length;
	}

	/** Raggruppa gli ordini filtrati per prenotazione + send_seq. */
	get groupedOrdiniList(): { bookingId: number; sendSeq: number; bkrName: string; customerName: string; bkrtType: string; createdAt: any; items: BookingItem[]; total: number; allReady: boolean }[] {
		const groups = new Map<string, { bookingId: number; sendSeq: number; bkrName: string; customerName: string; bkrtType: string; createdAt: any; items: BookingItem[]; total: number; allReady: boolean }>();
		for (const i of this.filteredOrdiniList) {
			const key = `${i.bkbi_booking_id}|${i.bkbi_send_seq ?? 0}`;
			let g = groups.get(key);
			if (!g) {
				g = {
					bookingId: i.bkbi_booking_id,
					sendSeq: i.bkbi_send_seq ?? 0,
					bkrName: i.bkr_name || ('#' + i.bkbi_booking_id),
					customerName: i.bkb_customer_name || '',
					bkrtType: i.bkrt_type || '',
					createdAt: i.bkbi_created_at,
					items: [],
					total: 0,
					allReady: true,
				};
				groups.set(key, g);
			}
			g.items.push(i);
			g.total += Number(i.bkbi_total_price) || 0;
			const status = this.parseItemNotes(i.bkbi_notes).status;
			if (!['READY', 'PAID_READY', 'PARTIAL_READY'].includes(status)) g.allReady = false;
		}
		return Array.from(groups.values()).sort((a, b) => {
			// Più recenti per primi
			const ta = a.createdAt ? new Date(a.createdAt).getTime() : 0;
			const tb = b.createdAt ? new Date(b.createdAt).getTime() : 0;
			return tb - ta;
		});
	}

	/** Consegna tutti gli items pronti di un gruppo (replica web consegnaTutto). */
	consegnaTutto(bookingId: number, sendSeq: number) {
		const items = this.ordiniList.filter(i => i.bkbi_booking_id === bookingId && (i.bkbi_send_seq ?? 0) === sendSeq);
		let done = 0;
		items.forEach(item => {
			this.consegna(item);
			done++;
		});
		if (done === 0) this.configService.presentToastError('Nessun ordine da consegnare in questo gruppo');
	}

	/** Estrae status + metodo pagamento dalle note dell'item (es. 'ORDERED:CASH' → {status:'ORDERED', method:'CASH'}) */
	parseItemNotes(notes: string | null | undefined): { status: string; method: string } {
		if (!notes) return { status: 'ORDERED', method: '' };
		const parts = notes.split(':');
		return { status: parts[0] || 'ORDERED', method: parts[1] || '' };
	}

	getOrdineStatusColor(notes: string): string {
		switch (this.parseItemNotes(notes).status) {
			case 'CART':              return 'medium';
			case 'ORDERED':           return 'warning';
			case 'READY':
			case 'PAID_READY':
			case 'PARTIAL_READY':     return 'tertiary';
			case 'ELABORATE':
			case 'PAID_ELABORATE':
			case 'PARTIAL_ELABORATE': return 'success';
			case 'PAID':              return 'success';
			case 'PARTIAL':           return 'warning';
			default:                  return 'medium';
		}
	}

	/** Marca l'item come PRONTO (cucina/bar). Preserva il prefisso PAID/PARTIAL e il metodo pagamento. */
	marcaPronto(item: BookingItem) {
		const { status, method } = this.parseItemNotes(item.bkbi_notes);
		let newNotes: string;
		if (status === 'PAID')         newNotes = 'PAID_READY';
		else if (status === 'PARTIAL') newNotes = 'PARTIAL_READY';
		else                            newNotes = method ? `READY:${method}` : 'READY';
		this.bookingService.UpdateBookingItemNotes(item.bkbi_id, newNotes).subscribe({
			next: () => { item.bkbi_notes = newNotes; this.configService.presentToastSuccess('Segnato pronto'); },
			error: (err) => this.configService.presentToastError(err),
		});
	}

	/** Marca l'item come CONSEGNATO (passaggio finale). */
	consegna(item: BookingItem) {
		const { status, method } = this.parseItemNotes(item.bkbi_notes);
		let newNotes: string;
		if (status === 'PAID' || status === 'PAID_READY')         newNotes = 'PAID_ELABORATE';
		else if (status === 'PARTIAL' || status === 'PARTIAL_READY') newNotes = 'PARTIAL_ELABORATE';
		else                                                          newNotes = method ? `ELABORATE:${method}` : 'ELABORATE';
		this.bookingService.UpdateBookingItemNotes(item.bkbi_id, newNotes).subscribe({
			next: () => { item.bkbi_notes = newNotes; this.configService.presentToastSuccess('Consegnato'); },
			error: (err) => this.configService.presentToastError(err),
		});
	}

	/** Riporta indietro lo stato (ELABORATE→READY, READY→ORDERED). Utile in caso di errore manager. */
	rimetti(item: BookingItem) {
		const { status, method } = this.parseItemNotes(item.bkbi_notes);
		let newNotes: string;
		if (status === 'PAID_ELABORATE')         newNotes = 'PAID_READY';
		else if (status === 'PARTIAL_ELABORATE') newNotes = 'PARTIAL_READY';
		else if (status === 'ELABORATE')          newNotes = method ? `READY:${method}` : 'READY';
		else if (status === 'PAID_READY')         newNotes = 'PAID';
		else if (status === 'PARTIAL_READY')      newNotes = 'PARTIAL';
		else if (status === 'READY')              newNotes = method ? `ORDERED:${method}` : 'ORDERED';
		else return;
		this.bookingService.UpdateBookingItemNotes(item.bkbi_id, newNotes).subscribe({
			next: () => { item.bkbi_notes = newNotes; this.configService.presentToastSuccess('Stato riportato indietro'); },
			error: (err) => this.configService.presentToastError(err),
		});
	}

	async showOrdineActions(item: BookingItem) {
		const { status } = this.parseItemNotes(item.bkbi_notes);
		const isReady = ['READY', 'PAID_READY', 'PARTIAL_READY'].includes(status);
		const isElaborate = ['ELABORATE', 'PAID_ELABORATE', 'PARTIAL_ELABORATE'].includes(status);
		const isOrdered = ['ORDERED', 'PAID', 'PARTIAL'].includes(status);

		const buttons: any[] = [];
		if (isOrdered) {
			buttons.push({
				text: 'Segna pronto',
				icon: 'checkmark-circle-outline',
				handler: () => this.marcaPronto(item),
			});
		}
		if (isOrdered || isReady) {
			buttons.push({
				text: 'Segna consegnato',
				icon: 'checkmark-done-outline',
				handler: () => this.consegna(item),
			});
		}
		if (isReady || isElaborate) {
			buttons.push({
				text: 'Rimetti indietro',
				icon: 'arrow-undo-outline',
				handler: () => this.rimetti(item),
			});
		}
		buttons.push({
			text: 'Apri prenotazione',
			icon: 'open-outline',
			handler: () => this.openDetail(item.bkbi_booking_id),
		});
		buttons.push({ text: 'Chiudi', role: 'cancel', icon: 'chevron-down-outline' });

		const sheet = await this.actionSheetCtrl.create({
			header: `${item.bkmi_name} × ${item.bkbi_quantity}`,
			subHeader: `${this.getOrdineStatusLabel(item.bkbi_notes)} · ${item.bkr_name || ''}`,
			buttons,
		});
		await sheet.present();
	}

	/** Etichetta utente-friendly del metodo di pagamento associato all'item (vuoto se non scelto) */
	getItemPaymentMethodLabel(notes: string): string {
		const m = this.parseItemNotes(notes).method;
		switch (m) {
			case 'CASH':     return 'Contanti';
			case 'CARD':     return 'Carta';
			case 'TRANSFER': return 'Bonifico';
			case 'OTHER':    return 'Altro';
			default:         return '';
		}
	}

	getItemPaymentMethodIcon(notes: string): string {
		const m = this.parseItemNotes(notes).method;
		switch (m) {
			case 'CASH':     return 'cash-outline';
			case 'CARD':     return 'card-outline';
			case 'TRANSFER': return 'swap-horizontal-outline';
			case 'OTHER':    return 'ellipsis-horizontal-outline';
			default:         return '';
		}
	}

	getOrdineStatusLabel(notes: string): string {
		const { status } = this.parseItemNotes(notes);
		switch (status) {
			case 'CART':              return 'Carrello';
			case 'ORDERED':           return 'Ordinato';
			case 'READY':             return 'Pronto';
			case 'PAID_READY':        return 'Pronto · pagato';
			case 'PARTIAL_READY':     return 'Pronto · parz.';
			case 'ELABORATE':         return 'Consegnato';
			case 'PAID_ELABORATE':    return 'Consegnato · pagato';
			case 'PARTIAL_ELABORATE': return 'Consegnato · parz.';
			case 'PAID':              return 'Pagato';
			case 'PARTIAL':           return 'Pagato parz.';
			default:                  return status || 'Ordinato';
		}
	}

	clearManagerFilters() {
		const today = new Date().toISOString().substring(0, 10);
		this.managerFilters = {
			dateFrom: today,
			dateTo: today,
			status: '',
			paymentStatus: '',
			typeId: 0,
			timeSlotId: 0,
		};
		const restaurantType = this.managerResourceTypes.find(t => t.bkrt_type === 'RESTAURANT');
		if (restaurantType) {
			this.managerFilters.typeId = restaurantType.bkrt_id;
			this.loadManagerTimeSlots();
		} else {
			this.managerTimeSlots = [];
			this.loadCompanyBookings();
		}
	}

	hasNonDefaultManagerFilters(): boolean {
		const today = new Date().toISOString().substring(0, 10);
		if (this.managerFilters.dateFrom !== today) return true;
		if (this.managerFilters.dateTo !== today) return true;
		if (this.managerFilters.status) return true;
		if (this.managerFilters.paymentStatus) return true;
		return false;
	}

	/** Calcola lo stato di pagamento di una prenotazione in base ai totali */
	getPaymentStatus(b: Booking): 'PAID' | 'UNPAID' | 'PARTIAL' | 'NONE' {
		const total = Number(b.bkb_total_amount) || 0;
		const paid = Number(b.bkb_paid_amount) || 0;
		if (total <= 0 && paid <= 0) return 'NONE';
		if (paid <= 0)                return 'UNPAID';
		if (paid >= total)            return 'PAID';
		return 'PARTIAL';
	}

	getPaymentStatusLabel(b: Booking): string {
		switch (this.getPaymentStatus(b)) {
			case 'PAID':    return 'Pagato';
			case 'PARTIAL': return 'Parziale';
			case 'UNPAID':  return 'Da pagare';
			case 'NONE':    return 'Nessun importo';
		}
	}

	getPaymentStatusIcon(b: Booking): string {
		switch (this.getPaymentStatus(b)) {
			case 'PAID':    return 'checkmark-circle';
			case 'PARTIAL': return 'time-outline';
			case 'UNPAID':  return 'alert-circle';
			case 'NONE':    return 'remove-circle-outline';
		}
	}

	private _paymentStatusOf(b: Booking): 'PAID' | 'UNPAID' | 'PARTIAL' | 'NONE' {
		return this.getPaymentStatus(b);
	}

	/** Filtro per stato di pagamento (lato client perché PARTIAL non è uno stato discreto in DB) */
	private _matchesPaymentFilter(b: Booking): boolean {
		const f = this.managerFilters.paymentStatus;
		if (!f) return true;
		return this._paymentStatusOf(b) === f;
	}

	/** Filtro per fascia oraria (lato client, attivo solo se è stata selezionata una fascia) */
	private _matchesTimeSlotFilter(b: Booking): boolean {
		if (!this.managerFilters.timeSlotId) return true;
		return b.bkb_time_slot_id === this.managerFilters.timeSlotId;
	}

	/** Tab "Lista": tutte le prenotazioni della company esclusi i placeholder PENDING da assegnare */
	get managerListBookings(): Booking[] {
		return this.companyBookings.filter(b =>
			!this._isPlaceholderPending(b) &&
			this._matchesPaymentFilter(b) &&
			this._matchesTimeSlotFilter(b)
		);
	}

	/** Tab "Da assegnare": ristoranti PENDING che puntano alla risorsa _DA_ASSEGNARE_ */
	get managerUnassignedBookings(): Booking[] {
		return this.companyBookings.filter(b =>
			this._isPlaceholderPending(b) &&
			this._matchesPaymentFilter(b) &&
			this._matchesTimeSlotFilter(b)
		);
	}

	private _isPlaceholderPending(b: Booking): boolean {
		if (b.bkb_status !== 'PENDING') return false;
		// Heuristic: il placeholder ha bkr_name = 'Da assegnare' (vedi migration _DA_ASSEGNARE_)
		return (b.bkr_name || '').toLowerCase().includes('da assegnare');
	}

	// ── Azioni stato manager ──────────────────────────────────────────
	async confirmBooking(b: Booking) {
		this.bookingService.UpdateBookingStatus(b.bkb_id, 'CONFIRMED').subscribe({
			next: () => { this.configService.presentToastSuccess('Prenotazione confermata'); this.loadCompanyBookings(); },
			error: (err) => this.configService.presentToastError(err),
		});
	}

	async completeBooking(b: Booking) {
		this.bookingService.UpdateBookingStatus(b.bkb_id, 'COMPLETED').subscribe({
			next: () => { this.configService.presentToastSuccess('Prenotazione completata'); this.loadCompanyBookings(); },
			error: (err) => this.configService.presentToastError(err),
		});
	}

	async cancelBookingManager(b: Booking) {
		const sheet = await this.actionSheetCtrl.create({
			header: 'Annullare la prenotazione?',
			subHeader: `${b.bkb_customer_name || ''} · ${this.formatDate(b.bkb_date)}`,
			buttons: [
				{
					text: 'Sì, annulla',
					role: 'destructive',
					icon: 'trash-outline',
					handler: () => {
						this.bookingService.UpdateBookingStatus(b.bkb_id, 'CANCELLED').subscribe({
							next: () => { this.configService.presentToastSuccess('Prenotazione annullata'); this.loadCompanyBookings(); },
							error: (err) => this.configService.presentToastError(err),
						});
					},
				},
				{ text: 'No, torna indietro', role: 'cancel', icon: 'arrow-back-outline' },
			],
		});
		await sheet.present();
	}

	/** Click sulla card manager → action sheet con dettaglio + azioni */
	async showManagerBookingActions(b: Booking) {
		const buttons: any[] = [
			{
				text: 'Apri dettaglio',
				icon: 'open-outline',
				handler: () => this.openDetail(b.bkb_id),
			},
			{
				text: 'Ordini',
				icon: 'restaurant-outline',
				handler: () => this.router.navigate(
					['/portale', this.prt_id, 'company', this.cmp_id, 'service', 'prenotazioni', 'dettaglio', b.bkb_id],
					{ queryParams: { tab: 'ordine' } },
				),
			},
			{
				text: 'Cassa',
				icon: 'card-outline',
				handler: () => this.router.navigate(
					['/portale', this.prt_id, 'company', this.cmp_id, 'service', 'prenotazioni', 'dettaglio', b.bkb_id],
					{ queryParams: { tab: 'cassa' } },
				),
			},
		];
		if (b.bkb_status === 'PENDING') {
			buttons.push({
				text: 'Conferma',
				icon: 'checkmark-circle-outline',
				handler: () => this.confirmBooking(b),
			});
		}
		if (b.bkb_status === 'CONFIRMED') {
			buttons.push({
				text: 'Completa',
				icon: 'checkmark-done-outline',
				handler: () => this.completeBooking(b),
			});
		}
		if (b.bkb_status !== 'CANCELLED' && b.bkb_status !== 'COMPLETED') {
			buttons.push({
				text: 'Annulla',
				icon: 'close-circle-outline',
				role: 'destructive',
				handler: () => this.cancelBookingManager(b),
			});
		}
		buttons.push({
			text: 'Fattura prenotazione',
			icon: 'document-text-outline',
			handler: () => this.scaricaFattura(b, 'BOOKING'),
		});
		buttons.push({
			text: 'Fattura ordine',
			icon: 'receipt-outline',
			handler: () => this.scaricaFattura(b, 'BOOKING_ORDER'),
		});
		buttons.push({ text: 'Chiudi', role: 'cancel', icon: 'chevron-down-outline' });

		const sheet = await this.actionSheetCtrl.create({
			header: b.bkb_customer_name || 'Prenotazione',
			subHeader: `${this.formatDate(b.bkb_date)} · ${this.getStatusLabel(b.bkb_status)}`,
			buttons,
		});
		await sheet.present();
	}

	/**
	 * Scarica la fattura di cortesia (module BOOKING = prenotazione, BOOKING_ORDER = ordini).
	 * Se non esiste, avvisa.
	 */
	scaricaFattura(b: Booking, module: 'BOOKING' | 'BOOKING_ORDER') {
		this.fatturaService.getInvoiceStatus(this.cmp_id, module, b.bkb_id).subscribe((st) => {
			if (st?.esiste && st.fatId) this._downloadFattura(st.fatId);
			else this.configService.presentToastError(
				module === 'BOOKING' ? 'Nessuna fattura prenotazione disponibile' : 'Nessuna fattura ordine disponibile');
		});
	}

	private _downloadFattura(fatId: number) {
		this.fatturaService.printInvoice(fatId).subscribe({
			next: (blob) => this.fatturaService.openPdf(blob),
			error: () => this.configService.presentToastError('Errore nella stampa fattura'),
		});
	}

	loadActiveBookings() {
		this.loading = true;
		const today = new Date().toISOString().split('T')[0];

		this.bookingService.GetBookingListByUser(this.cmp_id, '', '', '', 1, 500).subscribe({
			next: (res) => {
				this.loading = false;
				const all = res || [];
				this.activeBookings = all.filter((b) => {
					if (b.bkb_status === 'CANCELLED') return false;
					const dt = this.toDateStr(b.bkb_date_to) || this.toDateStr(b.bkb_date);
					return dt >= today;
				});
				this.pastBookings = all.filter((b) => {
					if (b.bkb_status === 'CANCELLED') return false;
					const dt = this.toDateStr(b.bkb_date_to) || this.toDateStr(b.bkb_date);
					return dt < today;
				});
				this.cancelledBookings = all.filter((b) => b.bkb_status === 'CANCELLED');
			},
			error: (err) => {
				this.loading = false;
				this.configService.presentToastError(err);
			},
		});
	}

	// ---------- Navigazione ----------

	onPrenotaClick() {
		this.router.navigate(['/portale', this.prt_id, 'company', this.cmp_id, 'service', 'prenotazioni', 'form']);
	}

	openDetail(bkb_id: number) {
		this.router.navigate(['/portale', this.prt_id, 'company', this.cmp_id, 'service', 'prenotazioni', 'dettaglio', bkb_id]);
	}

	openMenu(bkb_id: number) {
		this.router.navigate(
			['/portale', this.prt_id, 'company', this.cmp_id, 'service', 'prenotazioni', 'dettaglio', bkb_id],
			{ queryParams: { tab: 'ordine' } },
		);
	}

	async showBookingActions(booking: Booking) {
		const sheet = await this.actionSheetCtrl.create({
			header: booking.bkr_name ? `Ombrellone ${booking.bkr_name}` : 'Prenotazione',
			subHeader: `${this.formatDate(booking.bkb_date)} – ${this.formatDate(booking.bkb_date_to || booking.bkb_date)}`,
			buttons: [
				{
					text: 'Dettaglio prenotazione',
					icon: 'calendar-outline',
					handler: () => this.openDetail(booking.bkb_id),
				},
				{
					text: 'Ordina dal menu',
					icon: 'restaurant-outline',
					handler: () => this.openMenu(booking.bkb_id),
				},
				{
					text: 'Paga alla cassa',
					icon: 'card-outline',
					handler: () => this.router.navigate(
						['/portale', this.prt_id, 'company', this.cmp_id, 'service', 'prenotazioni', 'dettaglio', booking.bkb_id],
						{ queryParams: { tab: 'cassa' } },
					),
				},
				{
					text: 'Annulla prenotazione',
					icon: 'close-circle-outline',
					role: 'destructive',
					handler: () => this.confirmCancel(booking),
				},
				{
					text: 'Chiudi',
					role: 'cancel',
					icon: 'chevron-down-outline',
				},
			],
		});
		await sheet.present();
	}

	async confirmCancel(booking: Booking) {
		const sheet = await this.actionSheetCtrl.create({
			header: 'Annullare la prenotazione?',
			subHeader: 'Questa azione non può essere annullata.',
			buttons: [
				{
					text: 'Sì, annulla',
					role: 'destructive',
					icon: 'trash-outline',
					handler: () => {
						this.bookingService.UpdateBookingStatus(booking.bkb_id, 'CANCELLED').subscribe({
							next: () => {
								this.configService.presentToastSuccess('Prenotazione annullata');
								this.loadActiveBookings();
							},
							error: (err) => this.configService.presentToastError(err),
						});
					},
				},
				{ text: 'No, torna indietro', role: 'cancel', icon: 'arrow-back-outline' },
			],
		});
		await sheet.present();
	}

	async scanQrCode() {
		try {
			const result = await CapacitorBarcodeScanner.scanBarcode({
				hint: CapacitorBarcodeScannerTypeHint.QR_CODE,
			});
			if (!result.ScanResult) return;

			this.bookingService.GetBookingListByUser(this.cmp_id, '', '', '', 1, 500).subscribe({
				next: (res) => {
					const bookings = (res || []).filter(
						(b: Booking) =>
							b.bkb_resource_id === +result.ScanResult &&
							(b.bkb_status === 'PENDING' || b.bkb_status === 'CONFIRMED'),
					);
					if (bookings.length > 0) {
						this.showBookingActions(bookings[0]);
					} else {
						this.configService.presentToastError('Nessuna prenotazione attiva trovata per questa risorsa.');
					}
				},
				error: (err) => this.configService.presentToastError(err),
			});
		} catch (e: any) {
			this.configService.presentToastError('Impossibile avviare lo scanner: ' + e?.message);
		}
	}

	// ---------- Azioni rapide company ----------

	callCompany(): void {
		if (this.company?.cmp_phone_number) {
			window.open(`tel:${this.company.cmp_phone_number}`, '_system');
		}
	}

	openWebsite(): void {
		if (this.company?.cmp_web) {
			let url = this.company.cmp_web;
			if (!url.startsWith('http')) {
				url = 'https://' + url;
			}
			window.open(url, '_system');
		}
	}

	getDirections(): void {
		const addr = this.getCompanyAddress();
		if (addr) {
			window.open(`https://maps.google.com/maps?q=${encodeURIComponent(addr)}`, '_system');
		}
	}

	// ---------- Chat ----------

	openChat(): void {
		this.chatMessage = '';
		this.chatSending = false;
		this.chatModal.present();
	}

	closeChatModal(): void {
		this.chatModal.dismiss();
	}

	sendChatMessage(): void {
		const text = this.chatMessage.trim();
		this.chatSending = true;
		this.messagingService.findOrCreateConversationByCompany(this.cmp_id).subscribe({
			next: (conv) => {
				const navigate = () => {
					this.chatSending = false;
					this.chatModal.dismiss();
					this.router.navigate(['/messaging/chat', conv.conv_id], {
						state: {
							chatWith: conv.other_company_name || this.company?.cmp_name || this.company?.name || 'Chat',
							isCompany: true,
						},
					});
				};
				if (text) {
					this.messagingService.sendMessage(conv.conv_id, text).subscribe({ next: navigate, error: navigate });
				} else {
					navigate();
				}
			},
			error: () => {
				this.chatSending = false;
				this.configService.presentToastError('Impossibile avviare la chat con questa azienda');
			},
		});
	}

	getCompanyInitials(): string {
		const name: string = this.company?.name || this.company?.cmp_name || '';
		return name
			.split(' ')
			.slice(0, 2)
			.map((w: string) => w.charAt(0).toUpperCase())
			.join('');
	}

	getCompanyAvatarGradient(): string {
		const name: string = this.company?.name || this.company?.cmp_name || 'A';
		const hue = name.split('').reduce((acc, c) => acc + c.charCodeAt(0), 0) % 360;
		return `linear-gradient(135deg, hsl(${hue}, 65%, 50%), hsl(${(hue + 40) % 360}, 75%, 40%))`;
	}

	getCompanyAddress(): string {
		if (!this.company) return '';
		const of = this.company.of;
		if (of) {
			const parts = [of.of_address, of.of_number, of.of_cap, of.of_municipality_name].filter(Boolean);
			if (parts.length > 0) return parts.join(', ');
		}
		const parts = [
			this.company.cmp_rlegal_res_address,
			this.company.cmp_rlegal_res_number,
			this.company.cmp_rlegal_res_cup,
			this.company.cmp_rlegal_res_city,
		].filter(Boolean);
		return parts.join(', ');
	}

	// ---------- Helpers ----------

	toDateStr(d: any): string {
		if (!d) return '';
		if (typeof d === 'number') {
			const dt = new Date(d);
			return `${dt.getFullYear()}-${String(dt.getMonth() + 1).padStart(2, '0')}-${String(dt.getDate()).padStart(2, '0')}`;
		}
		if (Array.isArray(d)) {
			const [y, m, day] = d as number[];
			return `${y}-${String(m).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
		}
		return String(d).substring(0, 10);
	}

	formatDate(d?: any): string {
		const s = this.toDateStr(d);
		if (!s) return '';
		const [y, m, day] = s.split('-');
		return `${day}/${m}/${y}`;
	}

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

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

	getStatusAccentColor(status: string): string {
		switch (status) {
			case 'CONFIRMED': return 'var(--ion-color-success)';
			case 'PENDING':   return 'var(--ion-color-warning)';
			case 'COMPLETED': return 'var(--ion-color-medium)';
			case 'CANCELLED': return 'var(--ion-color-danger)';
			default:           return 'var(--ion-color-primary)';
		}
	}

	get currentList(): Booking[] {
		switch (this.activeTab) {
			case 'past':      return this.pastBookings;
			case 'cancelled': return this.cancelledBookings;
			default:           return this.activeBookings;
		}
	}
}
