import { Injectable, inject } from '@angular/core';
import { Router, NavigationExtras } from '@angular/router';
import { NavigationContextService } from './navigation-context.service';
import { NavigationContextData } from './navigation-context.types';

/**
 * Service for managing navigation context preservation across multi-step flows.
 *
 * This service provides automatic context preservation using tab metadata,
 * allowing navigation state to survive intermediate navigations. Contexts are
 * automatically scoped to the active tab and cleaned up when the tab closes.
 *
 * ## Context Preservation for Multi-Step Flows
 *
 * @example
 * ```typescript
 * // Start of flow - preserve context (automatically scoped to active tab)
 * await navigationStateService.preserveContext({
 *   returnUrl: '/reward/cart',
 *   customerId: 123
 * });
 *
 * // ... multiple intermediate navigations happen ...
 * await router.navigate(['/customer/details']);
 * await router.navigate(['/add-shipping-address']);
 *
 * // End of flow - restore and cleanup (auto-scoped to active tab)
 * const context = await navigationStateService.restoreAndClearContext<{ returnUrl: string }>();
 * if (context?.returnUrl) {
 *   await router.navigateByUrl(context.returnUrl);
 * }
 * ```
 *
 * ## Simplified Navigation with Context
 *
 * @example
 * ```typescript
 * // Navigate and preserve context in one call
 * const { success } = await navigationStateService.navigateWithPreservedContext(
 *   ['/customer/search'],
 *   { returnUrl: '/reward/cart' }
 * );
 *
 * // Later, restore and navigate back
 * const context = await navigationStateService.restoreAndClearContext<{ returnUrl: string }>();
 * if (context?.returnUrl) {
 *   await router.navigateByUrl(context.returnUrl);
 * }
 * ```
 *
 * ## Automatic Tab Cleanup
 *
 * @example
 * ```typescript
 * ngOnDestroy() {
 *   // Clean up all contexts when tab closes (auto-uses active tab ID)
 *   this.navigationStateService.clearScopeContexts();
 * }
 * ```
 *
 * Key Features:
 * - ✅ Automatic tab scoping using TabService
 * - ✅ Stored in tab metadata (automatic cleanup when tab closes)
 * - ✅ Type-safe with TypeScript generics and Zod validation
 * - ✅ Automatic cleanup with restoreAndClearContext()
 * - ✅ Support for multiple custom scopes per tab
 * - ✅ No manual expiration management needed
 * - ✅ Platform-agnostic (works with SSR)
 */
@Injectable({ providedIn: 'root' })
export class NavigationStateService {
  readonly #router = inject(Router);
  readonly #contextService = inject(NavigationContextService);

  // Context Preservation Methods

  /**
   * Preserve navigation state for multi-step flows.
   *
   * This method stores navigation context in tab metadata, allowing it to
   * persist across intermediate navigations within a flow. Contexts are automatically
   * scoped to the active tab, with optional custom scope for different flows.
   *
   * Use this when starting a flow that will have intermediate navigations
   * before returning to the original location.
   *
   * @template T The type of state data being preserved
   * @param state The navigation state to preserve
   * @param customScope Optional custom scope within the tab (e.g., 'customer-details')
   *
   * @example
   * ```typescript
   * // Preserve context for default tab scope
   * await navigationStateService.preserveContext({ returnUrl: '/products' });
   *
   * // Preserve context for custom scope within tab
   * await navigationStateService.preserveContext({ customerId: 42 }, 'customer-details');
   *
   * // ... multiple intermediate navigations ...
   *
   * // Restore at end of flow
   * const context = await navigationStateService.restoreContext<{ returnUrl: string }>();
   * if (context?.returnUrl) {
   *   await router.navigateByUrl(context.returnUrl);
   * }
   * ```
   */
  async preserveContext<T extends NavigationContextData>(
    state: T,
    customScope?: string,
  ): Promise<void> {
    await this.#contextService.setContext(state, customScope);
  }

  /**
   * Patch preserved navigation state.
   *
   * Merges partial state with existing preserved context, keeping unspecified properties intact.
   * This is useful when you need to update or clear specific properties without replacing
   * the entire context. Properties set to `undefined` will be removed.
   *
   * Use cases:
   * - Clear a trigger flag while preserving return URL
   * - Update one property in a multi-property context
   * - Remove specific properties from context
   *
   * @template T The type of state data being patched
   * @param partialState The partial state to merge (properties set to undefined will be removed)
   * @param customScope Optional custom scope within the tab
   *
   * @example
   * ```typescript
   * // Clear the autoTriggerContinueFn flag while preserving returnUrl
   * await navigationStateService.patchContext(
   *   { autoTriggerContinueFn: undefined },
   *   'select-customer'
   * );
   *
   * // Update selectedTab while keeping other properties
   * await navigationStateService.patchContext(
   *   { selectedTab: 'rewards' },
   *   'customer-flow'
   * );
   *
   * // Add a new property to existing context
   * await navigationStateService.patchContext(
   *   { shippingAddressId: 123 },
   *   'checkout-flow'
   * );
   * ```
   */
  async patchContext<T extends NavigationContextData>(
    partialState: Partial<T>,
    customScope?: string,
  ): Promise<void> {
    await this.#contextService.patchContext(partialState, customScope);
  }

  /**
   * Restore preserved navigation state.
   *
   * Retrieves a previously preserved navigation context for the active tab scope,
   * or a custom scope if specified.
   *
   * This method does NOT remove the context - use clearPreservedContext() or
   * restoreAndClearContext() for automatic cleanup.
   *
   * @template T The expected type of the preserved state
   * @param customScope Optional custom scope (defaults to active tab scope)
   * @returns The preserved state, or null if not found
   *
   * @example
   * ```typescript
   * // Restore from default tab scope
   * const context = navigationStateService.restoreContext<{ returnUrl: string }>();
   * if (context?.returnUrl) {
   *   console.log('Returning to:', context.returnUrl);
   * }
   *
   * // Restore from custom scope
   * const context = navigationStateService.restoreContext<{ customerId: number }>('customer-details');
   * ```
   */
  async restoreContext<T extends NavigationContextData = NavigationContextData>(
    customScope?: string,
  ): Promise<T | null> {
    return await this.#contextService.getContext<T>(customScope);
  }

  /**
   * Restore and automatically clear preserved navigation state.
   *
   * Retrieves a preserved navigation context and removes it from tab metadata in one operation.
   * Use this when completing a flow to clean up automatically.
   *
   * @template T The expected type of the preserved state
   * @param customScope Optional custom scope (defaults to active tab scope)
   * @returns The preserved state, or null if not found
   *
   * @example
   * ```typescript
   * // Restore and clear from default tab scope
   * const context = await navigationStateService.restoreAndClearContext<{ returnUrl: string }>();
   * if (context?.returnUrl) {
   *   await router.navigateByUrl(context.returnUrl);
   * }
   *
   * // Restore and clear from custom scope
   * const context = await navigationStateService.restoreAndClearContext<{ customerId: number }>('customer-details');
   * ```
   */
  async restoreAndClearContext<
    T extends NavigationContextData = NavigationContextData,
  >(customScope?: string): Promise<T | null> {
    return await this.#contextService.getAndClearContext<T>(customScope);
  }

  /**
   * Clear a preserved navigation context.
   *
   * Removes a context from tab metadata without returning its data.
   * Use this for explicit cleanup when you no longer need the preserved state.
   *
   * @param customScope Optional custom scope (defaults to active tab scope)
   * @returns true if context was found and cleared, false otherwise
   *
   * @example
   * ```typescript
   * // Clear default tab scope context
   * await navigationStateService.clearPreservedContext();
   *
   * // Clear custom scope context
   * await navigationStateService.clearPreservedContext('customer-details');
   * ```
   */
  async clearPreservedContext(customScope?: string): Promise<boolean> {
    return await this.#contextService.clearContext(customScope);
  }

  /**
   * Check if a preserved context exists.
   *
   * @param customScope Optional custom scope (defaults to active tab scope)
   * @returns true if context exists, false otherwise
   *
   * @example
   * ```typescript
   * // Check default tab scope
   * if (navigationStateService.hasPreservedContext()) {
   *   const context = navigationStateService.restoreContext();
   * }
   *
   * // Check custom scope
   * if (navigationStateService.hasPreservedContext('customer-details')) {
   *   const context = navigationStateService.restoreContext('customer-details');
   * }
   * ```
   */
  async hasPreservedContext(customScope?: string): Promise<boolean> {
    return await this.#contextService.hasContext(customScope);
  }

  /**
   * Navigate while preserving context state.
   *
   * Convenience method that combines navigation with context preservation.
   * The context will be stored in tab metadata and available throughout the
   * navigation flow and any intermediate navigations. Context is automatically
   * scoped to the active tab.
   *
   * @param commands Navigation commands (same as Router.navigate)
   * @param state The state to preserve
   * @param customScope Optional custom scope within the tab
   * @param extras Optional navigation extras
   * @returns Promise resolving to navigation success status
   *
   * @example
   * ```typescript
   * // Navigate and preserve context
   * const { success } = await navigationStateService.navigateWithPreservedContext(
   *   ['/customer/search'],
   *   { returnUrl: '/reward/cart', customerId: 123 }
   * );
   *
   * // Later, retrieve and navigate back
   * const context = await navigationStateService.restoreAndClearContext<{ returnUrl: string }>();
   * if (context?.returnUrl) {
   *   await router.navigateByUrl(context.returnUrl);
   * }
   * ```
   */
  async navigateWithPreservedContext<T extends NavigationContextData>(
    commands: unknown[],
    state: T,
    customScope?: string,
    extras?: NavigationExtras,
  ): Promise<{ success: boolean }> {
    await this.preserveContext(state, customScope);

    // Also pass state via router for immediate access
    const navigationExtras: NavigationExtras = {
      ...extras,
      state,
    };

    const success = await this.#router.navigate(commands, navigationExtras);

    return { success };
  }

  /**
   * Clear all preserved contexts for the active tab.
   *
   * Removes all contexts for the active tab (both default and custom scopes).
   * Useful for cleanup when a tab is closed.
   *
   * @returns The number of contexts cleared
   *
   * @example
   * ```typescript
   * // Clear all contexts for active tab
   * ngOnDestroy() {
   *   const cleared = this.navigationStateService.clearScopeContexts();
   *   console.log(`Cleared ${cleared} contexts`);
   * }
   * ```
   */
  async clearScopeContexts(): Promise<number> {
    return await this.#contextService.clearScope();
  }
}
