// 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 { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { ConfigService } from './config.service';
import {
	Booking,
	BookingBeachPricing,
	BookingItem,
	BookingPayment,
	BookingResource,
	BookingResourceAssignment,
	BookingResourceSlotOverride,
	BookingResourceType,
	BookingMenuCategory,
	BookingMenuItem,
	BookingTimeSlot,
} from '../models/booking.model';

@Injectable({
	providedIn: 'root',
})
export class BookingService {
	constructor(private http: HttpClient, private configService: ConfigService) {}

	GetBookingList(
		cmp_id: number,
		dateFrom?: string,
		dateTo?: string,
		status?: string,
		type_id?: number,
		orderby: string = 'bkb_date',
		limitda: number = 100,
		limitper: number = 100,
	): Observable<Booking[]> {
		let url = this.configService.bookingUrl + 'booking/list?cmp_id=' + cmp_id + '&orderby=' + orderby + '&limitda=' + limitda + '&limitper=' + limitper;
		if (dateFrom) url += '&date_from=' + encodeURIComponent(dateFrom);
		if (dateTo) url += '&date_to=' + encodeURIComponent(dateTo);
		if (status) url += '&status=' + status;
		if (type_id) url += '&type_id=' + type_id;
		return this.http.get<Booking[]>(url, { observe: 'response' }).pipe(map((r) => r.body as Booking[]));
	}

	GetBookingListByUser(
		cmp_id: number = 0,
		dateFrom: string = '',
		dateTo: string = '',
		status: string = '',
		limitda: number = 1,
		limitper: number = 100,
	): Observable<Booking[]> {
		let url = this.configService.bookingUrl + 'booking/user/list';
		const params: string[] = [];
		if (cmp_id > 0) params.push('cmp_id=' + cmp_id);
		if (dateFrom) params.push('date_from=' + encodeURIComponent(dateFrom));
		if (dateTo)   params.push('date_to='   + encodeURIComponent(dateTo));
		if (status)   params.push('status='    + encodeURIComponent(status));
		params.push('limitda=' + limitda + '&limitper=' + limitper);
		if (params.length) url += '?' + params.join('&');
		return this.http.get<Booking[]>(url, { observe: 'response' }).pipe(map((r) => r.body as Booking[]));
	}

	GetBookingById(bkb_id: number): Observable<Booking> {
		return this.http
			.get<Booking>(this.configService.bookingUrl + 'booking/get/' + bkb_id, { observe: 'response' })
			.pipe(map((r) => r.body as Booking));
	}

	InsertBooking(cmp_id: number, booking: Partial<Booking>): Observable<Booking> {
		return this.http
			.post<Booking>(this.configService.bookingUrl + 'booking/add/' + cmp_id, booking, { observe: 'response' })
			.pipe(map((r) => r.body as Booking));
	}

	UpdateBooking(booking: Booking): Observable<Booking> {
		return this.http
			.put<Booking>(this.configService.bookingUrl + 'booking/upd/' + booking.bkb_id, booking, { observe: 'response' })
			.pipe(map((r) => r.body as Booking));
	}

	UpdateBookingStatus(bkb_id: number, status: string): Observable<any> {
		return this.http
			.put(this.configService.bookingUrl + 'booking/status/' + bkb_id + '?status=' + status, null, { observe: 'response' })
			.pipe(map((r) => r.body));
	}

	DeleteBooking(bkb_id: number): Observable<any> {
		return this.http
			.delete(this.configService.bookingUrl + 'booking/del/' + bkb_id, { observe: 'response' })
			.pipe(map((r) => r.body));
	}

	GetBookingItems(bkb_id: number): Observable<BookingItem[]> {
		return this.http
			.get<BookingItem[]>(this.configService.bookingUrl + 'booking/items/' + bkb_id, { observe: 'response' })
			.pipe(map((r) => r.body as BookingItem[]));
	}

	/** Lista operativa items per cucina/bar (cross-prenotazione). Replica web `GetAllBookingItems`. */
	GetAllBookingItems(
		cmp_id: number,
		dateFrom: string = '',
		dateTo: string = '',
		notesStatus: string = '',
		limitda: number = 1,
		limitper: number = 500,
	): Observable<BookingItem[]> {
		let url = this.configService.bookingUrl + 'booking/items?cmp_id=' + cmp_id;
		if (dateFrom)    url += '&date_from=' + encodeURIComponent(dateFrom);
		if (dateTo)      url += '&date_to='   + encodeURIComponent(dateTo);
		if (notesStatus) url += '&notes_status=' + encodeURIComponent(notesStatus);
		url += '&limitda=' + limitda + '&limitper=' + limitper;
		return this.http.get<BookingItem[]>(url, { observe: 'response' }).pipe(map((r) => r.body as BookingItem[]));
	}

	InsertBookingItem(item: Partial<BookingItem>): Observable<BookingItem> {
		return this.http
			.post<BookingItem>(this.configService.bookingUrl + 'booking/items/add', item, { observe: 'response' })
			.pipe(map((r) => r.body as BookingItem));
	}

	UpdateBookingItem(item: Partial<BookingItem>): Observable<BookingItem> {
		return this.http
			.put<BookingItem>(this.configService.bookingUrl + 'booking/items/upd/' + item.bkbi_id, item, { observe: 'response' })
			.pipe(map((r) => r.body as BookingItem));
	}

	DeleteBookingItem(bkbi_id: number): Observable<any> {
		return this.http
			.delete(this.configService.bookingUrl + 'booking/items/del/' + bkbi_id, { observe: 'response' })
			.pipe(map((r) => r.body));
	}

	/** Aggiorna solo il campo note (= stato operativo) di un BookingItem. Replica web. */
	UpdateBookingItemNotes(bkbi_id: number, notes: string): Observable<any> {
		return this.http
			.put(this.configService.bookingUrl + 'booking/items/notes/' + bkbi_id + '?notes=' + encodeURIComponent(notes), null, { observe: 'response' })
			.pipe(map((r) => r.body));
	}

	GetResourceTypes(cmp_id: number): Observable<BookingResourceType[]> {
		return this.http
			.get<BookingResourceType[]>(this.configService.bookingUrl + 'booking/resource/types/' + cmp_id, { observe: 'response' })
			.pipe(map((r) => r.body as BookingResourceType[]));
	}

	GetResources(cmp_id: number, type_id: number): Observable<BookingResource[]> {
		return this.http
			.get<BookingResource[]>(this.configService.bookingUrl + 'booking/resource/list?cmp_id=' + cmp_id + '&type_id=' + type_id, { observe: 'response' })
			.pipe(map((r) => r.body as BookingResource[]));
	}

	GetTimeSlots(cmp_id: number, type_id: number): Observable<BookingTimeSlot[]> {
		return this.http
			.get<BookingTimeSlot[]>(this.configService.bookingUrl + 'booking/resource/slots?cmp_id=' + cmp_id + '&type_id=' + type_id, { observe: 'response' })
			.pipe(map((r) => r.body as BookingTimeSlot[]));
	}

	/**
	 * Override visivi (posizione/disable) per (resource, date, slot) — usato dal web mapping
	 * per persistere il displayMap dei cluster. L'app legge la stessa cosa così le posizioni
	 * sul cliente/manager mobile coincidono col rendering del portale web.
	 */
	GetSlotOverrides(cmp_id: number, date: string): Observable<BookingResourceSlotOverride[]> {
		return this.http
			.get<BookingResourceSlotOverride[]>(this.configService.bookingUrl + 'booking/resource/override/' + cmp_id + '?date=' + encodeURIComponent(date), { observe: 'response' })
			.pipe(map((r) => (r.body || []) as BookingResourceSlotOverride[]));
	}

	GetMenuCategories(cmp_id: number, type_id: number): Observable<BookingMenuCategory[]> {
		return this.http
			.get<BookingMenuCategory[]>(this.configService.bookingUrl + 'booking/menu/categories?cmp_id=' + cmp_id + '&type_id=' + type_id, { observe: 'response' })
			.pipe(map((r) => r.body as BookingMenuCategory[]));
	}

	GetMenuItems(cmp_id: number, category_id: number): Observable<BookingMenuItem[]> {
		return this.http
			.get<BookingMenuItem[]>(this.configService.bookingUrl + 'booking/menu/items?cmp_id=' + cmp_id + '&category_id=' + category_id, { observe: 'response' })
			.pipe(map((r) => r.body as BookingMenuItem[]));
	}

	GetPaymentsByBooking(bkb_id: number): Observable<BookingPayment[]> {
		return this.http
			.get<BookingPayment[]>(this.configService.bookingUrl + 'booking/payment/list/' + bkb_id, { observe: 'response' })
			.pipe(map((r) => r.body as BookingPayment[]));
	}

	InsertPayment(payment: Partial<BookingPayment>): Observable<BookingPayment> {
		return this.http
			.post<BookingPayment>(this.configService.bookingUrl + 'booking/payment/add', payment, { observe: 'response' })
			.pipe(map((r) => r.body as BookingPayment));
	}

	GetResourceAssignments(bkb_id: number): Observable<BookingResourceAssignment[]> {
		return this.http
			.get<BookingResourceAssignment[]>(this.configService.bookingUrl + 'booking/resource/assignments/' + bkb_id, { observe: 'response' })
			.pipe(map((r) => r.body as BookingResourceAssignment[]));
	}

	/** Prenotazioni che si sovrappongono al periodo (per calcolo disponibilità mappa) */
	GetBookingsByPeriod(cmp_id: number, dateFrom: string, dateTo: string, type_id: number): Observable<Booking[]> {
		const url = this.configService.bookingUrl
			+ `booking/list?cmp_id=${cmp_id}&orderby=bkb_date&limitda=0&limitper=5000&date_from=${dateFrom}&date_to=${dateTo}&type_id=${type_id}`;
		return this.http.get<Booking[]>(url, { observe: 'response' }).pipe(map((r) => r.body as Booking[]));
	}

	GetBeachPricing(cmp_id: number, type_id: number): Observable<BookingBeachPricing[]> {
		return this.http
			.get<BookingBeachPricing[]>(this.configService.bookingUrl + 'booking/beach/pricing?cmp_id=' + cmp_id + '&type_id=' + type_id, { observe: 'response' })
			.pipe(map((r) => r.body as BookingBeachPricing[]));
	}

	SaveResourceAssignments(bkb_id: number, resourceIds: number[]): Observable<any> {
		return this.http
			.post(this.configService.bookingUrl + 'booking/resource/assignments/save/' + bkb_id, resourceIds, { observe: 'response' })
			.pipe(map((r) => r.body));
	}
}
