import { Component, OnInit, Input, Output, EventEmitter, ElementRef, ViewChild, HostListener, Renderer2 } from '@angular/core';

@Component({
  selector: 'nbp-fid-calendar-generic',
  templateUrl: './nbp-fid-calendar-generic.component.html',
  styleUrls: ['./nbp-fid-calendar-generic.component.scss']
})
export class NbpFidCalendarGenericComponent implements OnInit {
  @Input() dateFormat: string = 'dd/MM/yyyy';
  @Input() disabledWeekdays: number[] = []; // 0=domenica, 6=sabato
  @Input() disabledDateRanges: { start: Date, end: Date }[] = [];
  @Input() minDate: Date;
  @Input() maxDate: Date;
  @Output() dateChange = new EventEmitter<Date>();

  @ViewChild('dateInput') dateInput: ElementRef;
  
  // Cache per evitare ricalcoli continui e problemi di rendering
  private _previousMonthDaysCache: { day: number, disabled: boolean }[] = [];
  private _currentMonthDaysCache: { day: number, isToday: boolean, isSelected: boolean, disabled: boolean }[] = [];
  private _nextMonthDaysCache: { day: number, disabled: boolean }[] = [];
  private _visibleMonthsCache: { monthIndex: number, name: string, isCurrentMonth: boolean, isSelected: boolean, disabled: boolean }[] = [];
  private _visibleYearsCache: { year: number, isCurrentYear: boolean, isSelected: boolean, disabled: boolean }[] = [];
  @ViewChild('calendarPopup') calendarPopup: ElementRef;

  currentDate: Date = new Date();
  selectedDate: Date = null;
  view: 'days' | 'months' | 'years' = 'days';
  months: string[] = ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno', 'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'];
  daysOfWeek: string[] = ['lun', 'mar', 'mer', 'gio', 'ven', 'sab', 'dom'];
  formattedSelectedDate: string = '';
  calendarVisible: boolean = false;

  constructor(private renderer: Renderer2, private elementRef: ElementRef) { }

  ngOnInit(): void {
    // Inizializza la data selezionata se presente
    if (this.selectedDate) {
      this.formattedSelectedDate = this.formatDate(this.selectedDate, this.dateFormat);
    }
    
    // Inizializza le cache
    this.updateAllCaches();
  }

  // --- UTILITY ---
  private pad(n: number): string {
    return n < 10 ? '0' + n : n.toString();
  }

  formatDate(date: Date, format: string): string {
    if (!date) return '';
    return format
      .replace('dd', this.pad(date.getDate()))
      .replace('MM', this.pad(date.getMonth() + 1))
      .replace('yyyy', date.getFullYear().toString())
      .replace('yy', String(date.getFullYear()).slice(-2));
  }

  sameDate(d1: Date, d2: Date): boolean {
    return d1.getFullYear() === d2.getFullYear() &&
      d1.getMonth() === d2.getMonth() &&
      d1.getDate() === d2.getDate();
  }

  // --- GESTIONE POPUP ---
  positionCalendarPopup(): void {
    if (!this.calendarPopup || !this.dateInput) return;
    
    const input = this.dateInput.nativeElement;
    const popup = this.calendarPopup.nativeElement;
    
    // Sposta il popup in fondo al body (portal)
    if (popup.parentNode !== document.body) {
      document.body.appendChild(popup);
    }
    
    popup.style.display = 'block';
    popup.style.position = 'absolute';
    popup.style.zIndex = '9999';
    
    const rect = input.getBoundingClientRect();
    const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
    const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
    
    popup.style.top = (rect.bottom + scrollTop) + 'px';
    popup.style.left = (rect.left + scrollLeft) + 'px';
  }

  toggleCalendar(): void {
    this.calendarVisible = !this.calendarVisible;
    if (this.calendarVisible) {
      this.showCalendar();
      this.updateAllCaches();
    } else {
      this.hideCalendar();
    }
  }

  showCalendar(): void {
    this.calendarVisible = true;
    setTimeout(() => {
      this.positionCalendarPopup();
    });
  }

  hideCalendar(): void {
    this.calendarVisible = false;
    if (this.calendarPopup) {
      this.calendarPopup.nativeElement.style.display = 'none';
    }
    
    if (this.selectedDate) {
      this.currentDate = new Date(this.selectedDate);
    } else {
      this.currentDate = new Date();
    }
  }

  @HostListener('document:mousedown', ['$event'])
  onClickOutside(event: MouseEvent): void {
    if (this.calendarVisible && 
        !this.elementRef.nativeElement.contains(event.target) &&
        this.calendarPopup && 
        !this.calendarPopup.nativeElement.contains(event.target)) {
      this.hideCalendar();
    }
  }

  // --- LOGICA DATE ---
  isDateDisabled(date: Date): boolean {
    // Disabilita date precedenti alla minDate
    if (this.minDate && date < this.minDate) return true;

    // Disabilita date precedenti alla maxDate
    if (this.maxDate && date > this.maxDate) return true;

    // Disabilita per giorno della settimana
    if (this.disabledWeekdays.indexOf(date.getDay()) !== -1) return true;

    // Disabilita per range
    for (const range of this.disabledDateRanges) {
      if (date >= range.start && date <= range.end) return true;
    }
    
    return false;
  }

  // --- NAVIGATION ---
  prev(): void {
    if (this.view === 'days') {
      this.currentDate.setMonth(this.currentDate.getMonth() - 1);
    } else if (this.view === 'months') {
      this.currentDate.setFullYear(this.currentDate.getFullYear() - 1);
    } else {
      this.currentDate.setFullYear(this.currentDate.getFullYear() - 20);
    }
    this.currentDate = new Date(this.currentDate); // Forza refresh
    this.updateAllCaches();
  }

  next(): void {
    if (this.view === 'days') {
      this.currentDate.setMonth(this.currentDate.getMonth() + 1);
    } else if (this.view === 'months') {
      this.currentDate.setFullYear(this.currentDate.getFullYear() + 1);
    } else {
      this.currentDate.setFullYear(this.currentDate.getFullYear() + 20);
    }
    this.currentDate = new Date(this.currentDate); // Forza refresh
    this.updateAllCaches();
  }

  goToday(): void {
    this.currentDate = new Date();
    this.selectedDate = new Date();
    this.formattedSelectedDate = this.formatDate(this.selectedDate, this.dateFormat);
    this.view = 'days';
    this.dateChange.emit(this.selectedDate);
    this.updateAllCaches();
  }

  clearSelection(): void {
    this.selectedDate = null;
    this.formattedSelectedDate = '';
    this.dateChange.emit(null);
  }

  confirmDate(): void {
    // Metodo usato per confermare esplicitamente la data corrente
    this.dateChange.emit(this.selectedDate);
    this.hideCalendar();
  }

  changeView(): void {
    if (this.view === 'days') {
      this.view = 'months';
    } else if (this.view === 'months') {
      this.view = 'years';
    }
    this.updateAllCaches();
  }

  // --- SELEZIONE DATE ---
  selectDate(date: Date): void {
    if (this.isDateDisabled(date)) return;
    
    this.selectedDate = date;
    this.formattedSelectedDate = this.formatDate(this.selectedDate, this.dateFormat);
    // Non emettiamo qui l'evento dateChange per evitare doppie emissioni
    this.updateAllCaches();
  }

  selectMonth(monthIndex: number): void {
    if (this.isMonthDisabled(monthIndex)) return;
    
    this.currentDate.setMonth(monthIndex);
    this.currentDate = new Date(this.currentDate); // Forza refresh
    this.view = 'days';
    this.updateAllCaches();
  }

  selectYear(year: number): void {
    if (this.isYearDisabled(year)) return;
    
    this.currentDate.setFullYear(year);
    this.currentDate = new Date(this.currentDate); // Forza refresh
    this.view = 'months';
    this.updateAllCaches();
  }

  selectDayFromPrevMonth(day: number): void {
    const year = this.currentDate.getFullYear();
    const month = this.currentDate.getMonth();
    const prevMonthDate = new Date(year, month - 1, day);
    
    if (this.isDateDisabled(prevMonthDate)) return;
    
    this.currentDate.setMonth(month - 1);
    this.currentDate = new Date(this.currentDate); // Forza refresh
    this.selectedDate = prevMonthDate;
    this.formattedSelectedDate = this.formatDate(this.selectedDate, this.dateFormat);
    this.dateChange.emit(this.selectedDate);
    this.updateAllCaches();
    this.hideCalendar(); // Chiudi il calendario dopo la selezione
  }

  selectDayFromNextMonth(day: number): void {
    const year = this.currentDate.getFullYear();
    const month = this.currentDate.getMonth();
    const nextMonthDate = new Date(year, month + 1, day);
    
    if (this.isDateDisabled(nextMonthDate)) return;
    
    this.currentDate.setMonth(month + 1);
    this.currentDate = new Date(this.currentDate); // Forza refresh
    this.selectedDate = nextMonthDate;
    this.formattedSelectedDate = this.formatDate(this.selectedDate, this.dateFormat);
    this.dateChange.emit(this.selectedDate);
    this.updateAllCaches();
    this.hideCalendar(); // Chiudi il calendario dopo la selezione
  }

  // --- UTILITY PER MESI E ANNI ---
  isMonthDisabled(monthIndex: number): boolean {
    // Se non ci sono restrizioni, il mese è abilitato
    if (!this.minDate && !this.maxDate && (!this.disabledDateRanges || this.disabledDateRanges.length === 0)) {
      return false;
    }
    
    const year = this.currentDate.getFullYear();
    const firstDayOfMonth = new Date(year, monthIndex, 1, 0, 0, 0, 0);
    const lastDayOfMonth = new Date(year, monthIndex + 1, 0, 23, 59, 59, 999);
    
    // Disabilita mese se completamente fuori dai limiti
    if (this.minDate && lastDayOfMonth < this.minDate) return true;
    if (this.maxDate && firstDayOfMonth > this.maxDate) return true;
    
    return false;
  }

  isYearDisabled(year: number): boolean {
    // Se non ci sono restrizioni, l'anno è abilitato
    if (!this.minDate && !this.maxDate) {
      return false;
    }
    
    const firstDayOfYear = new Date(year, 0, 1, 0, 0, 0, 0);
    const lastDayOfYear = new Date(year, 11, 31, 23, 59, 59, 999);
    
    // Disabilita anno se completamente fuori dai limiti
    if (this.minDate && lastDayOfYear < this.minDate) return true;
    if (this.maxDate && firstDayOfYear > this.maxDate) return true;
    
    return false;
  }

  // --- INPUT HANDLING ---
  onDateInputChange(event: Event): void {
    const value = (event.target as HTMLInputElement).value;
    const datePattern = /^(\d{2})\/(\d{2})\/(\d{4})$/;
    
    if (datePattern.test(value)) {
      const [, dd, mm, yyyy] = value.match(datePattern);
      const date = new Date(parseInt(yyyy), parseInt(mm) - 1, parseInt(dd));
      
      // Controlla che sia una data valida
      if (date && 
          date.getDate() === parseInt(dd) && 
          (date.getMonth() + 1) === parseInt(mm) && 
          date.getFullYear() === parseInt(yyyy)) {
        
        if (!this.isDateDisabled(date)) {
          this.selectedDate = date;
          this.currentDate = new Date(date);
          this.formattedSelectedDate = this.formatDate(this.selectedDate, this.dateFormat);
          this.dateChange.emit(this.selectedDate);
          this.updateAllCaches();
          return;
        }
      }
    }
  }

  // --- CALENDAR DATA ---
  getDaysInMonth(year: number, month: number): number {
    return new Date(year, month + 1, 0).getDate();
  }
  
  // Metodo per aggiornare tutte le cache quando cambiano i dati
  private updateAllCaches(): void {
    this.updatePreviousMonthDaysCache();
    this.updateCurrentMonthDaysCache();
    this.updateNextMonthDaysCache();
    this.updateVisibleMonthsCache();
    this.updateVisibleYearsCache();
  }
  
  // Metodi per aggiornare le singole cache
  private updatePreviousMonthDaysCache(): void {
    const year = this.currentDate.getFullYear();
    const month = this.currentDate.getMonth();
    const startDay = this.getFirstDayOfMonth(year, month);
    const daysInPrevMonth = this.getDaysInMonth(year, month - 1);
    
    const days = [];
    for (let i = startDay; i > 0; i--) {
      const day = daysInPrevMonth - i + 1;
      const prevMonthDate = new Date(year, month - 1, day);
      days.push({
        day,
        disabled: this.isDateDisabled(prevMonthDate)
      });
    }
    
    this._previousMonthDaysCache = days;
  }
  
  private updateCurrentMonthDaysCache(): void {
    const year = this.currentDate.getFullYear();
    const month = this.currentDate.getMonth();
    const daysInMonth = this.getDaysInMonth(year, month);
    const today = new Date();
    
    const days = [];
    for (let i = 1; i <= daysInMonth; i++) {
      const date = new Date(year, month, i);
      days.push({
        day: i,
        isToday: this.sameDate(today, date),
        isSelected: this.selectedDate ? this.sameDate(this.selectedDate, date) : false,
        disabled: this.isDateDisabled(date)
      });
    }
    
    this._currentMonthDaysCache = days;
  }
  
  private updateNextMonthDaysCache(): void {
    const year = this.currentDate.getFullYear();
    const month = this.currentDate.getMonth();
    const startDay = this.getFirstDayOfMonth(year, month);
    const daysInMonth = this.getDaysInMonth(year, month);
    
    const totalCells = 42; // 7 giorni x 6 settimane
    const nextMonthDays = totalCells - (startDay + daysInMonth);
    
    const days = [];
    for (let i = 1; i <= nextMonthDays; i++) {
      const nextMonthDate = new Date(year, month + 1, i);
      days.push({
        day: i,
        disabled: this.isDateDisabled(nextMonthDate)
      });
    }
    
    this._nextMonthDaysCache = days;
  }
  
  private updateVisibleMonthsCache(): void {
    const currentYear = this.currentDate.getFullYear();
    const today = new Date();
    
    const months = this.months.map((name, idx) => ({
      monthIndex: idx,
      name,
      isCurrentMonth: currentYear === today.getFullYear() && idx === today.getMonth(),
      isSelected: this.selectedDate ? 
                  currentYear === this.selectedDate.getFullYear() && 
                  idx === this.selectedDate.getMonth() : false,
      disabled: this.isMonthDisabled(idx)
    }));
    
    this._visibleMonthsCache = months;
  }
  
  private updateVisibleYearsCache(): void {
    const base = Math.floor(this.currentDate.getFullYear() / 20) * 20;
    const currentYear = new Date().getFullYear();
    
    const years = [];
    for (let y = base; y < base + 20; y++) {
      years.push({
        year: y,
        isCurrentYear: y === currentYear,
        isSelected: this.selectedDate ? y === this.selectedDate.getFullYear() : false,
        disabled: this.isYearDisabled(y)
      });
    }
    
    this._visibleYearsCache = years;
  }

  getFirstDayOfMonth(year: number, month: number): number {
    return (new Date(year, month, 1).getDay() + 6) % 7; // Lunedì=0, Domenica=6
  }

  getPreviousMonthDays(): { day: number, disabled: boolean }[] {
    return this._previousMonthDaysCache;
  }

  getCurrentMonthDays(): { day: number, isToday: boolean, isSelected: boolean, disabled: boolean }[] {
    return this._currentMonthDaysCache;
  }

  getNextMonthDays(): { day: number, disabled: boolean }[] {
    return this._nextMonthDaysCache;
  }

  getVisibleYears(): { year: number, isCurrentYear: boolean, isSelected: boolean, disabled: boolean }[] {
    return this._visibleYearsCache;
  }

  getVisibleMonths(): { monthIndex: number, name: string, isCurrentMonth: boolean, isSelected: boolean, disabled: boolean }[] {
    return this._visibleMonthsCache;
  }

  get yearRange(): string {
    if (this.view === 'years') {
      const base = Math.floor(this.currentDate.getFullYear() / 20) * 20;
      return `${base} - ${base + 19}`;
    }
    return this.currentDate.getFullYear().toString();
  }

  get monthYearHeader(): string {
    if (this.view === 'days') {
      return `${this.months[this.currentDate.getMonth()]} ${this.currentDate.getFullYear()}`;
    }
    return this.yearRange;
  }

  // --- AZIONI SUI GIORNI, MESI E ANNI ---
  onPrevMonthDayClick(day: { day: number, disabled: boolean }): void {
    if (day.disabled) return;
    this.selectDayFromPrevMonth(day.day);
    // Non serve chiamare hideCalendar() qui perché lo fa già selectDayFromPrevMonth
  }

  onCurrentMonthDayClick(day: { day: number, isToday: boolean, isSelected: boolean, disabled: boolean }): void {
    if (day.disabled) return;
    const date = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth(), day.day);
    this.selectDate(date);
    this.dateChange.emit(this.selectedDate);
    this.hideCalendar();
  }

  onNextMonthDayClick(day: { day: number, disabled: boolean }): void {
    if (day.disabled) return;
    this.selectDayFromNextMonth(day.day);
    // Non serve chiamare hideCalendar() qui perché lo fa già selectDayFromNextMonth
  }

  onMonthClick(month: { monthIndex: number, name: string, isCurrentMonth: boolean, isSelected: boolean, disabled: boolean }): void {
    if (month.disabled) return;
    this.selectMonth(month.monthIndex);
  }

  onYearClick(yearObj: { year: number, isCurrentYear: boolean, isSelected: boolean, disabled: boolean }): void {
    if (yearObj.disabled) return;
    this.selectYear(yearObj.year);
  }
}
