/**
 * @file Deep Link Handler Service
 * @author Matthew Raymer
 *
 * This service handles the processing and routing of deep links in the TimeSafari app.
 * It provides a type-safe interface between the raw deep links and the application router.
 *
 * Architecture:
 * 1. DeepLinkHandler class encapsulates all deep link processing logic
 * 2. Uses Zod schemas from interfaces/deepLinks for parameter validation
 * 3. Provides consistent error handling and logging
 * 4. Maps validated parameters to Vue router calls
 *
 * Error Handling Strategy:
 * - All errors are wrapped in DeepLinkError interface
 * - Errors include error codes for systematic handling
 * - Detailed error information is logged for debugging
 * - Errors are propagated to the global error handler
 *
 * Validation Strategy:
 * - URL structure validation
 * - Route-specific parameter validation using Zod schemas
 * - Query parameter validation and sanitization
 * - Type-safe parameter passing to router
 *
 * Deep Link Format:
 * timesafari://<route>[/<param>][?queryParam1=value1&queryParam2=value2]
 *
 * Supported Routes:
 * - claim: View claim
 * - claim-add-raw: Add raw claim
 * - claim-cert: View claim certificate
 * - confirm-gift
 * - contact-import: Import contacts
 * - did: View DID
 * - invite-one-accept: Accept invitation
 * - onboard-meeting-members
 * - project: View project details
 * - user-profile: View user profile
 *
 * @example
 * const handler = new DeepLinkHandler(router);
 * await handler.handleDeepLink("timesafari://claim/123?view=details");
 */

import { Router } from "vue-router";
import {
  deepLinkSchemas,
  baseUrlSchema,
  routeSchema,
  DeepLinkRoute,
} from "../interfaces/deepLinks";
import { logConsoleAndDb } from "../db/databaseUtil";
import type { DeepLinkError } from "../interfaces/deepLinks";

/**
 * 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;
  }

  /**
   * Maps deep link routes to their corresponding Vue router names and optional parameter keys.
   *
   * The paramKey is used to extract the parameter from the route path,
   * because "router.replace" expects the right parameter name for the route.
   * The default is "id".
   */
  private readonly ROUTE_MAP: Record<
    string,
    { name: string; paramKey?: string }
  > = {
    // note that similar lists are in src/interfaces/deepLinks.ts
    claim: { name: "claim" },
    "claim-add-raw": { name: "claim-add-raw" },
    "claim-cert": { name: "claim-cert" },
    "confirm-gift": { name: "confirm-gift" },
    "contact-import": { name: "contact-import", paramKey: "jwt" },
    did: { name: "did", paramKey: "did" },
    "invite-one-accept": { name: "invite-one-accept", paramKey: "jwt" },
    "onboard-meeting-members": { name: "onboard-meeting-members" },
    project: { name: "project" },
    "user-profile": { name: "user-profile" },
  };

  /**
   * Parses deep link URL into path, params and query components.
   * Validates URL structure using Zod schemas.
   *
   * @param url - The deep link URL to parse (format: scheme://path[?query])
   * @throws {DeepLinkError} If URL format is invalid
   * @returns Parsed URL components (path: string, params: {KEY: string}, query: {KEY: string})
   */
  private parseDeepLink(url: string) {
    const parts = url.split("://");
    if (parts.length !== 2) {
      throw { code: "INVALID_URL", message: "Invalid URL format" };
    }

    // Validate base URL structure
    baseUrlSchema.parse({
      scheme: parts[0],
      path: parts[1],
      queryParams: {}, // Will be populated below
    });

    const [path, queryString] = parts[1].split("?");
    const [routePath, ...pathParams] = path.split("/");
    // logger.info(
    //   "[DeepLink] Debug:",
    //   "Route Path:",
    //   routePath,
    //   "Path Params:",
    //   pathParams,
    //   "Query String:",
    //   queryString,
    // );

    // Validate route exists before proceeding
    if (!this.ROUTE_MAP[routePath]) {
      throw {
        code: "INVALID_ROUTE",
        message: `Invalid route path: ${routePath}`,
        details: { routePath },
      };
    }

    const query: Record<string, string> = {};
    if (queryString) {
      new URLSearchParams(queryString).forEach((value, key) => {
        query[key] = value;
      });
    }

    const params: Record<string, string> = {};
    if (pathParams) {
      // Now we know routePath exists in ROUTE_MAP
      const routeConfig = this.ROUTE_MAP[routePath];
      params[routeConfig.paramKey ?? "id"] = pathParams.join("/");
    }
    return { path: routePath, params, query };
  }

  /**
   * Routes the deep link to appropriate view with validated parameters.
   * Validates route and parameters using Zod schemas before routing.
   *
   * @param path - The route path from the deep link
   * @param params - URL parameters
   * @param query - Query string parameters
   * @throws {DeepLinkError} If validation fails or route is invalid
   */
  private async validateAndRoute(
    path: string,
    params: Record<string, string>,
    query: Record<string, string>,
  ): Promise<void> {
    // First try to validate the route path
    let routeName: string;

    try {
      // Validate route exists
      const validRoute = routeSchema.parse(path) as DeepLinkRoute;
      routeName = this.ROUTE_MAP[validRoute].name;
    } catch (error) {
      // Log the invalid route attempt
      logConsoleAndDb(`[DeepLink] Invalid route path: ${path}`, true);

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

      throw {
        code: "INVALID_ROUTE",
        message: `Unsupported route: ${path}`,
      };
    }

    // Continue with parameter validation as before...
    const schema = deepLinkSchemas[path as keyof typeof deepLinkSchemas];

    try {
      const validatedParams = await schema.parseAsync({
        ...params,
        ...query,
      });

      await this.router.replace({
        name: routeName,
        params: validatedParams,
        query,
      });
    } catch (error) {
      // For parameter validation errors, provide specific error feedback
      await this.router.replace({
        name: "deep-link-error",
        query: {
          originalPath: path,
          errorCode: "INVALID_PARAMETERS",
          message: `The link parameters are invalid: ${(error as Error).message}`,
        },
      });

      throw {
        code: "INVALID_PARAMETERS",
        message: (error as Error).message,
        details: error,
        params: params,
        query: query,
      };
    }
  }

  /**
   * Processes incoming deep links and routes them appropriately.
   * Handles validation, error handling, and routing to the correct view.
   *
   * @param url - The deep link URL to process
   * @throws {DeepLinkError} If URL processing fails
   */
  async handleDeepLink(url: string): Promise<void> {
    try {
      logConsoleAndDb("[DeepLink] Processing URL: " + url, false);
      const { path, params, query } = this.parseDeepLink(url);
      // Ensure params is always a Record<string,string> by converting undefined to empty string
      const sanitizedParams = Object.fromEntries(
        Object.entries(params).map(([key, value]) => [key, value ?? ""]),
      );
      await this.validateAndRoute(path, sanitizedParams, query);
    } catch (error) {
      const deepLinkError = error as DeepLinkError;
      logConsoleAndDb(
        `[DeepLink] Error (${deepLinkError.code}): ${deepLinkError.message}`,
        true,
      );

      throw {
        code: deepLinkError.code || "UNKNOWN_ERROR",
        message: deepLinkError.message,
        details: deepLinkError.details,
      };
    }
  }
}
