import { Injectable } from '@angular/core';
import {
  ActivatedRouteSnapshot,
  Router,
  RouterStateSnapshot,
} from '@angular/router';
import { ApplicationProcess, ApplicationService } from '@core/application';
import { DomainCheckoutService } from '@domain/checkout';
import { logger } from '@isa/core/logging';
import { CustomerSearchNavigation } from '@shared/services/navigation';
import { first } from 'rxjs/operators';

@Injectable({ providedIn: 'root' })
export class CanActivateCustomerGuard {
  #logger = logger(() => ({
    module: 'isa-app',
    importMetaUrl: import.meta.url,
    class: 'CanActivateCustomerGuard',
  }));

  constructor(
    private readonly _applicationService: ApplicationService,
    private readonly _checkoutService: DomainCheckoutService,
    private readonly _router: Router,
    private readonly _navigation: CustomerSearchNavigation,
  ) {}

  async canActivate(
    route: ActivatedRouteSnapshot,
    { url }: RouterStateSnapshot,
  ) {
    if (url.startsWith('/kunde/customer/search/')) {
      const processId = Date.now(); // Generate a new process ID
      // Extract parts before and after the pattern
      const parts = url.split('/kunde/customer/');
      if (parts.length === 2) {
        const prefix = parts[0] + '/kunde/';
        const suffix = 'customer/' + parts[1];

        // Construct the new URL with process ID inserted
        const newUrl = `${prefix}${processId}/${suffix}`;

        this.#logger.info('Redirecting to URL with process ID', () => ({
          originalUrl: url,
          newUrl,
          processId,
        }));

        // Navigate to the new URL and prevent original navigation
        this._router.navigateByUrl(newUrl);
        return false;
      }
    }

    const processes = await this._applicationService
      .getProcesses$('customer')
      .pipe(first())
      .toPromise();
    const lastActivatedProcessId = (
      await this._applicationService
        .getLastActivatedProcessWithSectionAndType$('customer', 'cart')
        .pipe(first())
        .toPromise()
    )?.id;

    const lastActivatedCartCheckoutProcessId = (
      await this._applicationService
        .getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout')
        .pipe(first())
        .toPromise()
    )?.id;

    const lastActivatedGoodsOutProcessId = (
      await this._applicationService
        .getLastActivatedProcessWithSectionAndType$('customer', 'goods-out')
        .pipe(first())
        .toPromise()
    )?.id;

    const activatedProcessId = await this._applicationService
      .getActivatedProcessId$()
      .pipe(first())
      .toPromise();

    // Darf nur reinkommen wenn der aktuell aktive Tab ein Bestellabschluss Tab ist
    if (
      !!lastActivatedCartCheckoutProcessId &&
      lastActivatedCartCheckoutProcessId === activatedProcessId
    ) {
      await this.fromCartCheckoutProcess(
        processes,
        lastActivatedCartCheckoutProcessId,
      );
      return false;
    } else if (
      !!lastActivatedGoodsOutProcessId &&
      lastActivatedGoodsOutProcessId === activatedProcessId
    ) {
      await this.fromGoodsOutProcess(processes, lastActivatedGoodsOutProcessId);
      return false;
    }

    if (!lastActivatedProcessId) {
      await this.fromCartProcess(processes);
      return false;
    } else {
      await this.navigateToDefaultRoute(lastActivatedProcessId);
    }
    return false;
  }

  async navigateToDefaultRoute(processId: number) {
    const route = this._navigation.defaultRoute({ processId });

    await this._router.navigate(route.path, { queryParams: route.queryParams });
  }

  // Bei offener Artikelsuche/Kundensuche und Klick auf Footer Kundensuche
  async fromCartProcess(processes: ApplicationProcess[]) {
    const newProcessId = Date.now();
    await this._applicationService.createProcess({
      id: newProcessId,
      type: 'cart',
      section: 'customer',
      name: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`,
    });

    await this.navigateToDefaultRoute(newProcessId);
  }

  // Bei offener Bestellbestätigung und Klick auf Footer Kundensuche
  async fromCartCheckoutProcess(
    processes: ApplicationProcess[],
    processId: number,
  ) {
    // Um alle Checkout Daten zu resetten die mit dem Prozess assoziiert sind
    this._checkoutService.removeProcess({ processId });

    // Ändere type cart-checkout zu cart
    this._applicationService.patchProcess(processId, {
      id: processId,
      type: 'cart',
      section: 'customer',
      name: `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`,
      data: {},
    });

    // Navigation
    await this.navigateToDefaultRoute(processId);
  }

  // Bei offener Warenausgabe und Klick auf Footer Kundensuche
  async fromGoodsOutProcess(
    processes: ApplicationProcess[],
    processId: number,
  ) {
    const buyer = await this._checkoutService
      .getBuyer({ processId })
      .pipe(first())
      .toPromise();
    const customerFeatures = await this._checkoutService
      .getCustomerFeatures({ processId })
      .pipe(first())
      .toPromise();
    const name = buyer
      ? customerFeatures?.b2b
        ? buyer.organisation?.name
          ? buyer.organisation?.name
          : buyer.lastName
        : buyer.lastName
      : `Vorgang ${this.processNumber(processes.filter((process) => process.type === 'cart'))}`;

    // Ändere type goods-out zu cart
    this._applicationService.patchProcess(processId, {
      id: processId,
      type: 'cart',
      section: 'customer',
      name,
    });

    // Navigation
    await this.navigateToDefaultRoute(processId);
  }

  processNumber(processes: ApplicationProcess[]) {
    const processNumbers = processes?.map((process) =>
      Number(process?.name?.replace(/\D/g, '')),
    );
    return !!processNumbers && processNumbers.length > 0
      ? this.findMissingNumber(processNumbers)
      : 1;
  }

  findMissingNumber(processNumbers: number[]) {
    for (
      let missingNumber = 1;
      missingNumber < Math.max(...processNumbers);
      missingNumber++
    ) {
      if (!processNumbers.find((number) => number === missingNumber)) {
        return missingNumber;
      }
    }
    return Math.max(...processNumbers) + 1;
  }
}
