import { GeneratedJsWithTypes, header } from "./common.js";

export function importPath(modulePath: string) {
  // Replace backslashes with forward slashes.
  const filePath = modulePath.replace("\\", "/");
  // Strip off the file extension.
  const lastDot = filePath.lastIndexOf(".");
  return filePath.slice(0, lastDot === -1 ? undefined : lastDot);
}

export function moduleIdentifier(modulePath: string) {
  // TODO: This encoding seems ambiguous (`foo/bar` vs `foo_bar`).
  // Also we should probably rename things that are TypeScript keywords like `delete`.
  return importPath(modulePath).replace("/", "_");
}

const reactJS = `
  ${header("Generated React hooks.")}
  import { useQueryGeneric, useMutationGeneric, useConvexGeneric } from "convex/react";
  
  /**
   * Load a reactive query within a React component.
   *
   * This React hook contains internal state that will cause a rerender whenever
   * the query result changes.
   *
   * This relies on the {@link ConvexProvider} being above in the React component tree.
   *
   * @param name - The name of the query function.
   * @param args - The arguments to the query function.
   * @returns \`undefined\` if loading and the query's return value otherwise.
   */
  export const useQuery = useQueryGeneric;
  
  /**
   * Construct a new {@link ReactMutation}.
   *
   * Mutation objects can be called like functions to request execution of the
   * corresponding Convex function, or further configured with
   * [optimistic updates](https://docs.convex.dev/using/optimistic-updates).
   *
   * The value returned by this hook is stable across renders, so it can be used
   * by React dependency arrays and memoization logic relying on object identity
   * without causing rerenders.
   *
   * This relies on the {@link ConvexProvider} being above in the React component tree.
   *
   * @param name - The name of the mutation.
   * @returns The {@link ReactMutation} object with that name.
   */
  export const useMutation = useMutationGeneric;
  
  /**
   * Get the {@link ConvexReactClient} within a React component.
   *
   * This relies on the {@link ConvexProvider} being above in the React component tree.
   *
   * @returns The active {@link ConvexReactClient} object, or \`undefined\`.
   */
  export const useConvex = useConvexGeneric;
  `;

export function reactCodegen(modulePaths: string[]): GeneratedJsWithTypes {
  const reactDTS = `${header("Generated React hooks.")}
  import type { ApiFromModules, OptimisticLocalStore as GenericOptimisticLocalStore } from "convex/browser";
  import type { UseQueryForAPI, UseMutationForAPI, UseConvexForAPI } from "convex/react";
  ${modulePaths
    .map(
      modulePath =>
        `import type * as ${moduleIdentifier(modulePath)} from "../${importPath(
          modulePath
        )}";`
    )
    .join("\n")}

  /**
   * A type describing your app's public Convex API.
   *
   * This \`ConvexAPI\` type includes information about the arguments and return
   * types of your app's query and mutation functions.
   *
   * This type should be used with type-parameterized classes like
   * \`ConvexReactClient\` to create app-specific types.
   */
  export type ConvexAPI = ApiFromModules<{
    ${modulePaths
      .map(
        modulePath =>
          `"${importPath(modulePath)}": typeof ${moduleIdentifier(modulePath)},`
      )
      .join("\n")}
  }>;
  
  /**
   * Load a reactive query within a React component.
   *
   * This React hook contains internal state that will cause a rerender whenever
   * the query result changes.
   *
   * This relies on the {@link ConvexProvider} being above in the React component tree.
   *
   * @param name - The name of the query function.
   * @param args - The arguments to the query function.
   * @returns \`undefined\` if loading and the query's return value otherwise.
   */
  export declare const useQuery: UseQueryForAPI<ConvexAPI>;
  
  /**
   * Construct a new {@link ReactMutation}.
   *
   * Mutation objects can be called like functions to request execution of the
   * corresponding Convex function, or further configured with
   * [optimistic updates](https://docs.convex.dev/using/optimistic-updates).
   *
   * The value returned by this hook is stable across renders, so it can be used
   * by React dependency arrays and memoization logic relying on object identity
   * without causing rerenders.
   *
   * This relies on the {@link ConvexProvider} being above in the React component tree.
   *
   * @param name - The name of the mutation.
   * @returns The {@link ReactMutation} object with that name.
   */
  export declare const useMutation: UseMutationForAPI<ConvexAPI>;
  
  /**
   * Get the {@link ConvexReactClient} within a React component.
   *
   * This relies on the {@link ConvexProvider} being above in the React component tree.
   *
   * @returns The active {@link ConvexReactClient} object, or \`undefined\`.
   */
  export declare const useConvex: UseConvexForAPI<ConvexAPI>;

  /**
   * A view of the query results currently in the Convex client for use within
   * optimistic updates.
   */
  export type OptimisticLocalStore = GenericOptimisticLocalStore<ConvexAPI>;
  `;
  return {
    JS: reactJS,
    DTS: reactDTS,
  };
}
