import { Injectable, inject } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { filter } from 'rxjs/operators';
import { TabService } from './tab';
import { TabLocation } from './schemas';
import { Title } from '@angular/platform-browser';
import { HISTORY_BLACKLIST_URLS } from './tab-navigation.constants';

/**
 * Service that automatically syncs browser navigation events to tab location history.
 *
 * This service listens to Angular Router NavigationEnd events and updates the active tab's
 * location history with the current URL, page title, and timestamp. It handles both
 * tab-specific routes (:tabId) and legacy process routes (:processId).
 *
 * Key features:
 * - Automatic browser navigation synchronization
 * - URL blacklist to exclude specific routes from history
 * - History pruning awareness with fallback navigation
 * - Back/forward navigation with step-by-step movement
 * - Prevention of infinite loops during navigation
 * - Legacy route support for backward compatibility
 *
 * The service is designed to work seamlessly with the tab history pruning system,
 * providing fallback mechanisms when navigation history has been pruned.
 */
@Injectable({ providedIn: 'root' })
export class TabNavigationService {
  #router = inject(Router);
  #tabService = inject(TabService);
  #title = inject(Title);

  init() {
    this.#router.events
      .pipe(filter((event) => event instanceof NavigationEnd))
      .subscribe((event: NavigationEnd) => {
        this.#syncNavigationToTab(event);
      });
  }

  #syncNavigationToTab(event: NavigationEnd) {
    // Skip blacklisted URLs
    if (this.#shouldSkipHistory(event.url)) {
      return;
    }

    const activeTabId = this.#getActiveTabId(event.url);
    if (!activeTabId) {
      return;
    }
    const location = this.#createTabLocation(event.url);

    // Check if this location already exists in history (browser back/forward)
    const currentTab = this.#tabService.entityMap()[activeTabId];
    if (
      currentTab &&
      this.#isLocationInHistory(currentTab.location.locations, location)
    ) {
      this.#handleBrowserNavigation(activeTabId, location);
    } else {
      this.#tabService.navigateToLocation(activeTabId, location);
    }
  }

  /**
   * Checks if a URL should be excluded from tab navigation history.
   *
   * @param url - The URL to check against the blacklist
   * @returns true if the URL should be skipped, false otherwise
   */
  #shouldSkipHistory(url: string): boolean {
    return HISTORY_BLACKLIST_URLS.includes(url);
  }

  #getActiveTabId(url: string): number | null {
    // Extract tabId from URL pattern /:tabId/...
    const tabIdMatch = url.match(/^\/(\d+)(?:\/|$)/);
    if (tabIdMatch) {
      return parseInt(tabIdMatch[1], 10);
    }

    // Extract processId from legacy URL pattern /kunde/:processId/...
    const processIdMatch = url.match(/^\/kunde\/(\d+)(?:\/|$)/);
    if (processIdMatch) {
      const processId = parseInt(processIdMatch[1], 10);
      // In legacy routes, processId maps to tabId
      return processId;
    }

    // If no ID in URL, use currently activated tab
    return this.#tabService.activatedTabId();
  }

  #createTabLocation(url: string): TabLocation {
    return {
      timestamp: Date.now(),
      title: this.#getPageTitle(),
      url: url,
    };
  }

  #getPageTitle(): string {
    return this.#title.getTitle();
  }

  #isLocationInHistory(
    locations: TabLocation[],
    location: TabLocation,
  ): boolean {
    return locations.some((loc) => loc.url === location.url);
  }

  /**
   * Handles browser navigation events with pruning-aware fallback logic.
   *
   * This method attempts step-by-step navigation (back/forward) to reach the target
   * location. If step-by-step navigation fails due to pruned history or other issues,
   * it falls back to direct navigation to ensure the user reaches their intended destination.
   *
   * Features:
   * - Step-by-step back/forward navigation
   * - Loop prevention with maximum attempt limits
   * - Fallback to direct navigation when step-by-step fails
   * - Handles pruned history gracefully
   *
   * @param tabId - ID of the tab to navigate
   * @param location - Target location to navigate to
   * @private
   */
  #handleBrowserNavigation(tabId: number, location: TabLocation) {
    const currentTab = this.#tabService.entityMap()[tabId];
    if (!currentTab) return;

    const locationIndex = currentTab.location.locations.findIndex(
      (loc) => loc.url === location.url,
    );

    // If location not found in history (possibly pruned), navigate to new location
    if (locationIndex === -1) {
      this.#tabService.navigateToLocation(tabId, location);
      return;
    }

    // Determine if we need to go back or forward
    const currentIndex = currentTab.location.current;

    if (locationIndex < currentIndex) {
      // Navigate back
      let steps = currentIndex - locationIndex;
      let attempts = 0;
      const maxAttempts = Math.abs(steps) + 5; // Prevent infinite loops

      while (steps > 0 && attempts < maxAttempts) {
        const result = this.#tabService.navigateBack(tabId);
        if (!result) break;
        steps--;
        attempts++;
      }

      // If we couldn't reach the target, fallback to direct navigation
      if (steps > 0) {
        this.#tabService.navigateToLocation(tabId, location);
      }
    } else if (locationIndex > currentIndex) {
      // Navigate forward
      let steps = locationIndex - currentIndex;
      let attempts = 0;
      const maxAttempts = Math.abs(steps) + 5; // Prevent infinite loops

      while (steps > 0 && attempts < maxAttempts) {
        const result = this.#tabService.navigateForward(tabId);
        if (!result) break;
        steps--;
        attempts++;
      }

      // If we couldn't reach the target, fallback to direct navigation
      if (steps > 0) {
        this.#tabService.navigateToLocation(tabId, location);
      }
    }
    // If locationIndex === currentIndex, we're already at the right position
  }

  /**
   * Manually syncs the current route to the active tab's location history.
   *
   * This method is useful for:
   * - Initial page loads when navigation service starts
   * - When new tabs are created and need current location
   * - Manual synchronization after route changes outside normal navigation
   * - Recovery scenarios where tab history needs refreshing
   *
   * The method extracts the current URL and creates a location entry with
   * the appropriate tab ID, current page title, and timestamp.
   *
   * @example
   * ```typescript
   * // Sync current route after creating a new tab
   * const tab = tabService.addTab({ name: 'New Tab' });
   * tabNavigationService.syncCurrentRoute();
   * ```
   */
  syncCurrentRoute() {
    const url = this.#router.url;
    const activeTabId = this.#getActiveTabId(url);

    if (activeTabId) {
      const location = this.#createTabLocation(url);
      this.#tabService.navigateToLocation(activeTabId, location);
    }
  }
}
