// 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, inject } from '@angular/core';
import { CompaniesService } from '../../providers/companies.service';
import { ConfigService } from '../../providers/config.service';
import { ServiceCard } from 'src/app/models/serviceCard.model';
import { LayoutService } from 'src/app/providers/layout.service';
import { FavoritesService } from 'src/app/providers/favorites.service';
import { GeolocationService } from 'src/app/providers/geolocation.service';
import { BookingService } from 'src/app/providers/booking.service';
import { OrdersService } from 'src/app/providers/orders.service';

// Macrogruppo (categoria) di servizi mostrato nella home a 2 livelli
interface CategoryGroup {
	key: string; // = csc_name_view, oppure 'Generale' per i servizi senza categoria
	name: string;
	icon: string; // classe PrimeIcons (pi pi-*)
	bgClass: string;
	bgImage?: string; // base64 / url (sovrascrive il colore)
	bgOpacity?: number;
	order: number;
	count: number;
}

@Component({
	selector: 'app-home',
	templateUrl: 'home.page.html',
	styleUrls: ['home.page.scss'],
	standalone: false,
})
export class HomePage {
	layoutService = inject(LayoutService);

	// Data
	cards: ServiceCard[] = [];
	filteredCards: ServiceCard[] = [];
	groupedServices: { [key: string]: ServiceCard[] } = {};
	filteredCategories: string[] = [];
	availableCategories: string[] = [];
	expandedCategories = new Set<string>();
	favoriteIds: Set<string> = new Set();
	loading = false;

	// Vista "I miei servizi" (default): mostra solo i servizi personali, cioè quelli
	// che hai perché sei dipendente/collaboratore/fornitore di un'azienda. Si riconoscono
	// dal contenuto di srv_type (join su cmp_employees / cmp_collaborators iniettato dal backend).
	personalOnly = false;
	private readonly PERSONAL_SRV_TYPE_MARKERS = ['cmp_employees', 'cmp_collaborators'];
	// Aziende presso cui ho "usato" un servizio pubblico (→ lo considero mio):
	// prenotazioni (bk_booking.bkb_cmp_id) e ordini onlineshop (orders.ord_cmp_id).
	private usedBookingCmps = new Set<number>();
	private usedOrderCmps = new Set<number>();

	// Macrogruppi (categorie) + navigazione a 2 livelli
	readonly FALLBACK_CAT = 'Generale';
	categories: CategoryGroup[] = [];
	personalCategories: CategoryGroup[] = []; // tile dei soli servizi personali (sezione "I miei servizi" nella landing)
	favoriteCards: ServiceCard[] = []; // servizi preferiti (sezione "Preferiti" in cima alla landing)
	selectedCategory: string | null = null; // null = livello 1 (tile categorie); valorizzato = livello 2 (servizi)

	// Quick search
	searchTerm = '';

	// Advanced filters (modal)
	showAdvancedFilters = false;
	sortOption: 'relevance' | 'az' | 'za' | 'distance' = 'relevance';
	filterFavoritesOnly = false;
	filterOpenNow = false;
	filterWithBooking = false;
	filterNearby = false;

	// Temp state while modal is open (apply only on confirm)
	tempSortOption: 'relevance' | 'az' | 'za' | 'distance' = 'relevance';
	tempFavoritesOnly = false;
	tempOpenNow = false;
	tempWithBooking = false;
	tempNearby = false;

	// Distance map: key = getServiceUniqueId(card), value = km
	distanceMap: Map<string, number> = new Map();

	// Location permission modal
	showLocationModal = false;

	get advancedFilterCount(): number {
		let n = 0;
		if (this.sortOption !== 'relevance') n++;
		if (this.filterFavoritesOnly) n++;
		if (this.filterOpenNow) n++;
		if (this.filterWithBooking) n++;
		if (this.filterNearby) n++;
		return n;
	}

	get hasPosition(): boolean {
		return this.geoService.getStatus() === 'granted';
	}

	private readonly viewTitles: Record<string, string> = {
		services: '',
		categories: 'Categorie',
	};

	constructor(private companiesservice: CompaniesService, public config: ConfigService, private favoritesService: FavoritesService, public geoService: GeolocationService, private bookingService: BookingService, private ordersService: OrdersService) {}

	ionViewWillEnter() {
		const view = this.layoutService.homeView();
		const isServicesView = view === 'services';
		this.layoutService.setPageConfig({
			title: this.viewTitles[view] ?? '',
			showBack: false,
			showHeader: !isServicesView,
		});
		this.layoutService.selectedTab.set(view);
		this.loadFavorites();
		this.loadCompaniesService();
		this.loadUsage();
		this.askGeolocationIfNeeded();
	}

	askGeolocationIfNeeded() {
		if (this.geoService.hasBeenAsked() || this.geoService.getStatus() === 'granted') return;
		setTimeout(() => {
			this.showLocationModal = true;
		}, 700);
	}

	acceptLocation() {
		this.showLocationModal = false;
		this.geoService
			.requestPosition()
			.then(() => this.computeDistances())
			.catch(() => {});
	}

	declineLocation() {
		this.showLocationModal = false;
		this.geoService.markAsAsked();
	}

	ionViewWillLeave() {
		this.layoutService.setPageConfig({ showHeader: true });
	}

	ionViewDidEnter() {
		this.sortAndFilterCards();
	}

	loadCompaniesService() {
		this.loading = true;
		this.companiesservice.loadCompaniesService().subscribe((res: any) => {
			if (res) {
				this.cards = res as ServiceCard[];
				this.computeDistances();
				this.refreshView();
				this.loadCardLogos();

				// DEBUG TEMP (rimuovere dopo verifica): cosa vede l'app per ogni servizio e se è "mio"
				console.table(
					(this.cards || []).map((c) => ({
						nome: c.srv_name_view,
						srv_pt_rl_id: c.srv_pt_rl_id,
						srv_type: (c.srv_type || '').slice(0, 50),
						cmp_id: c.company?.id,
						mio: this.isPersonalService(c),
					})),
				);
			}
			this.loading = false;
		});
	}

	/** Le liste non trasportano piu' cmp_logo_b64: scarica i loghi solo per le card che li mostrano */
	private loadCardLogos() {
		const ids = [...new Set((this.cards || []).filter((c) => c.srv_show_cmp_logo && c.company?.id && !c.company.cmp_logo_b64).map((c) => c.company.id))];
		for (const id of ids) {
			this.companiesservice.getCompanyLogo(id).subscribe({
				next: (b64: string) => {
					if (!b64) return;
					for (const c of this.cards) {
						if (c.company?.id === id) c.company.cmp_logo_b64 = b64;
					}
				},
				error: () => {},
			});
		}
	}

	computeDistances() {
		this.distanceMap = new Map();
		for (const card of this.cards) {
			const d = this.geoService.distanceFromUser(card.company?.org_latitude, card.company?.org_longitude);
			if (d !== null) {
				this.distanceMap.set(this.getServiceUniqueId(card), d);
			}
		}
	}

	getCardDistance(card: ServiceCard): number | null {
		return this.distanceMap.get(this.getServiceUniqueId(card)) ?? null;
	}

	formatCardDistance(card: ServiceCard): string | null {
		const d = this.getCardDistance(card);
		return d !== null ? this.geoService.formatDistance(d) : null;
	}

	loadFavorites() {
		this.favoritesService.loadFavorites().subscribe((ids) => {
			this.favoriteIds = ids;
			this.sortAndFilterCards();
		});
	}

	onToggleFavorite(serviceId: string) {
		const [srvId, cmpId] = serviceId.split('_').map(Number);
		this.favoritesService.toggleFavorite(srvId, cmpId);
		this.favoriteIds = this.favoritesService.getFavoriteIds();
		this.sortAndFilterCards();
	}

	// ─── Macrogruppi (categorie) ──────────────────────────────────

	/** chiave categoria di una card (fallback 'Generale' per i servizi senza categoria) */
	categoryKey(card: ServiceCard): string {
		return card.csc_name_view || this.FALLBACK_CAT;
	}

	/** raggruppa le card per macro-categoria */
	private groupByCategory(cards: ServiceCard[]): { [key: string]: ServiceCard[] } {
		return (cards || []).reduce((acc: { [key: string]: ServiceCard[] }, c) => {
			const key = this.categoryKey(c);
			(acc[key] = acc[key] || []).push(c);
			return acc;
		}, {});
	}

	/** pool di base secondo la vista attiva: solo personali in "I miei servizi", altrimenti tutti */
	private getBasePool(): ServiceCard[] {
		return this.personalOnly ? this.cards.filter((c) => this.isPersonalService(c)) : this.cards;
	}

	/** ricostruisce raggruppamenti/categorie sul pool della vista attiva e riapplica ricerca/filtri */
	private refreshView(): void {
		const pool = this.getBasePool();
		this.groupedServices = this.groupByCategory(pool);
		this.availableCategories = Object.keys(this.groupedServices);
		// le tile "Tutti" mostrano sempre tutte le categorie (i miei sono inclusi); la sezione "I miei servizi" usa personalCategories
		this.categories = this.buildCategoryGroups(this.cards);
		this.personalCategories = this.buildCategoryGroups(this.personalCards);
		this.sortAndFilterCards();
	}

	/** costruisce l'elenco dei macrogruppi (icona/sfondo/ordine/conteggio) per le tile su un dato pool */
	private buildCategoryGroups(pool: ServiceCard[]): CategoryGroup[] {
		const map = new Map<string, CategoryGroup>();
		for (const c of pool) {
			const key = this.categoryKey(c);
			if (!map.has(key)) {
				map.set(key, {
					key,
					name: key,
					icon: c.csc_icon || 'pi pi-th-large',
					bgClass: c.csc_background_class || 'bg-gradient-ocean',
					bgImage: c.csc_background_image || undefined,
					bgOpacity: c.csc_background_opacity ?? 0.5,
					order: c.csc_order ?? 999,
					count: 0,
				});
			}
			map.get(key)!.count++;
		}
		return Array.from(map.values()).sort((a, b) => a.order - b.order || a.name.localeCompare(b.name));
	}

	/** icona di un macrogruppo per chiave (usata nell'accordion) */
	categoryIcon(key: string): string {
		return this.categories.find((c) => c.key === key)?.icon || 'pi pi-th-large';
	}

	/** overlay immagine di sfondo della tile categoria (se impostata da DB) */
	categoryOverlayStyles(cat: CategoryGroup): { [k: string]: string | number } | null {
		if (!cat.bgImage) return null;
		return {
			'background-image': `url('${cat.bgImage}')`,
			'background-size': 'cover',
			'background-position': 'center',
			opacity: cat.bgOpacity ?? 0.5,
		};
	}

	/** livello 1 = tile categorie: nessuna categoria aperta, nessuna ricerca/filtro attivo (vale sia per "Tutti" che per "I miei servizi") */
	get isCategoryLevel(): boolean {
		return !this.selectedCategory && !this.searchTerm.trim() && !this.filterFavoritesOnly && !this.filterOpenNow && !this.filterWithBooking && !this.filterNearby;
	}

	/**
	 * Carica i segnali d'uso (prenotazioni e ordini) dell'utente loggato, per ricavare le aziende
	 * presso cui ho "usato" un servizio pubblico → quel servizio diventa mio. Le tabelle d'uso stanno
	 * in moduli/DB diversi dalla lista servizi, quindi la composizione avviene qui lato app.
	 */
	loadUsage() {
		this.bookingService.GetBookingListByUser().subscribe({
			next: (res: any[]) => {
				console.log('[USAGE] prenotazioni (risposta):', res);
				this.usedBookingCmps = new Set((res || []).map((b) => b.bkb_cmp_id).filter((id) => id != null));
				console.log('[USAGE] usedBookingCmps:', [...this.usedBookingCmps]);
				this.refreshView();
				this.dumpUsageDebug();
			},
			error: (e) => console.log('[USAGE] errore prenotazioni:', e),
		});

		const ordersFilter = {
			usr_id: this.config.getUserID(),
			cmp_id: -1, // -1 = tutte le aziende (il backend filtra ord_cmp_id solo se cmp_id >= 0)
			stato: -1,
			section: 'profile',
			pagina: 1,
			righePagina: 1000,
			orderBy: 'ord_date desc',
		};
		this.ordersService.get_orders_list(ordersFilter).subscribe({
			next: (res: any[]) => {
				console.log('[USAGE] ordini (risposta):', res);
				this.usedOrderCmps = new Set((res || []).map((o) => o.ord_cmp_id).filter((id) => id != null));
				console.log('[USAGE] usedOrderCmps:', [...this.usedOrderCmps]);
				this.refreshView();
				this.dumpUsageDebug();
			},
			error: (e) => console.log('[USAGE] errore ordini:', e),
		});
	}

	/** DEBUG TEMP (rimuovere dopo verifica): card booking/onlineshop e match con le aziende usate */
	private dumpUsageDebug() {
		console.table(
			(this.cards || [])
				.filter((c) => this.isBookingService(c) || this.isOnlineshopService(c))
				.map((c) => ({
					nome: c.srv_name_view,
					link: c.srv_link,
					cmp_id: c.company?.id,
					isBooking: this.isBookingService(c),
					isShop: this.isOnlineshopService(c),
					mio: this.isPersonalService(c),
				})),
		);
	}

	private isBookingService(card: ServiceCard): boolean {
		const link = (card.srv_link || '').toLowerCase();
		const name = (card.srv_name || '').toLowerCase();
		return link.includes('prenotazioni') || link.includes('/booking') || name.includes('booking');
	}

	private isOnlineshopService(card: ServiceCard): boolean {
		const link = (card.srv_link || '').toLowerCase();
		const name = (card.srv_name || '').toLowerCase();
		return link.includes('onlineshop') || name.includes('shop');
	}

	/**
	 * true se il servizio è "mio" (vista "I miei servizi"):
	 * 1) relazione dipendente/collaboratore/fornitore (via srv_type)
	 * 2) servizio pubblico che ho usato (prenotazione/ordine presso quell'azienda)
	 *
	 * NB: il ruolo di portale (srv_pt_rl_id) NON è un segnale affidabile: nei dati i servizi
	 * sono quasi tutti taggati con ruoli non-GUEST (EST/OP/…), quindi includerebbe tutto.
	 */
	isPersonalService(card: ServiceCard): boolean {
		// 1) relazione via srv_type (dipendente/collaboratore/fornitore)
		const t = (card.srv_type || '').toLowerCase();
		if (this.PERSONAL_SRV_TYPE_MARKERS.some((m) => t.includes(m))) return true;

		// 2) servizio pubblico ma usato
		const cmpId = card.company?.id;
		if (cmpId != null) {
			if (this.isBookingService(card) && this.usedBookingCmps.has(cmpId)) return true;
			if (this.isOnlineshopService(card) && this.usedOrderCmps.has(cmpId)) return true;
		}

		return false;
	}

	/** servizi personali dell'utente (dipendente/collaboratore/fornitore o pubblici già usati): sezione "I miei servizi" in cima alla landing */
	get personalCards(): ServiceCard[] {
		return this.cards.filter((c) => this.isPersonalService(c));
	}

	/** chip "Tutti": tutti i servizi visibili, organizzati per categoria */
	showAll(): void {
		this.personalOnly = false;
		this.selectedCategory = null;
		this.searchTerm = '';
		this.refreshView();
	}

	openCategory(key: string, personal: boolean = false): void {
		// apre il macrogruppo nello scope richiesto: "I miei servizi" (personal) o "Tutti"
		this.personalOnly = personal;
		this.selectedCategory = key;
		this.searchTerm = '';
		this.sortAndFilterCards();
	}

	backToCategories(): void {
		// torna alla landing (tile "I miei servizi" + "Tutti")
		this.selectedCategory = null;
		this.personalOnly = false;
		this.sortAndFilterCards();
	}

	// ─── Quick search ─────────────────────────────────────────────

	filterServices() {
		this.sortAndFilterCards();
	}

	onSearchClear() {
		this.searchTerm = '';
		this.sortAndFilterCards();
	}

	clearFilters() {
		this.searchTerm = '';
		this.selectedCategory = null;
		this.personalOnly = false;
		this.sortOption = 'relevance';
		this.filterFavoritesOnly = false;
		this.filterOpenNow = false;
		this.filterWithBooking = false;
		this.filterNearby = false;
		this.refreshView();
	}

	// ─── Advanced filters modal ───────────────────────────────────

	openAdvancedFilters() {
		this.tempSortOption = this.sortOption;
		this.tempFavoritesOnly = this.filterFavoritesOnly;
		this.tempOpenNow = this.filterOpenNow;
		this.tempWithBooking = this.filterWithBooking;
		this.tempNearby = this.filterNearby;
		this.showAdvancedFilters = true;
	}

	applyAdvancedFilters() {
		this.sortOption = this.tempSortOption;
		this.filterFavoritesOnly = this.tempFavoritesOnly;
		this.filterOpenNow = this.tempOpenNow;
		this.filterWithBooking = this.tempWithBooking;
		this.filterNearby = this.tempNearby;
		// Se filtra per vicinanza ma non ha la posizione, richiede permesso
		if (this.filterNearby && !this.hasPosition) {
			this.geoService
				.requestPosition()
				.then(() => this.computeDistances())
				.catch(() => {});
		}
		this.showAdvancedFilters = false;
		this.sortAndFilterCards();
	}

	resetAdvancedFilters() {
		this.tempSortOption = 'relevance';
		this.tempFavoritesOnly = false;
		this.tempOpenNow = false;
		this.tempWithBooking = false;
		this.tempNearby = false;
	}

	// ─── Core filter + sort ───────────────────────────────────────

	sortAndFilterCards() {
		// preferiti per la sezione in cima alla landing (indipendente da scope/ricerca/filtri)
		this.favoriteCards = this.cards.filter((c) => this.favoriteIds.has(this.getServiceUniqueId(c)));

		const term = this.searchTerm.toLowerCase().trim();
		let pool = [...this.cards];

		// "I miei servizi" (default): solo i servizi personali (dipendente/collaboratore/fornitore)
		if (this.personalOnly) {
			pool = pool.filter((c) => this.isPersonalService(c));
		}

		// Text search
		if (term) {
			pool = pool.filter(
				(c) =>
					(c.srv_name_view && c.srv_name_view.toLowerCase().includes(term)) ||
					(c.company?.name && c.company.name.toLowerCase().includes(term)) ||
					(c.srv_description && c.srv_description.toLowerCase().includes(term)) ||
					(c.srv_name && c.srv_name.toLowerCase().includes(term)),
			);
		}

		// Scope alla macro-categoria selezionata (la ricerca testuale resta globale su tutte le categorie)
		if (this.selectedCategory && !term) {
			pool = pool.filter((c) => this.categoryKey(c) === this.selectedCategory);
		}

		// Advanced: solo preferiti
		if (this.filterFavoritesOnly) {
			pool = pool.filter((c) => this.favoriteIds.has(this.getServiceUniqueId(c)));
		}

		// Advanced: aperto adesso (srv_open_now from backend)
		if (this.filterOpenNow) {
			pool = pool.filter((c) => c.srv_open_now === true);
		}

		// Advanced: con prenotazione online
		if (this.filterWithBooking) {
			pool = pool.filter((c) => c.srv_has_booking === true || (c.srv_link && c.srv_link.includes('biz-appointments')));
		}

		// Advanced: vicino a me (solo card con coordinate)
		if (this.filterNearby) {
			pool = pool.filter((c) => this.distanceMap.has(this.getServiceUniqueId(c)));
		}

		this.filteredCards = this.sortCards(pool);

		// Rebuild category list
		const allCats = this.getServiceNames();
		const hasFilter = term || !!this.selectedCategory || this.filterFavoritesOnly || this.filterOpenNow || this.filterWithBooking || this.filterNearby;
		if (hasFilter) {
			const visibleIds = new Set(pool.map((c) => this.getServiceUniqueId(c)));
			this.filteredCategories = allCats.filter((cat) => (this.groupedServices[cat] || []).some((c) => visibleIds.has(this.getServiceUniqueId(c))));
		} else {
			this.filteredCategories = allCats;
		}
	}

	sortCards(list: ServiceCard[]): ServiceCard[] {
		return [...list].sort((a, b) => {
			if (this.sortOption === 'distance') {
				const da = this.distanceMap.get(this.getServiceUniqueId(a)) ?? Infinity;
				const db = this.distanceMap.get(this.getServiceUniqueId(b)) ?? Infinity;
				return da - db;
			}
			if (this.sortOption === 'relevance') {
				const aFav = this.favoriteIds.has(this.getServiceUniqueId(a));
				const bFav = this.favoriteIds.has(this.getServiceUniqueId(b));
				if (aFav && !bFav) return -1;
				if (!aFav && bFav) return 1;
				return (a.srv_name_view || '').localeCompare(b.srv_name_view || '');
			}
			const na = a.srv_name_view || '';
			const nb = b.srv_name_view || '';
			return this.sortOption === 'az' ? na.localeCompare(nb) : nb.localeCompare(na);
		});
	}

	toggleCategoryGroup(catName: string) {
		if (this.expandedCategories.has(catName)) {
			this.expandedCategories.delete(catName);
		} else {
			this.expandedCategories.add(catName);
		}
	}

	doRefresh(event: any) {
		this.loadFavorites();
		this.loadUsage();
		this.companiesservice.loadCompaniesService().subscribe(
			(res: any) => {
				if (res) {
					this.cards = res as ServiceCard[];
					this.refreshView();
				}
				event.target.complete();
			},
			() => event.target.complete(),
		);
	}

	getServiceUniqueId(card: ServiceCard): string {
		return `${card.srv_id}_${card.company?.id || 0}`;
	}

	groupBy(array: any[], key: string) {
		return array.reduce((result, current) => {
			(result[current[key]] = result[current[key]] || []).push(current);
			return result;
		}, {});
	}

	getServiceNames(): string[] {
		return Object.keys(this.groupedServices);
	}
}
