import { inject, Injectable, signal } from '@angular/core';
import { PROJECT_REPOSITORY_TOKEN } from '@app/infrastructure/projects/project-repository.token';
import { CreateProjectUseCase } from '../../application/projects/create-project.usecase';
import { ListProjectUseCase } from '../../application/projects/list-project.usecase';
import { GetProjectUseCase } from '../../application/projects/get-project.usecase';
import { UpdateProjectUseCase } from '../../application/projects/update-project.usecase';
import { Project } from '@app/domain/projects/project.model';
import { ProjectViewModel } from '../projects/project.presenter.model';
import { ProjectPresenter } from '../projects/project.presenter';
import { CreateProjectDto } from '@app/domain/projects/dto/create-project.dto';
import { ErrorResponse } from '@app/domain/error-response.util';
import { ActionType } from '@app/domain/action-type.util';
import { LoaderAction } from '@app/domain/loader-action.util';
import { first, Subscription } from 'rxjs';
import { FeedbackService } from '../shared/services/feedback.service';

@Injectable({
  providedIn: 'root',
})
export class ProjectFacade {
  private readonly feedbackService = inject(FeedbackService);
  private readonly projectRepo = inject(PROJECT_REPOSITORY_TOKEN);

  private readonly createUseCase = new CreateProjectUseCase(this.projectRepo);
  private readonly listUseCase = new ListProjectUseCase(this.projectRepo);
  private readonly getUseCase = new GetProjectUseCase(this.projectRepo);
  private readonly UpdateUseCase = new UpdateProjectUseCase(this.projectRepo);

  readonly projects = signal<ProjectViewModel[]>([]);
  readonly project = signal<ProjectViewModel>({} as ProjectViewModel);
  readonly loading = signal<LoaderAction>({ isLoading: false, action: ActionType.NONE });
  readonly error = signal<ErrorResponse>({
    action: ActionType.NONE,
    hasError: false,
    message: null,
  });

  private readonly projectPresenter = new ProjectPresenter();
  private projectSubscription: Subscription | null = null;

  load(userId: string) {
    if (this.projectSubscription) {
      this.projectSubscription.unsubscribe();
      this.projectSubscription = null;
    }

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

    this.projectSubscription = this.listUseCase.execute(userId).subscribe({
      next: (projects: Project[]) => {
        this.projects.set(this.projectPresenter.toViewModels(projects));
        this.handleError(ActionType.READ, false, null, false);

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

  loadOne(projectId: string) {
    this.handleError(ActionType.READ, false, null, true);

    this.getUseCase
      .execute(projectId)
      .pipe(first())
      .subscribe({
        next: (project: Project) => {
          this.project.set(this.projectPresenter.toViewModel(project));
          this.handleError(ActionType.READ, false, null, false);
        },
        error: (err) => {
          this.handleError(ActionType.READ, false, err, false);
        },
      });
  }

  create(projectDto: CreateProjectDto) {
    this.handleError(ActionType.CREATE, false, null, true);

    this.createUseCase
      .execute(projectDto)
      .pipe(first())
      .subscribe({
        next: (project: Project) => {
          this.project.set(this.projectPresenter.toViewModel(project));
          this.projects.update((prev) => [...prev, this.projectPresenter.toViewModel(project)]);
          this.handleError(ActionType.CREATE, false, null, false);

          const message = `Le projet ${project.nom} a bien été créer !`;
          this.feedbackService.notify(ActionType.UPDATE, message);
        },
        error: (err) => {
          this.handleError(ActionType.CREATE, false, err, false);
        },
      });
  }

  update(userId: string, data: any) {
    this.handleError(ActionType.UPDATE, false, null, true);

    this.UpdateUseCase.execute(userId, data)
      .pipe(first())
      .subscribe({
        next: (project: Project) => {
          this.project.set(this.projectPresenter.toViewModel(project));
          this.handleError(ActionType.UPDATE, false, null, false);

          const message = `Les informations du projet ${project.nom} ont bien été modifier !`;
          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
  ) {
    this.error.set({ action, hasError, message });
    this.loading.set({ action, isLoading });

    if (hasError) {
      this.feedbackService.notify(ActionType.READ, message!, true);
    }
  }
}
