import {Component, OnDestroy, OnInit} from '@angular/core';
import * as FA from '@fortawesome/free-solid-svg-icons';
import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy';
import {Route} from '@shared/models/route.enum';
import {Store} from '@ngxs/store';
import {Router} from '@angular/router';
import {ChangePentest} from '@shared/stores/project-state/project-state.actions';
import {BehaviorSubject} from 'rxjs';
import {ProjectState} from '@shared/stores/project-state/project-state';
import {Project} from '@shared/models/project.model';
import {Pentest, transformPentestToRequestBody} from '@shared/models/pentest.model';
import {NotificationService, PopupType} from '@shared/services/toaster-service/notification.service';
import {PentestStatus} from '@shared/models/pentest-status.model';
import {PentestService} from '@shared/services/api/pentest.service';
import {StatusText} from '@shared/widgets/status-tag/status-tag.component';

@UntilDestroy()
@Component({
  selector: 'app-pentest-header',
  templateUrl: './pentest-header.component.html',
  styleUrls: ['./pentest-header.component.scss']
})
export class PentestHeaderComponent implements OnInit, OnDestroy {
  // HTML only
  readonly fa = FA;

  pentest$: BehaviorSubject<Pentest> = new BehaviorSubject<Pentest>(null);
  selectedProjectTitle$: BehaviorSubject<string> = new BehaviorSubject<string>('');
  pentestChanged$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

  // Pentest Timer Handler
  currentTimeSpent = 0;
  private initialTimeSpent: number;

  // Pentest Status Handler
  status = PentestStatus;
  currentStatus: PentestStatus = PentestStatus.NOT_STARTED;
  private initialPentestStatus: PentestStatus;

  // Status Text Translation Texts
  readonly statusTexts: Array<StatusText> = [
    {value: PentestStatus.NOT_STARTED, translationText: 'pentest.statusText.not_started'},
    /* ToDo: Disabled not needed inside pentest */
    /*{value: PentestStatus.DISABLED, translationText: 'pentest.statusText.disabled'},*/
    {value: PentestStatus.PAUSED, translationText: 'pentest.statusText.paused'},
    {value: PentestStatus.IN_PROGRESS, translationText: 'pentest.statusText.in_progress'},
    {value: PentestStatus.COMPLETED, translationText: 'pentest.statusText.completed'}
  ];

  selectedProjectId$: BehaviorSubject<string> = new BehaviorSubject<string>('');

  constructor(private store: Store,
              private pentestService: PentestService,
              private notificationService: NotificationService,
              private readonly router: Router) {
  }

  ngOnInit(): void {
    this.store.selectOnce(ProjectState.project).pipe(
      untilDestroyed(this)
    ).subscribe({
      next: (selectedProject: Project) => {
        this.selectedProjectId$.next(selectedProject.id);
        this.selectedProjectTitle$.next(selectedProject?.title);
      },
      error: err => {
        console.error(err);
      }
    });

    this.store.select(ProjectState.pentest).pipe(
      untilDestroyed(this)
    ).subscribe({
      next: (selectedPentest: Pentest) => {
        this.currentStatus = selectedPentest.status;
        this.currentTimeSpent = selectedPentest.timeSpent ? selectedPentest.timeSpent : 0;
        this.pentest$.next(selectedPentest);
      },
      error: err => {
        console.error(err);
      }
    });
    // Setup initial values for status and time outside of store subscription
    this.initialPentestStatus = this.currentStatus;
    this.initialTimeSpent = this.currentTimeSpent;
  }

  onClickRouteBack(): void {
    // Route back to overview
    this.router.navigate([Route.OBJECTIVE_OVERVIEW])
      .then(
        () => {
          this.store.dispatch(new ChangePentest(null));
        }
      ).finally();
  }

  onClickCompletePentest(): void {
    // Update existing Pentest
    this.pentest$.next({...this.pentest$.getValue(), status: PentestStatus.COMPLETED, timeSpent: this.currentTimeSpent});
    this.updatePentest();
  }

  private updatePentest(): void {
    this.pentestService.updatePentest(transformPentestToRequestBody(this.pentest$.getValue()))
      .subscribe({
        next: (pentest: Pentest) => {
          this.store.dispatch(new ChangePentest(pentest));
          this.initialTimeSpent = pentest.timeSpent;
          this.notificationService.showPopup('pentest.popup.update.success', PopupType.SUCCESS);
        },
        error: err => {
          console.log(err);
          this.notificationService.showPopup('pentest.popup.update.failed', PopupType.FAILURE);
        }
      });
  }

  /**
   * @return true if initial pentest Status is different from current pentest status
   */
  pentestStatusChanged(): boolean {
    if (this.initialTimeSpent !== this.currentTimeSpent && this.currentTimeSpent !== 0) {
      this.pentestChanged$.next(true);
    } else {
      this.pentestChanged$.next(false);
    }
    return this.pentestChanged$.getValue();
  }

  /**
   * @return true if pentest includes at least one finding or comment
   */
  pentestHasFindingsOrComments(): boolean {
    const pentest: Pentest = this.pentest$.getValue();
    // Check if pentest includes any findings or comments
    return pentest?.findingIds?.length > 0 || pentest?.commentIds?.length > 0;
  }

  /**
   * @return the correct nb-status for current pentest-status
   */
  getPentestFillStatus(value: PentestStatus): string {
    let pentestFillStatus;
    switch (value) {
      case PentestStatus.NOT_STARTED: {
        pentestFillStatus = 'basic';
        break;
      }
      case PentestStatus.PAUSED: {
        pentestFillStatus = 'info';
        break;
      }
      case PentestStatus.IN_PROGRESS: {
        pentestFillStatus = 'warning';
        break;
      }
      case PentestStatus.COMPLETED: {
        pentestFillStatus = 'success';
        break;
      }
      default: {
        pentestFillStatus = 'basic';
        break;
      }
    }
    return pentestFillStatus;
  }

  ngOnDestroy(): void {
    if (this.pentestStatusChanged()) {
      // Save current Pentest before exiting
      this.pentest$.next({...this.pentest$.getValue(), status: PentestStatus.PAUSED, timeSpent: this.currentTimeSpent});
      this.updatePentest();
    }
  }
}
