import type { PageName, PropsName, ResolvedUserOptions } from "../types.js";

import { resolveUrlOption } from "../config/resolveUrlOption.js";
import type { AutoDiscoveredFiles } from "../types.js";
import { createLogger } from "vite";

type GetRouteFilesSuccess = {
  type: "success";
  page: string;
  props?: string | undefined;
  root?: string | undefined;
  html?: string | undefined;
};

type GetRouteFilesError = {
  type: "error";
  error: unknown;
};

/**
 * Gets page and props file paths for a route using cached discovery + dynamic fallback.
 *
 * ## RUNTIME RESOLUTION STRATEGY
 * This function implements a two-tier lookup strategy:
 *
 * 1. **FAST PATH**: Check auto-discovered urlMap cache first
 *    - Built by resolveBuildPages.ts from build.pages array
 *    - O(1) lookup, no I/O, very fast
 *    - Used for routes that were pre-discovered at build time
 *
 * 2. **FALLBACK PATH**: Dynamic resolution via resolveUrlOption
 *    - Only when route not found in urlMap cache
 *    - Calls Page/props resolver functions with the route URL
 *    - Enables dynamic routing beyond what's in build.pages
 *    - Slower due to function calls + potential I/O
 *
 * ## Auto-Discovery Integration:
 * Results from dynamic resolution are cached back into autoDiscoveredFiles.urlMap
 * for future requests, so each route is only resolved dynamically once.
 *
 * ## Function Resolver Support:
 * This is where router functions like `(url) => "./src/pages/" + url + ".tsx"`
 * are actually called. The dynamic nature allows runtime route resolution but
 * creates complexity for static analysis and build-time discovery.
 */
export const getRouteFiles = async (
  route: string,
  autoDiscoveredFiles: AutoDiscoveredFiles,
  userOptions: Pick<
    ResolvedUserOptions,
    | PageName
    | PropsName
    | "Root"
    | "Html"
    | "moduleBasePath"
    | "verbose"
    | "pageExportName"
    | "propsExportName"
    | "rootExportName"
    | "htmlExportName"
  >,
  logger = createLogger()
): Promise<GetRouteFilesSuccess | GetRouteFilesError> => {
  if (autoDiscoveredFiles.urlMap.has(route)) {
    const cached = autoDiscoveredFiles.urlMap.get(route)!;
    const { page, props } = cached;
    let { root, html } = cached;

    if (userOptions.verbose) {
      logger.info(`[getRouteFiles] Found cached route: ${route}, page: "${page}", props: "${props}"`);
    }

    // If the cached page is undefined, fall back to resolveUrlOption
    if (!page) {
      if (userOptions.verbose) {
        logger.info("[getRouteFiles] Cached page is undefined, falling back to resolveUrlOption");
      }
      // Fall through to the resolveUrlOption logic below
    } else {
      if (userOptions.verbose) {
        if (page && page !== "") {
          logger.info(`[getRouteFiles] Page: \"${page}\"`);
        }
        if (props && props !== "") {
          logger.info(`[getRouteFiles] Props: \"${props}\"`);
        }
        if (root && root !== "") {
          logger.info(`[getRouteFiles] Root: \"${root}\"`);
        }
        if (html && html !== "") {
          logger.info(`[getRouteFiles] Html: \"${html}\"`);
        }
      }

      // For cached routes, we still need to resolve Root and Html dynamically if they are functions
      // and not already cached
      if (!root && userOptions.Root) {
      const {
        type: rootType,
        error: rootError,
        Root,
      } = await resolveUrlOption(userOptions, "Root", route);
      if (rootType === "error") {
        return { type: "error", error: rootError };
      }
      root = Root;
      // Update cache with resolved root path
      autoDiscoveredFiles.urlMap.set(route, { page, props, root, html });
    }

    if (!html && userOptions.Html) {
      const {
        type: htmlType,
        error: htmlError,
        Html,
      } = await resolveUrlOption(userOptions, "Html", route);
      if (htmlType === "error") {
        return { type: "error", error: htmlError };
      }
      html = Html;
      // Update cache with resolved html path
      autoDiscoveredFiles.urlMap.set(route, { page, props, root, html });
    }

      return { type: "success", page, props, root, html };
    }
  }
  if (userOptions.verbose) {
    logger.info("[getRouteFiles] Not in urlMap, resolving Page option");
  }
  const { type, error, Page } = await resolveUrlOption(
    userOptions,
    "Page",
    route
  );
  if (type === "error") {
    return { type: "error", error };
  }
  if (userOptions.verbose) {
    logger.info(`[getRouteFiles] Page resolved to: ${Page}`);
  }
  // Resolve Root and Html components
  let root: string | undefined;
  let html: string | undefined;

  if (userOptions.Root) {
    const {
      type: rootType,
      error: rootError,
      Root,
    } = await resolveUrlOption(userOptions, "Root", route);
    if (rootType === "error") {
      return { type: "error", error: rootError };
    }
    root = Root;
  }

  if (userOptions.Html) {
    const {
      type: htmlType,
      error: htmlError,
      Html,
    } = await resolveUrlOption(userOptions, "Html", route);
    if (htmlType === "error") {
      return { type: "error", error: htmlError };
    }
    html = Html;
  }

  if (!userOptions.props) {
    if (userOptions.verbose) {
      logger.info("[getRouteFiles] No props option, returning page only");
    }
    autoDiscoveredFiles.urlMap.set(route, {
      page: Page,
      props: undefined,
      root,
      html,
    });
    return { type: "success", page: Page, props: undefined, root, html };
  }
  if (userOptions.verbose) {
    logger.info("[getRouteFiles] Resolving props option");
  }
  const {
    type: propsType,
    error: propsError,
    props,
  } = await resolveUrlOption(userOptions, "props", route);

  if (propsType === "error") {
    return { type: "error", error: propsError };
  }
  if (userOptions.verbose) {
    logger.info(`[getRouteFiles] Props resolved to: ${props}`);
  }
  autoDiscoveredFiles.urlMap.set(route, { page: Page, props, root, html });
  return { type: "success", page: Page, props, root, html };
};
