/**
 * DeepLinks Service
 *
 * Handles deep link processing and routing for the TimeSafari application.
 * Supports both path parameters and query parameters with comprehensive validation.
 *
 * @author Matthew Raymer
 * @version 2.0.0
 * @since 2025-01-25
 */

import { Router } from "vue-router";
import { z } from "zod";

import {
  deepLinkPathSchemas,
  routeSchema,
  DeepLinkRoute,
  deepLinkQuerySchemas,
} from "../interfaces/deepLinks";
import type { DeepLinkError } from "../interfaces/deepLinks";
import { logger } from "../utils/logger";

// Helper function to extract the first key from a Zod object schema
function getFirstKeyFromZodObject(
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  schema: z.ZodObject<any>,
): string | undefined {
  const shape = schema.shape;
  const keys = Object.keys(shape);
  return keys.length > 0 ? keys[0] : undefined;
}

/**
 * Maps deep link routes to their corresponding Vue router names and optional parameter keys.
 *
 * It's an object where keys are the deep link routes and values are objects with 'name' and 'paramKey'.
 *
 * The paramKey is used to extract the parameter from the route path,
 * because "router.replace" expects the right parameter name for the route.
 */
export const ROUTE_MAP: Record<string, { name: string; paramKey?: string }> =
  Object.entries(deepLinkPathSchemas).reduce(
    (acc, [routeName, schema]) => {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const paramKey = getFirstKeyFromZodObject(schema as z.ZodObject<any>);
      acc[routeName] = {
        name: routeName,
        paramKey,
      };
      return acc;
    },
    {} as Record<string, { name: string; paramKey?: string }>,
  );

/**
 * Handles processing and routing of deep links in the application.
 * Provides validation, error handling, and routing for deep link URLs.
 */
export class DeepLinkHandler {
  private router: Router;

  /**
   * Creates a new DeepLinkHandler instance.
   * @param router - Vue Router instance for navigation
   */
  constructor(router: Router) {
    this.router = router;
  }

  /**
   * Main entry point for processing deep links
   * @param url - The deep link URL to process
   * @throws {DeepLinkError} If validation fails or route is invalid
   */
  async handleDeepLink(url: string): Promise<void> {
    logger.info(`[DeepLink] 🚀 Starting deeplink processing for URL: ${url}`);

    try {
      logger.info(`[DeepLink] 📍 Parsing URL: ${url}`);
      const { path, params, query } = this.parseDeepLink(url);

      logger.info(`[DeepLink] ✅ URL parsed successfully:`, {
        path,
        params: Object.keys(params),
        query: Object.keys(query),
        fullParams: params,
        fullQuery: query,
      });

      // Sanitize parameters (remove undefined values)
      const sanitizedParams = Object.fromEntries(
        Object.entries(params).map(([key, value]) => [key, value ?? ""]),
      );

      logger.info(`[DeepLink] 🧹 Parameters sanitized:`, sanitizedParams);

      await this.validateAndRoute(path, sanitizedParams, query);
      logger.info(`[DeepLink] 🎯 Deeplink processing completed successfully`);
    } catch (error) {
      logger.error(`[DeepLink] ❌ Deeplink processing failed:`, {
        url,
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined,
      });

      const deepLinkError = error as DeepLinkError;
      throw deepLinkError;
    }
  }

  /**
   * Parse a deep link URL into its components
   * @param url - The deep link URL
   * @returns Parsed components
   */
  private parseDeepLink(url: string): {
    path: string;
    params: Record<string, string>;
    query: Record<string, string>;
  } {
    logger.debug(`[DeepLink] 🔍 Parsing deep link: ${url}`);

    try {
      const parts = url.split("://");
      if (parts.length !== 2) {
        throw new Error("Invalid URL format");
      }

      const [path, queryString] = parts[1].split("?");
      const [routePath, ...pathParams] = path.split("/");

      // Parse path parameters using route-specific configuration
      const params: Record<string, string> = {};
      if (pathParams.length > 0) {
        // Get the correct parameter key for this route
        const routeConfig = ROUTE_MAP[routePath];
        if (routeConfig?.paramKey) {
          params[routeConfig.paramKey] = pathParams[0];
          logger.debug(
            `[DeepLink] 📍 Path parameter extracted: ${routeConfig.paramKey}=${pathParams[0]}`,
          );
        } else {
          // Fallback to 'id' for backward compatibility
          params.id = pathParams[0];
          logger.debug(
            `[DeepLink] 📍 Path parameter extracted: id=${pathParams[0]} (fallback)`,
          );
        }
      }

      // Parse query parameters
      const query: Record<string, string> = {};
      if (queryString) {
        const queryParams = new URLSearchParams(queryString);
        for (const [key, value] of queryParams.entries()) {
          query[key] = value;
        }
        logger.debug(`[DeepLink] 🔗 Query parameters extracted:`, query);
      }

      logger.info(`[DeepLink] ✅ Parse completed:`, {
        routePath,
        pathParams: pathParams.length,
        queryParams: Object.keys(query).length,
      });

      return { path: routePath, params, query };
    } catch (error) {
      logger.error(`[DeepLink] ❌ Parse failed:`, {
        url,
        error: error instanceof Error ? error.message : String(error),
      });
      throw error;
    }
  }

  /**
   * Validate and route the deep link
   * @param path - The route path
   * @param params - Path parameters
   * @param query - Query parameters
   */
  private async validateAndRoute(
    path: string,
    params: Record<string, string>,
    query: Record<string, string>,
  ): Promise<void> {
    logger.info(
      `[DeepLink] 🎯 Starting validation and routing for path: ${path}`,
    );

    // First try to validate the route path
    let routeName: string;

    try {
      logger.debug(`[DeepLink] 🔍 Validating route path: ${path}`);
      // Validate route exists
      const validRoute = routeSchema.parse(path) as DeepLinkRoute;
      logger.info(`[DeepLink] ✅ Route validation passed: ${validRoute}`);

      // Get route configuration
      const routeConfig = ROUTE_MAP[validRoute];
      logger.info(`[DeepLink] 📋 Route config retrieved:`, routeConfig);

      if (!routeConfig) {
        logger.error(`[DeepLink] ❌ No route config found for: ${validRoute}`);
        throw new Error(`Route configuration missing for: ${validRoute}`);
      }

      routeName = routeConfig.name;
      logger.info(`[DeepLink] 🎯 Route name resolved: ${routeName}`);
    } catch (error) {
      logger.error(`[DeepLink] ❌ Route validation failed:`, {
        path,
        error: error instanceof Error ? error.message : String(error),
      });

      // Redirect to error page with information about the invalid link
      await this.router.replace({
        name: "deep-link-error",
        params,
        query: {
          originalPath: path,
          errorCode: "INVALID_ROUTE",
          errorMessage: `The link you followed (${path}) is not supported`,
          ...query,
        },
      });

      logger.info(
        `[DeepLink] 🔄 Redirected to error page for invalid route: ${path}`,
      );
      return;
    }

    // Continue with parameter validation
    logger.info(
      `[DeepLink] 🔍 Starting parameter validation for route: ${routeName}`,
    );

    const pathSchema =
      deepLinkPathSchemas[path as keyof typeof deepLinkPathSchemas];
    const querySchema =
      deepLinkQuerySchemas[path as keyof typeof deepLinkQuerySchemas];

    logger.debug(`[DeepLink] 📋 Schemas found:`, {
      hasPathSchema: !!pathSchema,
      hasQuerySchema: !!querySchema,
      pathSchemaType: pathSchema ? typeof pathSchema : "none",
      querySchemaType: querySchema ? typeof querySchema : "none",
    });

    let validatedPathParams: Record<string, string> = {};
    let validatedQueryParams: Record<string, string> = {};

    try {
      if (pathSchema) {
        logger.debug(`[DeepLink] 🔍 Validating path parameters:`, params);
        validatedPathParams = await pathSchema.parseAsync(params);
        logger.info(
          `[DeepLink] ✅ Path parameters validated:`,
          validatedPathParams,
        );
      } else {
        logger.debug(`[DeepLink] ⚠️ No path schema found for: ${path}`);
        validatedPathParams = params;
      }

      if (querySchema) {
        logger.debug(`[DeepLink] 🔍 Validating query parameters:`, query);
        validatedQueryParams = await querySchema.parseAsync(query);
        logger.info(
          `[DeepLink] ✅ Query parameters validated:`,
          validatedQueryParams,
        );
      } else {
        logger.debug(`[DeepLink] ⚠️ No query schema found for: ${path}`);
        validatedQueryParams = query;
      }
    } catch (error) {
      logger.error(`[DeepLink] ❌ Parameter validation failed:`, {
        routeName,
        path,
        params,
        query,
        error: error instanceof Error ? error.message : String(error),
        errorDetails: JSON.stringify(error),
      });

      await this.router.replace({
        name: "deep-link-error",
        params,
        query: {
          originalPath: path,
          errorCode: "INVALID_PARAMETERS",
          errorMessage: `The link parameters are invalid: ${(error as Error).message}`,
          ...query,
        },
      });

      logger.info(
        `[DeepLink] 🔄 Redirected to error page for invalid parameters`,
      );
      return;
    }

    // Attempt navigation
    try {
      logger.info(`[DeepLink] 🚀 Attempting navigation:`, {
        routeName,
        pathParams: validatedPathParams,
        queryParams: validatedQueryParams,
      });

      await this.router.replace({
        name: routeName,
        params: validatedPathParams,
        query: validatedQueryParams,
      });

      logger.info(`[DeepLink] ✅ Navigation successful to: ${routeName}`);
    } catch (error) {
      logger.error(`[DeepLink] ❌ Navigation failed:`, {
        routeName,
        path,
        validatedPathParams,
        validatedQueryParams,
        error: error instanceof Error ? error.message : String(error),
        errorDetails: JSON.stringify(error),
      });

      // Redirect to error page for navigation failures
      await this.router.replace({
        name: "deep-link-error",
        params: validatedPathParams,
        query: {
          originalPath: path,
          errorCode: "ROUTING_ERROR",
          errorMessage: `Error routing to ${routeName}: ${(error as Error).message}`,
          ...validatedQueryParams,
        },
      });

      logger.info(
        `[DeepLink] 🔄 Redirected to error page for navigation failure`,
      );
    }
  }
}
