import { ListProfilesUseCase } from '../../application/profiles/list-profiles.usecase';
import { inject, Injectable, signal } from '@angular/core';
import { PROFILE_REPOSITORY_TOKEN } from '@app/infrastructure/profiles/profile-repository.token';
import { Profile, ProfilePaginated } from '@app/domain/profiles/profile.model';
import { ProfilePresenter } from '../profiles/profile.presenter';
import { ProfileViewModel, ProfileViewModelPaginated } from '../profiles/profile.presenter.model';
import { LoaderAction } from '@app/domain/loader-action.util';
import { ActionType } from '@app/domain/action-type.util';
import { ErrorResponse } from '@app/domain/error-response.util';
import { CreateProfileUseCase } from '../../application/profiles/create-profile.usecase';
import { UpdateProfileUseCase } from '../../application/profiles/update-profile.usecase';
import { GetProfileUseCase } from '../../application/profiles/get-profile.usecase';
import { ProfileDTO } from '@app/domain/profiles/dto/create-profile.dto';
import { SearchFilters } from '@app/domain/search/search-filters';
import { SearchService } from '@app/infrastructure/search/search.service';
import { UpdateCoordinateProfileUseCase } from '../../application/profiles/update-coordinate-profile.usecase';
import { SettingsProfileDto } from '@app/domain/profiles/dto/settings-profile.dto';
import { UpdateSettingsProfileUseCase } from '../../application/profiles/update-settings-profile.usecase';
import { FeedbackService } from '../shared/services/feedback.service';
import { first, Subscription } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class ProfileFacade {
  private readonly feedbackService = inject(FeedbackService);
  private readonly profileRepository = inject(PROFILE_REPOSITORY_TOKEN);
  private readonly searchService = inject(SearchService);
  private readonly profilePresenter = new ProfilePresenter();

  private listUseCase = new ListProfilesUseCase(this.profileRepository);
  private createUseCase = new CreateProfileUseCase(this.profileRepository);
  private updateUseCase = new UpdateProfileUseCase(this.profileRepository);
  private updateCoordinateUseCase = new UpdateCoordinateProfileUseCase(this.profileRepository);
  private updateSettingsUseCase = new UpdateSettingsProfileUseCase(this.profileRepository);
  private getUseCase = new GetProfileUseCase(this.profileRepository);

  readonly searchFilters = this.searchService.getFilters();
  readonly profiles = signal<ProfileViewModel[]>([]);
  readonly profilePaginated = signal<ProfileViewModelPaginated>({} as ProfileViewModelPaginated);
  readonly profile = signal<ProfileViewModel>({} as ProfileViewModel);
  readonly loading = signal<LoaderAction>({
    isLoading: false,
    action: ActionType.NONE,
    isDone: false,
  });
  readonly error = signal<ErrorResponse>({
    action: ActionType.NONE,
    hasError: false,
    message: null,
  });

  private searchSubscription: Subscription | null = null;

  load(search?: SearchFilters) {
    if (this.searchSubscription) {
      this.searchSubscription.unsubscribe();
      this.searchSubscription = null;
    }

    this.handleError(ActionType.READ, false, null, true);

    if (search === undefined || search === null) {
      search = this.searchFilters();
    }

    this.searchSubscription = this.listUseCase.execute(search).subscribe({
      next: (profilePaginated: ProfilePaginated) => {
        const filters = {
          ...this.searchFilters(),
          page: profilePaginated.page,
          perPage: profilePaginated.perPage,
          totalItems: profilePaginated.totalItems,
          totalPages: profilePaginated.totalPages,
        };

        this.searchService.setFilters(filters);
        this.searchFilters.set(filters);

        const profileViewModelPaginated =
          this.profilePresenter.toViewModelPaginated(profilePaginated);

        this.profilePaginated.set(profileViewModelPaginated);
        this.profiles.set(profileViewModelPaginated.items);
        this.handleError(ActionType.READ, false, null, false);

        this.searchSubscription = null;
      },
      error: (err) => {
        this.handleError(ActionType.READ, false, err, false);
        this.searchSubscription = null;
      },
    });
  }

  loadOne(profileId: string) {
    this.handleError(ActionType.READ, false, null, true);
    this.getUseCase
      .execute(profileId)
      .pipe(first())
      .subscribe({
        next: (profile: Profile) => {
          this.profile.set(this.profilePresenter.toViewModel(profile));
          this.handleError(ActionType.READ, false, null, false);
        },
        error: (err) => {
          this.handleError(ActionType.READ, false, err, false);
        },
      });
  }

  loadOneByUserId(userId: string) {
    this.handleError(ActionType.READ, false, null, true);
    this.getUseCase
      .executeByUserId(userId)
      .pipe(first())
      .subscribe({
        next: (profile: Profile) => {
          this.profile.set(this.profilePresenter.toViewModel(profile));
          this.handleError(ActionType.READ, false, null, false);
        },
        error: (err) => {
          this.handleError(ActionType.READ, false, err, false);
        },
      });
  }

  create(profileDto: ProfileDTO) {
    this.handleError(ActionType.CREATE, false, null, true);
    let message = null;

    this.createUseCase
      .execute(profileDto)
      .pipe(first())
      .subscribe({
        next: (profile: Profile) => {
          this.profile.set(this.profilePresenter.toViewModel(profile));
          this.handleError(ActionType.CREATE, false, null, false);
        },
        error: (err) => {
          this.handleError(ActionType.CREATE, false, err, false);
        },
      });
  }

  update(profileId: string, profile: Partial<Profile>) {
    this.handleError(ActionType.UPDATE, false, null, true);

    this.updateUseCase
      .execute(profileId, profile)
      .pipe(first())
      .subscribe({
        next: (profile: Profile) => {
          this.profile.set(this.profilePresenter.toViewModel(profile));
          this.handleError(ActionType.UPDATE, false, null, false);

          const message = `Vos informations personnelles ont bien été modifier !`;
          this.feedbackService.notify(ActionType.UPDATE, message);
        },
        error: (err) => {
          this.handleError(ActionType.UPDATE, false, err, false);
        },
      });
  }

  updateCoordinate(profileId: string, latitude: number, longitude: number) {
    this.handleError(ActionType.UPDATE, false, null, true);

    this.updateCoordinateUseCase
      .execute(profileId, latitude, longitude)
      .pipe(first())
      .subscribe({
        next: (profile: Profile) => {
          this.profile.set(this.profilePresenter.toViewModel(profile));
          this.handleError(ActionType.UPDATE, false, null, false);

          const message = `Vos coordonnées géographique ont été enregistrés.`;
          this.feedbackService.notify(ActionType.UPDATE, message);
        },
        error: (err) => {
          this.handleError(ActionType.UPDATE, false, err, false);
        },
      });
  }

  updateSettings(profileId: string, settings: SettingsProfileDto) {
    this.handleError(ActionType.UPDATE, false, null, true);

    this.updateSettingsUseCase
      .execute(profileId, settings)
      .pipe(first())
      .subscribe({
        next: (profile: Profile) => {
          this.profile.set(this.profilePresenter.toViewModel(profile));
          this.handleError(ActionType.UPDATE, false, null, false, true);

          const message = `Vos paramètres ont été enregistrés.`;
          this.feedbackService.notify(ActionType.UPDATE, message);
        },
        error: (err) => {
          this.handleError(ActionType.UPDATE, false, err, false);
        },
      });
  }

  private handleError(
    action: ActionType = ActionType.NONE,
    hasError: boolean,
    message: string | null = null,
    isLoading = false,
    isDone = false
  ) {
    this.error.set({ action, hasError, message });
    this.loading.set({ action, isLoading, isDone });
    if (hasError) {
      this.feedbackService.notify(ActionType.READ, message!, true);
    }
  }
}
