import { fileURLToPath } from 'node:url';
import { join, normalize } from 'pathe';
import { createUnplugin } from 'unplugin';
import { defu } from 'defu';
import tailwind from '@tailwindcss/vite';
import { g as getTemplates, d as defaultOptions, a as getDefaultConfig } from './shared/b24ui-nuxt.CIiXiDdZ.mjs';
import fs from 'node:fs';
import path from 'node:path';
import MagicString from 'magic-string';
import { genSafeVariableName } from 'knitwork';
import { resolvePathSync } from 'mlly';
import { globSync } from 'tinyglobby';
import AutoImportComponents from 'unplugin-vue-components';
import AutoImport from 'unplugin-auto-import';
import 'scule';
import '@nuxt/kit';
import 'node:fs/promises';

function TemplatePlugin(options, appConfig) {
  const templates = getTemplates(options, appConfig.b24ui);
  const templateKeys = new Set(templates.map((t) => `#build/${t.filename}`));
  async function writeTemplates(root) {
    const map = {};
    const dir = path.join(root, "node_modules", ".b24ui-nuxt");
    for (const template of templates) {
      if (!template.write || !template.filename) {
        continue;
      }
      const filePath = path.join(dir, template.filename);
      if (!fs.existsSync(path.dirname(filePath))) {
        fs.mkdirSync(path.dirname(filePath), { recursive: true });
      }
      fs.writeFileSync(filePath, await template.getContents({}));
      map[`#build/${template.filename}`] = filePath;
    }
    return map;
  }
  return {
    name: "bitrix24:b24ui:templates",
    enforce: "pre",
    vite: {
      async config(config) {
        const alias = await writeTemplates(config.root || process.cwd());
        return {
          resolve: {
            alias
          }
        };
      }
    },
    resolveId(id) {
      if (templateKeys.has(id + ".ts")) {
        return id.replace("#build/", "virtual:bitrix24-ui-templates/") + ".ts";
      }
    },
    loadInclude: (id) => templateKeys.has(id.replace("virtual:bitrix24-ui-templates/", "#build/")),
    load(id) {
      id = id.replace("virtual:bitrix24-ui-templates/", "#build/");
      return templates.find((t) => `#build/${t.filename}` === id).getContents({});
    }
  };
}

function PluginsPlugin(options) {
  const plugins = globSync(["**/*", "!*.d.ts"], { cwd: join(runtimeDir, "plugins"), absolute: true });
  plugins.unshift(resolvePathSync("../runtime/vue/plugins/head", { extensions: [".ts", ".mjs", ".js"], url: import.meta.url }));
  if (options.colorMode) {
    plugins.push(resolvePathSync("../runtime/vue/plugins/color-mode", { extensions: [".ts", ".mjs", ".js"], url: import.meta.url }));
  }
  return {
    name: "bitrix24:b24ui:plugins",
    enforce: "pre",
    resolveId(id) {
      if (id === "@bitrix24/b24ui-nuxt/vue-plugin") {
        return "virtual:bitrix24-ui-plugins";
      }
    },
    transform(code, id) {
      if (plugins.some((p) => id.startsWith(p)) && code.includes("import.meta.client")) {
        const s = new MagicString(code);
        s.replaceAll("import.meta.client", "true");
        if (s.hasChanged()) {
          return {
            code: s.toString(),
            map: s.generateMap({ hires: true })
          };
        }
      }
    },
    loadInclude: (id) => id === "virtual:bitrix24-ui-plugins",
    load() {
      return `
        ${plugins.map((p) => `import ${genSafeVariableName(p)} from "${p}"`).join("\n")}
export default {
  install (app) {
${plugins.map((p) => `    app.use(${genSafeVariableName(p)})`).join("\n")}
  }
}
        `;
    },
    // Argument Vite specific configuration
    vite: {
      config() {
        return {
          // Opt-out Bitrix24 UI from Vite's pre-bundling,
          // as we need Vite's pipeline to resolve imports like `#imports`
          optimizeDeps: {
            exclude: ["@bitrix24/b24ui-nuxt"]
          }
        };
      }
    }
  };
}

function AppConfigPlugin(_options, appConfig) {
  return {
    name: "bitrix24:b24ui:app-config",
    enforce: "pre",
    resolveId(id) {
      if (id === "#build/app.config") {
        return "virtual:bitrix24-ui-app-config";
      }
    },
    loadInclude: (id) => id === "virtual:bitrix24-ui-app-config",
    load() {
      return `
          export default ${JSON.stringify(appConfig)}
        `;
    },
    vite: {
      config() {
        return {
          test: {
            server: {
              deps: {
                inline: ["@bitrix24/b24ui"]
              }
            }
          }
        };
      }
    }
  };
}

function ComponentImportPlugin(options, meta) {
  const components = globSync("**/*.vue", {
    cwd: join(runtimeDir, "components"),
    ignore: [
      !options.colorMode && "color-mode/**/*.vue",
      "content/*.vue",
      "prose/**/*.vue"
    ].filter(Boolean)
  });
  const componentNames = new Set(components.map((c) => `B24${c.split("/").pop()?.replace(/\.vue$/, "")}`));
  const componentPaths = new Map(components.map((c) => {
    const name = c.replace(/\.vue$/, "");
    const componentName = `B24${name.split("/").pop()}`;
    return [componentName, c];
  }));
  const componentsProse = globSync("**/*.vue", {
    cwd: join(runtimeDir, "components/prose")
  });
  const componentProseNames = new Set(componentsProse.map((c) => `Prose${c.replace(/\.vue$/, "")}`));
  const componentProsePaths = new Map(componentsProse.map((c) => {
    const name = c.replace(/\.vue$/, "");
    const componentName = `Prose${name.split("/").pop()}`;
    return [componentName, c];
  }));
  const overrides = globSync("**/*.vue", {
    cwd: join(runtimeDir, "vue/components"),
    ignore: [
      !options.colorMode && "color-mode/**/*.vue"
    ].filter(Boolean)
  });
  const overrideNames = new Set(overrides.map((c) => `B24${c.split("/").pop()?.replace(/\.vue$/, "")}`));
  const overridePaths = new Map(overrides.map((c) => {
    const name = c.replace(/\.vue$/, "");
    const componentName = `B24${name.split("/").pop()}`;
    return [componentName, c];
  }));
  const inertiaOverrides = globSync("**/*.vue", {
    cwd: join(runtimeDir, "inertia/components")
  });
  const inertiaOverrideNames = new Set(inertiaOverrides.map((c) => `B24${c.replace(/\.vue$/, "")}`));
  const pluginOptions = defu(options.components, {
    dts: options.dts ?? true,
    exclude: [
      /[\\/]node_modules[\\/](?!\.pnpm|@bitrix24\/b24ui-nuxt|@compodium\/examples)/,
      /[\\/]\.git[\\/]/,
      /[\\/]\.nuxt[\\/]/
    ],
    resolvers: [
      (componentName) => {
        if (options.inertia && inertiaOverrideNames.has(componentName)) {
          return { name: "default", from: join(runtimeDir, "inertia/components", `${componentName.slice("B24".length)}.vue`) };
        }
        if (overrideNames.has(componentName)) {
          const relativePath = overridePaths.get(componentName);
          return { name: "default", from: join(runtimeDir, "vue/components", relativePath) };
        }
        if (componentNames.has(componentName)) {
          const relativePath = componentPaths.get(componentName);
          return { name: "default", from: join(runtimeDir, "components", relativePath) };
        }
        if (componentProseNames.has(componentName)) {
          const relativePath = componentProsePaths.get(componentName);
          return { name: "default", from: join(runtimeDir, "components/prose", relativePath) };
        }
      }
    ]
  });
  return [
    /**
     * This plugin aims to ensure we override certain components with Vue-compatible versions:
     * <B24Link> currently.
     */
    {
      name: "bitrix24:b24ui:components",
      enforce: "pre",
      resolveId(id, importer) {
        if (!importer || !normalize(importer).includes(runtimeDir)) {
          return;
        }
        if (!RELATIVE_IMPORT_RE.test(id)) {
          return;
        }
        const filename = id.match(/([^/]+)\.vue$/)?.[1];
        if (filename && options.inertia && inertiaOverrideNames.has(`B24${filename}`)) {
          return join(runtimeDir, "inertia/components", `${filename}.vue`);
        }
        if (filename && overrideNames.has(`B24${filename}`)) {
          const relativePath = overridePaths.get(`B24${filename}`);
          return join(runtimeDir, "vue/components", relativePath);
        }
      }
    },
    AutoImportComponents.raw(pluginOptions, meta)
  ];
}
const RELATIVE_IMPORT_RE = /^\.{1,2}\//;

function Bitrix24EnvironmentPlugin(options) {
  const stubPath = resolvePathSync(options.inertia ? "../runtime/inertia/stubs" : "../runtime/vue/stubs", { extensions: [".ts", ".mjs", ".js"], url: import.meta.url });
  return {
    name: "bitrix24:b24ui",
    enforce: "pre",
    resolveId(id) {
      if (id === "#imports") {
        return stubPath;
      }
    },
    transformInclude(id) {
      return normalize(id).includes(runtimeDir);
    },
    transform(code) {
      if (code.includes("import.meta.client")) {
        const s = new MagicString(code);
        s.replaceAll("import.meta.client", "true");
        if (s.hasChanged()) {
          return {
            code: s.toString(),
            map: s.generateMap({ hires: true })
          };
        }
      }
    }
  };
}

function AutoImportPlugin(options, meta) {
  const pluginOptions = defu(options.autoImport, {
    dts: options.dts ?? true,
    dirs: [join(runtimeDir, "composables"), join(runtimeDir, "vue/composables")]
  });
  return AutoImport.raw(pluginOptions, meta);
}

const runtimeDir = normalize(fileURLToPath(new URL("./runtime", import.meta.url)));
const Bitrix24UIPlugin = createUnplugin((_options = {}, meta) => {
  const options = defu(_options, {}, defaultOptions);
  options.theme = options.theme || {};
  const appConfig = defu(
    {
      b24ui: options.b24ui,
      version: options.version,
      colorMode: options.colorMode,
      colorModeTypeLight: options.colorModeTypeLight
    },
    {
      b24ui: getDefaultConfig(options.theme)
    }
  );
  return [
    Bitrix24EnvironmentPlugin(options),
    ComponentImportPlugin(options, meta),
    AutoImportPlugin(options, meta),
    tailwind(),
    PluginsPlugin(options),
    TemplatePlugin(options, appConfig),
    AppConfigPlugin(options, appConfig),
    {
      name: "bitrix24:b24ui:plugins-duplication-detection",
      vite: {
        configResolved(config) {
          const plugins = config.plugins || [];
          if (plugins.filter((i) => i.name === "unplugin-auto-import").length > 1) {
            throw new Error("[Bitrix24 UI] Multiple instances of `unplugin-auto-import` detected. Bitrix24 UI includes `unplugin-auto-import` already, and you can configure it using `autoImport` option in Bitrix24 UI module options.");
          }
          if (plugins.filter((i) => i.name === "unplugin-vue-components").length > 1) {
            throw new Error("[Bitrix24 UI] Multiple instances of `unplugin-vue-components` detected. Bitrix24 UI includes `unplugin-vue-components` already, and you can configure it using `components` option in Bitrix24 UI module options.");
          }
        }
      }
    }
  ].flat(1);
});

export { Bitrix24UIPlugin, runtimeDir };
