import * as path from "node:path";
import { existsSync } from "node:fs";
import { RollupOptions } from "rollup";
import { z } from "zod";
import {
  errorFormatter,
  ComponentLibraryConfig,
} from "@embeddable.com/sdk-utils";

export type Region = "EU" | "US" | "legacy-US";

export type EmbeddableConfig = {
  plugins: (() => {
    pluginName: string;
    build: (config: ResolvedEmbeddableConfig) => Promise<unknown>;
    cleanup: (config: ResolvedEmbeddableConfig) => Promise<unknown>;
    validate: (config: ResolvedEmbeddableConfig) => Promise<unknown>;
    buildPackage: (config: ResolvedEmbeddableConfig) => Promise<unknown>;
  })[];
  pushModels?: boolean;
  pushComponents?: boolean;
  pushBaseUrl?: string;
  audienceUrl?: string;
  authDomain?: string;
  authClientId?: string;
  errorFallbackComponent?: string;
  applicationEnvironment?: string;
  rollbarAccessToken?: string;
  previewBaseUrl?: string;
  componentsSrc?: string;
  modelsSrc?: string;
  presetsSrc?: string;
  globalCss?: string;
  viteConfig?: {
    resolve?: {
      alias?: Record<string, string>;
    };
  };
  rollupOptions?: RollupOptions;
  region?: Region;
  componentLibraries?: string[] | ComponentLibraryConfig[];
  customizationFile?: string;
  lifecycleHooksFile?: string;
};

const PLUGIN_NAME = "sdk-react" as const;

export type PluginName = typeof PLUGIN_NAME;
export type ResolvedEmbeddableConfig = {
  core: {
    rootDir: string;
    templatesDir: string;
    configsDir: string;
  };
  client: {
    rootDir: string;
    srcDir: string;
    modelsSrc: string;
    presetsSrc: string;
    buildDir: string;
    tmpDir: string;
    globalCss: string;
    componentDir: string;
    stencilBuild: string;
    archiveFile: string;
    errorFallbackComponent?: string;
    bundleHash?: string;
    customizationFile: string;
    lifecycleHooksFile: string;
    componentLibraries: string[] | ComponentLibraryConfig[];
    viteConfig: {
      resolve?: {
        alias?: Record<string, string>;
      };
    };
    rollupOptions: RollupOptions;
  };
  outputOptions: {
    typesEntryPointFilename: string;
  };
  pushModels: boolean;
  pushComponents: boolean;
  pushBaseUrl: string;
  audienceUrl: string;
  previewBaseUrl: string;
  authDomain: string;
  authClientId: string;
  applicationEnvironment: string;
  rollbarAccessToken: string;
  plugins: EmbeddableConfig["plugins"];
  buildTime: [number, number];
  dev?: {
    watch: boolean;
    logger: any;
    sys: any;
  };
  [PLUGIN_NAME]: {
    rootDir: string;
    templatesDir: string;
    outputOptions: {
      fileName: string;
      buildName: string;
      componentsEntryPointFilename: string;
    };
  };
};

const REGION_CONFIGS = {
  EU: {
    pushBaseUrl: "https://api.eu.embeddable.com",
    audienceUrl: "https://auth.eu.embeddable.com",
    previewBaseUrl: "https://app.eu.embeddable.com",
    authDomain: "auth.eu.embeddable.com",
    authClientId: "6OGPwIQsVmtrBKhNrwAaXhz4ePb0kBGV",
  },
  US: {
    pushBaseUrl: "https://api.us.embeddable.com",
    audienceUrl: "https://auth.embeddable.com",
    previewBaseUrl: "https://app.us.embeddable.com",
    authDomain: "auth.embeddable.com",
    authClientId: "dygrSUmI6HmgY5ymVbEAoLDEBxIOyr1V",
  },
  "legacy-US": {
    pushBaseUrl: "https://api.embeddable.com",
    audienceUrl: "https://auth.embeddable.com",
    previewBaseUrl: "https://app.embeddable.com",
    authDomain: "auth.embeddable.com",
    authClientId: "dygrSUmI6HmgY5ymVbEAoLDEBxIOyr1V",
  },
};

const ComponentLibraryConfigSchema = z.object({
  name: z.string(),
  include: z.array(z.string()).optional(),
  exclude: z.array(z.string()).optional(),
});

export const embeddableConfigSchema = z
  .object({
    plugins: z.array(z.function()),
    region: z
      .union([z.literal("EU"), z.literal("US"), z.literal("legacy-US")])
      .optional(),
    pushModels: z.boolean().optional(),
    pushComponents: z.boolean().optional(),
    pushBaseUrl: z.string().optional(),
    audienceUrl: z.string().optional(),
    authDomain: z.string().optional(),
    authClientId: z.string().optional(),
    errorFallbackComponent: z.string().optional(),
    applicationEnvironment: z.string().optional(),
    rollbarAccessToken: z.string().optional(),
    previewBaseUrl: z.string().optional(),
    modelsSrc: z.string().optional(),
    presetsSrc: z.string().optional(),
    componentsSrc: z.string().optional(),
    globalCss: z.string().optional(),
    customizationFile: z.string().optional(),
    lifecycleHooksFile: z.string().optional(),
    componentLibraries: z
      .union([z.array(z.string()), z.array(ComponentLibraryConfigSchema)])
      .optional(),
    viteConfig: z
      .object({
        resolve: z
          .object({
            alias: z.record(z.string()),
          })
          .optional(),
      })
      .optional(),
    rollupOptions: z.object({}).optional(),
  })
  .strict();

export default (config: EmbeddableConfig) => {
  const safeParse = embeddableConfigSchema.safeParse(config);

  const errors: string[] = [];
  if (!safeParse.success) {
    errorFormatter(safeParse.error.issues).forEach((error) => {
      errors.push(`${error}`);
    });
  }

  if (errors.length > 0) {
    throw new Error(`Invalid Embeddable Configuration: ${errors.join("\n")}}`);
  }

  let {
    plugins,
    region = "legacy-US",
    pushModels = true,
    pushComponents = true,
    pushBaseUrl,
    audienceUrl,
    authDomain,
    authClientId,
    errorFallbackComponent,
    applicationEnvironment,
    rollbarAccessToken,
    previewBaseUrl,
    modelsSrc = "src",
    presetsSrc = "src",
    componentsSrc = "src",
    globalCss = "src/global.css",
    viteConfig = {},
    rollupOptions = {},
    componentLibraries = [],
    customizationFile = "embeddable.theme.ts",
    lifecycleHooksFile = "lifecycle.config.ts",
  } = config;

  const regionConfig = REGION_CONFIGS[region];

  const __dirname = import.meta.dirname;
  const coreRoot = path.resolve(__dirname, "..");
  const clientRoot = process.cwd();

  if (!path.isAbsolute(componentsSrc)) {
    componentsSrc = path.resolve(clientRoot, componentsSrc);

    if (!existsSync(componentsSrc)) {
      throw new Error(
        `componentsSrc directory ${componentsSrc} does not exist`,
      );
    }
  }

  if (modelsSrc && !path.isAbsolute(modelsSrc)) {
    modelsSrc = path.resolve(clientRoot, modelsSrc);

    if (!existsSync(modelsSrc)) {
      throw new Error(`modelsSrc directory ${modelsSrc} does not exist`);
    }
  }

  if (presetsSrc && !path.isAbsolute(presetsSrc)) {
    presetsSrc = path.resolve(clientRoot, presetsSrc);

    if (!existsSync(presetsSrc)) {
      throw new Error(`presetsSrc directory ${presetsSrc} does not exist`);
    }
  }

  return {
    core: {
      rootDir: coreRoot,
      templatesDir: path.resolve(coreRoot, "templates"),
      configsDir: path.resolve(coreRoot, "configs"),
    },
    client: {
      rootDir: clientRoot,
      srcDir: path.resolve(clientRoot, componentsSrc),
      modelsSrc: modelsSrc ? path.resolve(clientRoot, modelsSrc) : undefined,
      presetsSrc: presetsSrc ? path.resolve(clientRoot, presetsSrc) : undefined,
      buildDir: path.resolve(clientRoot, ".embeddable-build"),
      tmpDir: path.resolve(clientRoot, ".embeddable-tmp"),
      globalCss: path.resolve(clientRoot, globalCss),
      componentDir: path.resolve(clientRoot, ".embeddable-build", "component"),
      stencilBuild: path.resolve(
        clientRoot,
        ".embeddable-build",
        "dist",
        "embeddable-wrapper",
      ),
      archiveFile: path.resolve(clientRoot, "embeddable-build.zip"),
      errorFallbackComponent: errorFallbackComponent
        ? path.resolve(clientRoot, errorFallbackComponent)
        : undefined,
      bundleHash: undefined, // This will be set by the build process
      viteConfig,
      rollupOptions,
      componentLibraries,
      customizationFile: path.resolve(clientRoot, customizationFile),
      lifecycleHooksFile: path.resolve(clientRoot, lifecycleHooksFile),
    },
    outputOptions: {
      typesEntryPointFilename: "embeddable-types-entry-point.js",
    },
    buildTime: undefined, // This will be set by the build process
    dev: {
      watch: false,
      logger: undefined,
      sys: undefined,
    },
    pushModels,
    pushComponents,
    pushBaseUrl: pushBaseUrl ?? regionConfig.pushBaseUrl,
    audienceUrl: audienceUrl ?? regionConfig.audienceUrl,
    previewBaseUrl: previewBaseUrl ?? regionConfig.previewBaseUrl,
    authDomain: authDomain ?? regionConfig.authDomain,
    authClientId: authClientId ?? regionConfig.authClientId,
    applicationEnvironment: applicationEnvironment ?? "production",
    rollbarAccessToken:
      rollbarAccessToken ?? "5c6028038d844bf1835a0f4db5f55d9e",
    plugins,
  };
};
