import {
  AfterViewInit,
  Component,
  computed,
  ElementRef,
  inject,
  OnDestroy,
  OnInit,
  Renderer2,
  signal
} from '@angular/core';
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
import {ApplicationsService, EmailEntry, MailService, StaffApplication} from '@api';
import {HeaderComponent} from '@header/header.component';
import {NgOptimizedImage} from '@angular/common';
import {MatButtonModule} from '@angular/material/button';
import {MatIconModule} from '@angular/material/icon';
import {AuthService} from '@services/auth.service';
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
import {MatFormFieldModule} from '@angular/material/form-field';
import {MatSelectModule} from '@angular/material/select';
import {MatInputModule} from '@angular/material/input';
import {MatDialog} from '@angular/material/dialog';
import {VerifyMailDialogComponent} from '@pages/forms/verify-mail-dialog/verify-mail-dialog.component';
import {Router} from '@angular/router';
import {MatCheckboxModule} from '@angular/material/checkbox';
import {MatDatepickerModule} from '@angular/material/datepicker';
import {MatNativeDateModule} from '@angular/material/core';
import {MatChipsModule} from '@angular/material/chips';
import {MatSnackBar} from '@angular/material/snack-bar';

@Component({
  selector: 'app-staff-application',
  imports: [
    HeaderComponent,
    NgOptimizedImage,
    MatButtonModule,
    MatIconModule,
    MatProgressSpinnerModule,
    MatFormFieldModule,
    MatSelectModule,
    MatInputModule,
    ReactiveFormsModule,
    MatCheckboxModule,
    MatDatepickerModule,
    MatNativeDateModule,
    MatChipsModule
  ],
  templateUrl: './staff-application.component.html',
  styleUrl: './staff-application.component.scss'
})
export class StaffApplicationComponent implements OnInit, OnDestroy, AfterViewInit {

  private mailService = inject(MailService);
  private matSnackBar = inject(MatSnackBar);
  public authService = inject(AuthService);
  public staffApplicationService = inject(ApplicationsService)
  private resizeObserver: ResizeObserver | null = null;
  private boundHandleResize: any;
  protected isSubmitting = signal<boolean>(false);

  protected form: FormGroup<StaffApplicationForm>;
  private emails = signal<EmailEntry[]>([]);
  protected verifiedEmails = computed(() => this.emails()
    .filter(email => email.verified)
    .map(email => email.email.toLowerCase()));
  protected emailIsValid = signal<boolean>(false);
  protected dialog = inject(MatDialog);
  protected availableDays: string[] = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
  protected selectedDays: string[] = [];
  protected userTimezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
  protected staffApplicationsIsOpen = signal<boolean>(false)


  constructor(
    private elementRef: ElementRef,
    private renderer: Renderer2
  ) {
    const staffApplication: StaffApplicationForm = {
      email: new FormControl('', {
        nonNullable: true,
        validators: [Validators.required, Validators.email, Validators.maxLength(320)]
      }),
      age: new FormControl('', {
        nonNullable: true,
        validators: [Validators.required, Validators.min(13), Validators.pattern('^[0-9]*$')]
      }),
      discordUsername: new FormControl('', {
        nonNullable: true,
        validators: [Validators.required, Validators.maxLength(32)]
      }),
      meetsRequirements: new FormControl(false, {
        nonNullable: true,
        validators: [Validators.requiredTrue]
      }),
      pronouns: new FormControl('', {
        nonNullable: true,
        validators: [Validators.maxLength(32)]
      }),
      joinDate: new FormControl('', {
        nonNullable: true,
        validators: [Validators.required]
      }),
      weeklyPlaytime: new FormControl('', {
        nonNullable: true,
        validators: [Validators.required, Validators.min(1), Validators.pattern('^[0-9]*$')]
      }),
      availableDays: new FormControl([], {
        nonNullable: true,
        validators: [Validators.required]
      }),
      availableTimes: new FormControl('', {
        nonNullable: true,
        validators: [Validators.required, Validators.maxLength(900)]
      }),
      previousExperience: new FormControl('', {
        nonNullable: true,
        validators: [Validators.required, Validators.minLength(10), Validators.maxLength(4000)]
      }),
      pluginExperience: new FormControl('', {
        nonNullable: true,
        validators: [Validators.required, Validators.minLength(10), Validators.maxLength(4000)]
      }),
      moderatorExpectations: new FormControl('', {
        nonNullable: true,
        validators: [Validators.required, Validators.minLength(10), Validators.maxLength(4000)]
      }),
      additionalInfo: new FormControl('', {
        nonNullable: true,
        validators: [Validators.maxLength(4000)]
      })
    }
    this.form = new FormGroup(staffApplication);

    this.mailService.getUserEmails().subscribe(emails => {
      this.emails.set(emails);
    });

    this.form.valueChanges.subscribe(() => {
      if (this.verifiedEmails().includes(this.form.getRawValue().email.toLowerCase())) {
        this.emailIsValid.set(true);
      } else {
        this.emailIsValid.set(false);
      }
    });

    computed(() => {
      if (this.verifiedEmails().length > 0) {
        this.form.get('email')?.setValue(this.verifiedEmails()[0]);
        this.emailIsValid.set(true);
      }
    });
  }

  ngOnInit() {
    this.staffApplicationService.getStaffApplicationsIsOpen().subscribe(isOpen => {
      this.staffApplicationsIsOpen.set(isOpen)
    })
    const uuid = this.authService.getUuid();
    if (uuid === null) {
      alert('Error retrieving token, please relog on the website and try again')
      throw new Error('JWT subject is null, are you logged in?');
    }
  }

  ngAfterViewInit() {
    this.setupResizeObserver();
    this.updateContainerHeight();

    this.boundHandleResize = this.handleResize.bind(this);
    window.addEventListener('resize', this.boundHandleResize);

    setTimeout(() => this.updateContainerHeight(), 0);
  }

  ngOnDestroy() {
    if (this.resizeObserver) {
      this.resizeObserver.disconnect();
      this.resizeObserver = null;
    }

    if (this.boundHandleResize) {
      window.removeEventListener('resize', this.boundHandleResize);
    }
  }

  private handleResize() {
    this.updateContainerHeight();
  }

  private setupResizeObserver() {
    this.resizeObserver = new ResizeObserver(() => {
      this.updateContainerHeight();
    });

    const headerElement = document.querySelector('app-header');
    if (headerElement) {
      this.resizeObserver.observe(headerElement);
    }

    const footerElement = document.querySelector('footer');
    if (footerElement) {
      this.resizeObserver.observe(footerElement);
    }
  }

  private updateContainerHeight() {
    const headerElement = document.querySelector('app-header');
    const footerElement = document.querySelector('footer');

    const container = this.elementRef.nativeElement.querySelector('.staff-application-container');

    if (headerElement && footerElement && container) {
      const headerHeight = headerElement.getBoundingClientRect().height;
      const footerHeight = footerElement.getBoundingClientRect().height;

      const calculatedHeight = `calc(100vh - ${headerHeight}px - ${footerHeight}px)`;
      this.renderer.setStyle(container, 'min-height', calculatedHeight);
    }
  }

  public onSubmit() {
    this.isSubmitting.set(true);
    if (this.form === undefined) {
      console.error('Form is undefined');
      this.matSnackBar.open('An error occurred, please try again later')
      this.isSubmitting.set(false);
      return
    }
    if (this.form.valid) {
      this.sendForm()
    } else {
      // Mark all fields as touched to show validation errors
      Object.keys(this.form.controls).forEach(field => {
        const control = this.form.get(field);
        control?.markAsTouched();
      });
      this.matSnackBar.open('Please fill out all required fields')
      this.isSubmitting.set(false);
    }
  }


  isFormInvalid() {
    return !this.form.valid
  }

  private router = inject(Router)

  private sendForm() {
    const staffApplication: StaffApplication = this.mapToStaffApplication(this.form.getRawValue());

    this.staffApplicationService.submitStaffApplication(staffApplication).subscribe({
      next: result => {
        if (!result.verified_mail) {
          this.isSubmitting.set(false);
          this.matSnackBar.open('Your email has not been verified. Please verify your email before submitting.', 'Close', {
            duration: 5000
          });
          return;
        }
        this.router.navigate(['/forms/sent'], {
          state: {message: result.message}
        }).then();
      },
      error: (error) => {
        this.isSubmitting.set(false);
        console.error('Error submitting application:', error);
        this.matSnackBar.open('An error occurred while submitting your application. Please try again later.', 'Close', {
          duration: 5000
        });
      }
    });
  }

  public currentPageIndex: number = 0;
  public totalPages: number[] = [0, 1, 2, 3, 4];

  public goToPage(pageIndex: number): void {
    if (pageIndex >= 0 && pageIndex < this.totalPages.length) {
      this.currentPageIndex = pageIndex;
    }
  }

  public previousPage() {
    this.goToPage(this.currentPageIndex - 1);
  }

  public nextPage() {
    this.goToPage(this.currentPageIndex + 1);
  }

  public isFirstPage(): boolean {
    return this.currentPageIndex === 0;
  }

  public isLastPage(): boolean {
    return this.currentPageIndex === this.totalPages.length - 1;
  }

  protected validateMailOrNextPage() {
    if (this.emailIsValid()) {
      this.nextPage();
      return;
    }
    const dialogRef = this.dialog.open(VerifyMailDialogComponent, {
      data: {email: this.form.getRawValue().email},
    });
    dialogRef.afterClosed().subscribe(result => {
      if (result === true) {
        this.emailIsValid.set(true);
        this.nextPage();
      }
    });
  }

  toggleDay(day: string) {
    const availableDaysControl = this.form.get('availableDays');
    const currentDays = [...(availableDaysControl?.value || [])];

    if (currentDays.includes(day)) {
      const index = currentDays.indexOf(day);
      currentDays.splice(index, 1);
    } else {
      currentDays.push(day);
    }

    availableDaysControl?.setValue(currentDays);
  }

  private mapToStaffApplication(formData: any): StaffApplication {
    let joinDateString: string;

    if (formData.joinDate instanceof Date) {
      joinDateString = formData.joinDate.toISOString();
    } else if (typeof formData.joinDate === 'string' && formData.joinDate.trim() !== '') {
      const parsedDate = new Date(formData.joinDate);
      if (isNaN(parsedDate.getTime())) {
        throw new Error('Invalid date string');
      }
      joinDateString = parsedDate.toISOString();
    } else {
      throw new Error('Invalid date string');
    }

    return {
      email: formData.email,
      age: Number(formData.age),
      discordUsername: formData.discordUsername,
      meetsRequirements: formData.meetsRequirements,
      pronouns: formData.pronouns || '',
      joinDate: joinDateString,
      weeklyPlaytime: Number(formData.weeklyPlaytime),
      availableDays: formData.availableDays,
      availableTimes: `Timezone: ${this.userTimezone}\nAvailable Times: ${formData.availableTimes}`,
      previousExperience: formData.previousExperience,
      pluginExperience: formData.pluginExperience,
      moderatorExpectations: formData.moderatorExpectations,
      additionalInfo: formData.additionalInfo || ''
    };

  }
}

interface StaffApplicationForm {
  email: FormControl<string>;
  age: FormControl<string>;
  discordUsername: FormControl<string>;
  meetsRequirements: FormControl<boolean>;
  pronouns: FormControl<string>;
  joinDate: FormControl<string>;
  weeklyPlaytime: FormControl<string>;
  availableDays: FormControl<string[]>;
  availableTimes: FormControl<string>;
  previousExperience: FormControl<string>;
  pluginExperience: FormControl<string>;
  moderatorExpectations: FormControl<string>;
  additionalInfo: FormControl<string>;
}
