// 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 } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ConfigService } from '../../providers/config.service';
import { LayoutService } from '../../providers/layout.service';

@Component({
	selector: 'app-notifications',
	templateUrl: './notifications.page.html',
	styleUrls: ['./notifications.page.scss'],
	standalone: false,
})
export class NotificationsPage {
	notifications: any[] = [];
	groups: { label: string; items: any[] }[] = [];
	loading = false;
	totalrows = 0;
	limit = 20;
	offset = 0;
	unreadCount = 0;
	detailOpen = false;
	detailTarget: any = null;

	constructor(private http: HttpClient, private config: ConfigService, private layoutService: LayoutService) {}

	/* ionViewWillEnter (non ngOnInit): Ionic tiene la pagina in cache,
	 * quindi al rientro ricarichiamo lista e bucket temporali, che
	 * altrimenti resterebbero fermi al primo accesso (etichette
	 * Oggi/Ieri stantie oltre la mezzanotte, push arrivate nel
	 * frattempo assenti). Skeleton solo al primo caricamento. */
	ionViewWillEnter() {
		this.layoutService.setPageConfig({ title: 'Notifiche', showBack: false, rightButton: null });
		this.fetchPage({ showLoading: this.notifications.length === 0 });
	}

	/* Unico punto di fetch: reset (default) o append; la derivazione
	 * groups/unreadCount avviene sempre qui, mai nei chiamanti. */
	private fetchPage(opts: { append?: boolean; showLoading?: boolean; done?: (res: any[]) => void } = {}) {
		const offset = opts.append ? this.offset : 0;
		if (opts.showLoading) this.loading = true;
		this.http.get(`${this.config.serviceUrl_PortalManager}notify/my-list?limit=${this.limit}&offset=${offset}`).subscribe({
			next: (res: any) => {
				if (opts.append) {
					this.notifications = [...this.notifications, ...res];
				} else {
					this.notifications = res;
					this.offset = 0;
					this.totalrows = res.length > 0 ? res[0].totalrows : 0;
				}
				this.rebuildGroups();
				this.refreshUnreadCount();
				this.loading = false;
				opts.done?.(res);
			},
			error: () => {
				this.loading = false;
				opts.done?.([]);
			},
		});
	}

	loadMore(event: any) {
		this.offset += this.limit;
		this.fetchPage({
			append: true,
			done: (res) => {
				event.target.complete();
				if (res.length < this.limit) {
					event.target.disabled = true;
				}
			},
		});
	}

	doRefresh(event: any) {
		this.fetchPage({ done: () => event.target.complete() });
	}

	/* Conteggio non lette dal server (fonte vera, non paginata);
	 * se l'endpoint non risponde si ripiega sul conteggio locale. */
	private refreshUnreadCount() {
		this.http.get(`${this.config.serviceUrl_PortalManager}notify/unread-count`).subscribe({
			next: (count: any) => (this.unreadCount = Number(count) || 0),
			error: () => (this.unreadCount = this.notifications.filter((n) => !n.usrnot_dateread).length),
		});
	}

	openDetail(notif: any) {
		this.detailTarget = notif;
		this.detailOpen = true;
		this.markAsRead(notif);
	}

	/* detailTarget si azzera solo in didDismiss: azzerarlo alla
	 * chiusura svuoterebbe il modal durante l'animazione di uscita. */
	onDetailDismiss() {
		this.detailOpen = false;
		this.detailTarget = null;
	}

	markAsRead(notif: any) {
		if (notif.usrnot_dateread) return;
		// Ottimistico: se il PUT fallisce si ripristina lo stato non letto.
		notif.usrnot_dateread = new Date().toISOString();
		if (this.unreadCount > 0) this.unreadCount--;
		this.http.put(`${this.config.serviceUrl_PortalManager}notify/mark-read/${notif.usrnot_id}`, {}).subscribe({
			error: () => {
				notif.usrnot_dateread = null;
				this.unreadCount++;
			},
		});
	}

	/* Il backend serializza le date come 'YYYY-MM-DD HH:mm:ss': WebKit/iOS
	 * non parsa lo spazio, serve la 'T' (stesso workaround di
	 * assemblea-form e sopralluogo-cmp-form). */
	private parseDate(v: any): Date {
		return new Date(typeof v === 'string' ? v.replace(' ', 'T') : v);
	}

	/* Raggruppa per fascia temporale (Oggi / Ieri / Ultimi 7 giorni /
	 * Meno recenti) mantenendo l'ordine del backend. Il confronto usa
	 * Math.round sulla differenza tra mezzanotti locali, così i giorni
	 * da 23/25h dei cambi ora legale non spostano i confini. Date
	 * mancanti o non parsabili finiscono in 'Meno recenti'. */
	rebuildGroups() {
		const startOfDay = (d: Date) => new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
		const today = startOfDay(new Date());
		const oneDay = 86400000;
		const buckets: { label: string; items: any[] }[] = [
			{ label: 'Oggi', items: [] },
			{ label: 'Ieri', items: [] },
			{ label: 'Ultimi 7 giorni', items: [] },
			{ label: 'Meno recenti', items: [] },
		];
		for (const n of this.notifications) {
			const sent = n.usrnot_datesent ? startOfDay(this.parseDate(n.usrnot_datesent)) : NaN;
			const diffDays = Math.round((today - sent) / oneDay);
			if (diffDays <= 0) buckets[0].items.push(n);
			else if (diffDays === 1) buckets[1].items.push(n);
			else if (diffDays <= 7) buckets[2].items.push(n);
			else buckets[3].items.push(n);
		}
		this.groups = buckets.filter((b) => b.items.length > 0);
	}

	getFullDate(dateStr: string): string {
		if (!dateStr) return '';
		return this.parseDate(dateStr).toLocaleDateString('it-IT', {
			day: 'numeric',
			month: 'long',
			year: 'numeric',
			hour: '2-digit',
			minute: '2-digit',
		});
	}

	getTimeAgo(dateStr: string): string {
		if (!dateStr) return '';
		const date = this.parseDate(dateStr);
		const now = new Date();
		const diffMs = now.getTime() - date.getTime();
		const diffMin = Math.floor(diffMs / 60000);
		if (isNaN(diffMin)) return '';
		if (diffMin < 1) return 'adesso';
		if (diffMin < 60) return `${diffMin} min fa`;
		const diffHours = Math.floor(diffMin / 60);
		if (diffHours < 24) return `${diffHours} ore fa`;
		const diffDays = Math.floor(diffHours / 24);
		if (diffDays < 7) return `${diffDays} giorni fa`;
		return date.toLocaleDateString('it-IT');
	}

	getImageUrl(filename: string): string {
		return `${this.config.serviceUrl_PortalManager}notify/image/${filename}`;
	}
}
