// 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, OnInit, ViewChild, ElementRef } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { LoadingController } from '@ionic/angular';
import { Camera, CameraResultType, CameraSource } from '@capacitor/camera';

import { ConfigService } from 'src/app/providers/config.service';
import { CompaniesService } from 'src/app/providers/companies.service';
import { BizAppointmentsService } from 'src/app/providers/biz-appointments.service';
import { BizServiceTypesService } from 'src/app/providers/biz-service-types.service';
import { ServiceType } from 'src/models/biz-appointments/service-type.model';
import { FreeSlot } from 'src/models/biz-appointments/availability.model';
import { Appointment } from 'src/models/biz-appointments/appointment.model';

interface SlotGroup {
	date: string;
	dateLabel: string;
	morningSlots: FreeSlot[];
	afternoonSlots: FreeSlot[];
}

@Component({
	standalone: false,
	selector: 'app-biz-appointment-request',
	templateUrl: './biz-appointment-request.page.html',
	styleUrls: ['./biz-appointment-request.page.scss'],
})
export class BizAppointmentRequestPage implements OnInit {
	currentStep: number = 1;
	cmp_id: number = 0;
	companyName: string = '';

	// Step 1 - Multi-selezione
	serviceTypes: ServiceType[] = [];
	groupedServiceTypes: { category_name: string; services: ServiceType[] }[] = [];
	selectedServiceTypes: ServiceType[] = [];

	// Step 2 - SLOT_FISSO
	freeSlots: FreeSlot[] = [];
	slotGroups: SlotGroup[] = [];
	selectedSlot: FreeSlot | null = null;
	selectedDateGroup: SlotGroup | null = null;
	selectedPeriod: 'morning' | 'afternoon' = 'morning';
	slotsLoading: boolean = false;

	// Step 2 - SU_RICHIESTA
	preferredDate: string = '';
	preferredTime: string = '';
	problemDescription: string = '';
	pendingFiles: File[] = [];

	// Step 3
	additionalNotes: string = '';
	submitting: boolean = false;

	minDate: string = new Date().toISOString();

	constructor(
		private router: Router,
		private route: ActivatedRoute,
		private loadingController: LoadingController,
		private configService: ConfigService,
		private companiesService: CompaniesService,
		private appointmentsService: BizAppointmentsService,
		private serviceTypesService: BizServiceTypesService,
	) {}

	ngOnInit(): void {
		this.cmp_id = Number(this.route.snapshot.paramMap.get('cmp_id') || this.route.parent?.snapshot.paramMap.get('cmp_id') || 0);
	}

	// Ionic mette in cache la pagina: ngOnInit NON rigira al rientro. ionViewWillEnter scatta
	// ad ogni ingresso -> reset del wizard per evitare lo stato "sporco" (es. il tasto +
	// che riapre il wizard con i servizi/step precedenti gia' compilati dopo una creazione).
	ionViewWillEnter(): void {
		this.resetWizard();
		this.loadCompanyName();
		this.loadServiceTypes();
	}

	private resetWizard(): void {
		this.currentStep = 1;
		this.selectedServiceTypes = [];
		this.freeSlots = [];
		this.slotGroups = [];
		this.selectedSlot = null;
		this.selectedDateGroup = null;
		this.selectedPeriod = 'morning';
		this.slotsLoading = false;
		this.preferredDate = '';
		this.preferredTime = '';
		this.problemDescription = '';
		this.pendingFiles = [];
		this.additionalNotes = '';
		this.submitting = false;
	}

	loadCompanyName(): void {
		this.companiesService.GetCompany(this.cmp_id).subscribe({
			next: (data: any) => {
				this.companyName = data?.name || '';
			},
		});
	}

	loadServiceTypes(): void {
		this.serviceTypesService.getServiceTypes(this.cmp_id).subscribe({
			next: (data: any) => {
				this.serviceTypes = (data || []).filter((st: ServiceType) => st.st_active);
				this.groupServiceTypesByCategory();
				this.checkPreselectedServiceType();
			},
		});
	}

	private groupServiceTypesByCategory(): void {
		const groups = new Map<string, ServiceType[]>();
		for (const st of this.serviceTypes) {
			const catName = st.category_name || '';
			if (!groups.has(catName)) {
				groups.set(catName, []);
			}
			groups.get(catName)!.push(st);
		}
		this.groupedServiceTypes = [];
		groups.forEach((services, category_name) => {
			this.groupedServiceTypes.push({ category_name: category_name || 'Altro', services });
		});
	}

	private checkPreselectedServiceType(): void {
		const stId = Number(this.route.snapshot.queryParamMap.get('service_type_id'));
		if (stId && this.serviceTypes.length > 0) {
			const found = this.serviceTypes.find((st) => st.st_id === stId);
			if (found) {
				this.toggleServiceType(found);
			}
		}
		const additionalIds = this.route.snapshot.queryParamMap.get('additional_ids');
		if (additionalIds) {
			for (const idStr of additionalIds.split(',')) {
				const id = Number(idStr);
				if (!id) continue;
				const found = this.serviceTypes.find((st) => st.st_id === id);
				if (found) {
					this.toggleServiceType(found);
				}
			}
		}
	}

	// --- Computed getters ---

	get primaryServiceType(): ServiceType | null {
		return this.selectedServiceTypes.length > 0 ? this.selectedServiceTypes[0] : null;
	}

	get totalDurationMinutes(): number {
		return this.selectedServiceTypes.reduce((sum, st) => sum + st.st_duration_minutes, 0);
	}

	get anyRequiresAttachments(): boolean {
		return this.selectedServiceTypes.some((st) => st.st_requires_attachments);
	}

	// --- Step 1: Toggle selezione servizi ---

	toggleServiceType(st: ServiceType): void {
		const idx = this.selectedServiceTypes.findIndex((s) => s.st_id === st.st_id);
		if (idx >= 0) {
			this.selectedServiceTypes.splice(idx, 1);
		} else {
			if (this.selectedServiceTypes.length > 0 && this.selectedServiceTypes[0].st_mode !== st.st_mode) {
				this.configService.presentToastWarning('Non puoi combinare servizi con modalità di prenotazione diverse');
				return;
			}
			this.selectedServiceTypes.push(st);
		}
	}

	isServiceTypeSelected(st: ServiceType): boolean {
		return this.selectedServiceTypes.some((s) => s.st_id === st.st_id);
	}

	proceedFromStep1(): void {
		if (this.selectedServiceTypes.length === 0) return;
		this.currentStep = 2;
		if (this.primaryServiceType?.st_mode === 'SLOT_FISSO') {
			this.loadFreeSlots();
		}
	}

	// --- Step 2 SLOT_FISSO ---

	loadFreeSlots(): void {
		if (!this.primaryServiceType) return;

		this.slotsLoading = true;
		const today = new Date();
		const twoWeeks = new Date();
		twoWeeks.setDate(today.getDate() + 14);

		const dateFrom = this.formatDateISO(today);
		const dateTo = this.formatDateISO(twoWeeks);

		this.appointmentsService.getFreeSlots(this.cmp_id, this.primaryServiceType.st_id, dateFrom, dateTo, undefined, this.totalDurationMinutes).subscribe({
			next: (data: any) => {
				this.freeSlots = (data || []).filter((s: FreeSlot) => !s.reserved);
				this.groupSlotsByDate();
			},
			error: () => {
				this.configService.presentToastError('Errore caricamento slot');
			},
			complete: () => {
				this.slotsLoading = false;
			},
		});
	}

	groupSlotsByDate(): void {
		const groups: Map<string, SlotGroup> = new Map();
		const seen: Set<string> = new Set();
		const now = new Date();
		const todayStr = this.formatDateISO(now);
		const currentTime = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;

		for (const slot of this.freeSlots) {
			if (slot.date === todayStr && slot.start_time <= currentTime) continue;

			const key = `${slot.date}_${slot.start_time}`;
			if (seen.has(key)) continue;
			seen.add(key);

			if (!groups.has(slot.date)) {
				const d = new Date(slot.date);
				groups.set(slot.date, {
					date: slot.date,
					dateLabel: d.toLocaleDateString('it-IT', { weekday: 'long', day: 'numeric', month: 'long' }),
					morningSlots: [],
					afternoonSlots: [],
				});
			}
			const group = groups.get(slot.date)!;
			const hour = parseInt(slot.start_time.split(':')[0], 10);
			if (hour < 13) {
				group.morningSlots.push(slot);
			} else {
				group.afternoonSlots.push(slot);
			}
		}

		this.slotGroups = Array.from(groups.values());
		if (this.slotGroups.length > 0) {
			this.selectDateGroup(this.slotGroups[0]);
		}
	}

	@ViewChild('morningAnchor') morningAnchor!: ElementRef;
	@ViewChild('afternoonAnchor') afternoonAnchor!: ElementRef;
	@ViewChild('timeChipsScroll') timeChipsScroll!: ElementRef;

	selectDateGroup(group: SlotGroup): void {
		this.selectedDateGroup = group;
		this.selectedSlot = null;
		this.selectedPeriod = group.morningSlots.length > 0 ? 'morning' : 'afternoon';
	}

	selectPeriod(period: 'morning' | 'afternoon'): void {
		this.selectedPeriod = period;
		const anchor = period === 'morning' ? this.morningAnchor : this.afternoonAnchor;
		if (anchor?.nativeElement && this.timeChipsScroll?.nativeElement) {
			anchor.nativeElement.scrollIntoView({ behavior: 'smooth', inline: 'start', block: 'nearest' });
		}
	}

	onTimeScroll(): void {
		if (!this.timeChipsScroll?.nativeElement || !this.afternoonAnchor?.nativeElement) return;
		const container = this.timeChipsScroll.nativeElement;
		const afternoonEl = this.afternoonAnchor.nativeElement;
		const containerRect = container.getBoundingClientRect();
		const afternoonRect = afternoonEl.getBoundingClientRect();
		this.selectedPeriod = afternoonRect.left <= containerRect.left + containerRect.width / 2 ? 'afternoon' : 'morning';
	}

	getDayName(group: SlotGroup): string {
		const d = new Date(group.date);
		const today = new Date();
		today.setHours(0, 0, 0, 0);
		const dateObj = new Date(group.date);
		dateObj.setHours(0, 0, 0, 0);
		if (dateObj.getTime() === today.getTime()) {
			return 'Oggi';
		}
		return d.toLocaleDateString('it-IT', { weekday: 'short' });
	}

	getDayDate(group: SlotGroup): string {
		const d = new Date(group.date);
		return d.toLocaleDateString('it-IT', { day: 'numeric', month: 'short' });
	}

	selectSlot(slot: FreeSlot): void {
		this.selectedSlot = slot;
	}

	confirmSlotSelection(): void {
		if (this.selectedSlot) {
			this.currentStep = 3;
		}
	}

	// --- Step 2 SU_RICHIESTA ---

	async addPhoto(): Promise<void> {
		try {
			const photo = await Camera.getPhoto({
				resultType: CameraResultType.Uri,
				source: CameraSource.Camera,
				quality: 80,
			});
			if (photo.webPath) {
				const response = await fetch(photo.webPath);
				const blob = await response.blob();
				const file = new File([blob], `foto_${Date.now()}.jpg`, { type: 'image/jpeg' });
				this.pendingFiles.push(file);
			}
		} catch (e) {
			console.error('Camera error:', e);
		}
	}

	pickFile(): void {
		const input = document.createElement('input');
		input.type = 'file';
		input.accept = 'image/*,.pdf,.doc,.docx';
		input.onchange = (event: any) => {
			const file = event.target.files[0];
			if (file) {
				this.pendingFiles.push(file);
			}
		};
		input.click();
	}

	removePendingFile(index: number): void {
		this.pendingFiles.splice(index, 1);
	}

	canProceedStep2(): boolean {
		if (!this.primaryServiceType) return false;
		if (this.primaryServiceType.st_mode === 'SU_RICHIESTA') {
			if (!this.preferredDate || !this.preferredTime) return false;
			if (this.anyRequiresAttachments && this.pendingFiles.length === 0) return false;
		}
		return true;
	}

	proceedToStep3(): void {
		if (this.canProceedStep2()) {
			this.currentStep = 3;
		}
	}

	// --- Step 3 ---

	goBack(): void {
		if (this.currentStep > 1) {
			this.currentStep--;
			if (this.currentStep === 1) {
				this.selectedSlot = null;
			}
		}
	}

	async submitRequest(): Promise<void> {
		if (!this.primaryServiceType) return;

		this.submitting = true;
		const loader = await this.loadingController.create({ message: 'Invio richiesta...' });
		await loader.present();

		const allServiceIds = this.selectedServiceTypes.map((st) => st.st_id).join(',');

		const appointment: Partial<Appointment> = {
			apt_cmp_id: this.cmp_id,
			apt_service_type_id: this.primaryServiceType.st_id,
			apt_service_type_ids: allServiceIds,
			apt_client_usr_id: this.configService.getUserID(),
			apt_status: 'IN_ATTESA',
			apt_duration_minutes: this.totalDurationMinutes,
			apt_client_notes: this.additionalNotes || '',
		};

		if (this.primaryServiceType.st_mode === 'SLOT_FISSO' && this.selectedSlot) {
			appointment.apt_requested_date = this.selectedSlot.date;
			appointment.apt_requested_time = this.selectedSlot.start_time;
			appointment.apt_operator_em_id = this.selectedSlot.operator_em_id;
		} else if (this.primaryServiceType.st_mode === 'SU_RICHIESTA') {
			const d = new Date(this.preferredDate);
			appointment.apt_requested_date = this.formatDateISO(d);
			const t = new Date(this.preferredTime);
			appointment.apt_requested_time = `${String(t.getHours()).padStart(2, '0')}:${String(t.getMinutes()).padStart(2, '0')}`;
			if (this.problemDescription) {
				appointment.apt_client_notes = this.problemDescription + (this.additionalNotes ? '\n\n' + this.additionalNotes : '');
			}
		} else if (this.primaryServiceType.st_mode === 'SU_PREVENTIVO') {
			// Richiesta di preventivo: nessuna data/ora, solo la descrizione dell'intervento
			if (this.problemDescription) {
				appointment.apt_client_notes = this.problemDescription + (this.additionalNotes ? '\n\n' + this.additionalNotes : '');
			}
		}

		this.appointmentsService.insertAppointment(appointment).subscribe({
			next: (newId: number) => {
				if (this.pendingFiles.length > 0) {
					this.uploadPendingFiles(newId, loader);
				} else {
					loader.dismiss();
					this.configService.presentToastSuccess('Richiesta inviata con successo');
					this.router.navigate(['../list'], { relativeTo: this.route });
				}
			},
			error: () => {
				loader.dismiss();
				this.submitting = false;
				this.configService.presentToastError('Errore invio richiesta');
			},
		});
	}

	private uploadPendingFiles(apt_id: number, loader: any): void {
		let uploaded = 0;
		for (const file of this.pendingFiles) {
			this.appointmentsService.uploadAttachment(apt_id, file).subscribe({
				next: () => {
					uploaded++;
					if (uploaded === this.pendingFiles.length) {
						loader.dismiss();
						this.configService.presentToastSuccess('Richiesta inviata con successo');
						this.router.navigate(['../list'], { relativeTo: this.route });
					}
				},
				error: () => {
					uploaded++;
					if (uploaded === this.pendingFiles.length) {
						loader.dismiss();
						this.configService.presentToastWarning('Richiesta inviata, ma alcuni allegati non sono stati caricati');
						this.router.navigate(['../list'], { relativeTo: this.route });
					}
				},
			});
		}
	}

	// --- Helpers ---

	private formatDateISO(date: Date): string {
		const y = date.getFullYear();
		const m = String(date.getMonth() + 1).padStart(2, '0');
		const d = String(date.getDate()).padStart(2, '0');
		return `${y}-${m}-${d}`;
	}

	getSummaryDate(): string {
		if (this.primaryServiceType?.st_mode === 'SLOT_FISSO' && this.selectedSlot) {
			const d = new Date(this.selectedSlot.date);
			return d.toLocaleDateString('it-IT', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
		}
		if (this.preferredDate) {
			const d = new Date(this.preferredDate);
			return d.toLocaleDateString('it-IT', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
		}
		return '-';
	}

	getSummaryTime(): string {
		if (this.primaryServiceType?.st_mode === 'SLOT_FISSO' && this.selectedSlot) {
			return `${this.selectedSlot.start_time.substring(0, 5)} - ${this.selectedSlot.end_time.substring(0, 5)}`;
		}
		if (this.preferredTime) {
			const t = new Date(this.preferredTime);
			return `${String(t.getHours()).padStart(2, '0')}:${String(t.getMinutes()).padStart(2, '0')}`;
		}
		return '-';
	}
}
