import fs from 'node:fs';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
import { createRequire } from 'node:module';
import { c as chalk } from './chunks/index.js';
import 'node:process';
import 'node:os';
import 'node:tty';

/**
 * vite :: src/sass.mjs.
 *
 * Vite-based build/dev tooling for Vettvangur projects.
 *
 * These docs are generated from inline JSDoc in the repo and are intended for contributors.
 *
 * @generated
 * @module vite
 */

const /**
   * Pad.
   *
   * @param n - Value.
   * @param value - Value.
   * @returns Nothing.
   *
   * @generated
   * @example
   * // vite (src/tools/javascript/vite/src/sass.mjs)
   * // import { pad } from '@vettvangur/vite'
   *
   * pad(n, value)
   */
  pad = (n, w = 2) => String(n).padStart(w, '0');
const /**
   * Ts.
   *
   * @returns String result.
   *
   * @generated
   * @example
   * // vite (src/tools/javascript/vite/src/sass.mjs)
   * // import { ts } from '@vettvangur/vite'
   *
   * ts()
   */
  ts = () => {
    const d = new Date();
    return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(d.getMilliseconds(), 3)}`
  };
const /**
   * Vite Tag.
   *
   * @generated
   * @example
   * console.log(viteTag)
   */
  viteTag = chalk.cyan('[styles]');
const /**
   * T.
   *
   * @returns Nothing.
   *
   * @generated
   * @example
   * // vite (src/tools/javascript/vite/src/sass.mjs)
   * // import { t } from '@vettvangur/vite'
   *
   * t()
   */
  t = () => chalk.dim(`[${ts()}]`);

/**
 * Sass plugin unified.
 *
 * @param options - Options object.
 * @param options.styles - Path to the styles directory.
 * @param options.styleEntries - Entry file list.
 * @param options.dest - Destination path.
 * @param options.showMediaLabelDev - Whether to enable dev-only media labels.
 * @returns Result of the operation.
 * @throws When the operation fails.
 *
 * @example
 * // vite (src/tools/javascript/vite/src/sass.mjs)
 * // import { sassPluginUnified } from '@vettvangur/vite'
 *
 * sassPluginUnified({ styles: './src/styles', styleEntries: ['./src/styles/app.scss'], dest: './dist', showMediaLabelDev: true })
 */
function sassPluginUnified({
  styles, // absolute dir to watch (config.styles.path)
  styleEntries, // string[] of entry files (config.styles.entries)
  dest, // absolute output dir
  showMediaLabelDev = true,
}) {
  // --- validate inputs (strict) ---------------------------------------------
  if (typeof styles !== 'string' || !styles) {
    throw new Error(`[vettvangur-vite :: sass-lightning] "styles" must be a non-empty string (directory).`)
  }
  if (!Array.isArray(styleEntries) || styleEntries.length === 0) {
    throw new Error(`[vettvangur-vite :: sass-lightning] "styleEntries" must be a non-empty string[].`)
  }
  if (typeof dest !== 'string' || !dest) {
    throw new Error(`[vettvangur-vite :: sass-lightning] "dest" must be a non-empty string (directory).`)
  }

  const root = process.cwd();
  const existsDir = (p) => {
    try {
      return fs.existsSync(p) && fs.statSync(p).isDirectory()
    } catch {
      return false
    }
  };

  // normalize entries to absolute paths
  const entries = styleEntries.map((p) => (path.isAbsolute(p) ? p : path.resolve(root, p)));

  // ---------- resilient resolver (handles app vs pkg deps) -------------------
  let cached;
  const stack = async () => {
    if (cached) {
      return cached
    }

    const esbuild = (await import('esbuild')).default;

    const appRequire = createRequire(path.join(process.cwd(), 'package.json'));
    const pkgRequire = createRequire(import.meta.url);

    async function tryImport(id, deepFallback) {
      try {
        return await import(id)
      } catch {}
      try {
        return await import(pathToFileURL(appRequire.resolve(deepFallback ?? id)).href)
      } catch {}
      try {
        return await import(pathToFileURL(pkgRequire.resolve(deepFallback ?? id)).href)
      } catch (e) {
        throw new Error(
          `[vettvangur-vite :: sass-lightning] Missing dependency "${id}". Install in your APP when styles.type:'sass':\n  pnpm add -D esbuild-sass-plugin sass lightningcss\n${deepFallback ? `Tried deep fallback: ${deepFallback}\n` : ''}Original: ${e?.message || e}`
        )
      }
    }

    const sassMod = await tryImport('esbuild-sass-plugin', 'esbuild-sass-plugin/lib/index.js');
    const sassPlugin = sassMod.sassPlugin ?? sassMod.default?.sassPlugin ?? sassMod.default ?? sassMod;

    const lightningMod = await tryImport('lightningcss');
    const lightningTransform = lightningMod.transform ?? lightningMod.default?.transform;

    if (typeof sassPlugin !== 'function' || typeof lightningTransform !== 'function') {
      throw new Error('[vettvangur-vite :: sass-lightning] Failed to load sassPlugin / lightningcss.transform')
    }

    cached = { esbuild, sassPlugin, lightningTransform };
    return cached
  };

  // ---------- esbuild-sass plugin factory -----------------------------------
  function makeSassPlugin(deps, { showMediaLabel, minifyCss, cache, addTimestamp }) {
    const { sassPlugin, lightningTransform } = deps;
    const injectVar = `$showMediaLabel: ${showMediaLabel};\n`;

    return sassPlugin({
      type: 'css',
      filter: /\.(s[ac]ss|css)$/i,
      cache,
      sassOptions: { quietDeps: true, logger: { warn() {}, debug() {} } },
      additionalData: injectVar,
      precompile(src, isRoot) {
        return isRoot ? injectVar + src : src
      },
      async transform(src, resolveDir) {
        const { code } = lightningTransform({
          filename: path.join(resolveDir, 'style.css'),
          code: Buffer.from(src),
          minify: minifyCss,
          targets: { browsers: ['defaults'] },

          // ✨ Allow Vite to rewrite asset URLs
          resolver: {
            resolve(url) {
              return { url } // leave path untouched
            },
          },
        });
        return addTimestamp ? `/* dev-build: ${new Date().toISOString()} */\n${code.toString()}` : code.toString()
      },
    })
  }

  // ---------- shared esbuild options ----------------------------------------
  function baseEsbuildOptions({ outDir, sourcemap, plugin }) {
    return {
      entryPoints: entries,
      entryNames: '[name]',
      outExtension: { '.css': '.min.css' },
      bundle: true,
      sourcemap,
      minify: true, // JS off; CSS handled by LightningCSS
      outdir: outDir,
      write: true,
      format: 'esm',
      splitting: false,
      target: 'es2017',
      loader: {
        '.svg': 'dataurl',
        '.png': 'file',
        '.jpg': 'file',
        '.jpeg': 'file',
        '.gif': 'file',
        '.webp': 'file',
        '.woff': 'file',
        '.woff2': 'file',
        '.ttf': 'file',
        '.eot': 'file',
      },
      logLevel: 'silent',
      logOverride: {
        'empty-import-meta': 'silent',
        'equals-nan': 'silent',
        'this-is-undefined-in-esm': 'silent',
      },
      plugins: [plugin],
    }
  }

  let mode = 'serve';
  return {
    name: 'sass-lightning',

    async configResolved(cfg) {
      mode = cfg.command;
      if (!existsDir(styles)) {
        this.error(`[vettvangur-vite :: sass-lightning] styles directory does not exist: ${styles}`);
      }
    },

    // ------------------------------ DEV --------------------------------------
    async configureServer(server) {
      if (mode !== 'serve') {
        return
      }

      const deps = await stack();
      const outDirDev = dest;
      fs.mkdirSync(outDirDev, { recursive: true });

      const plugin = makeSassPlugin(deps, {
        showMediaLabel: showMediaLabelDev,
        minifyCss: true,
        cache: false,
        addTimestamp: false,
      });

      const ctx = await deps.esbuild.context(baseEsbuildOptions({ outDir: outDirDev, sourcemap: true, plugin }));

      // initial build (esbuild does the first build when watch starts)
      console.log(`${t()} ${viteTag} building styles...`);
      await ctx.watch();
      console.clear();
      console.log(`${t()} ${viteTag} build complete`);

      // watch the whole styles directory so partials trigger rebuild
      const globs = [`${styles}/**/*.scss`, `${styles}/**/*.sass`, `${styles}/**/*.css`];
      server.watcher.add(globs);

      // wait until chokidar finished initial scan, then only react to 'change'
      let primed = false;
      const prime = () => {
        primed = true;
      };
      server.watcher.once('ready', prime);
      setTimeout(() => {
        if (!primed) {
          prime();
        }
      }, 250);

      // debounce + single log per burst
      let pending = null;
      let inFlight = false;

      const schedule = () => {
        if (pending) {
          clearTimeout(pending);
        }
        pending = setTimeout(() => {
          pending = null;
          if (inFlight) {
            return
          }
          inFlight = true;
          console.log(`${t()} ${viteTag} updating styles...`);
          setTimeout(() => {
            console.clear();
            console.log(`${t()} ${viteTag} reloaded`);
            server.ws.send({ type: 'full-reload' });
            inFlight = false;
          }, 80);
        }, 80);
      };

      server.watcher.on('all', (evt, file) => {
        if (!primed || !file) {
          return
        }
        if (evt !== 'change') {
          return
        }
        const f = file.toLowerCase();
        if (f.endsWith('.scss') || f.endsWith('.sass') || f.endsWith('.css')) {
          schedule();
        }
      });

      server.httpServer?.once('close', async () => ctx.dispose());
    },

    // ------------------------------ BUILD ------------------------------------
    async buildStart() {
      if (mode !== 'build') {
        return
      }

      const deps = await stack();
      fs.mkdirSync(dest, { recursive: true });

      const plugin = makeSassPlugin(deps, {
        showMediaLabel: false,
        minifyCss: true,
        cache: true,
        addTimestamp: false,
      });

      await deps.esbuild.build(baseEsbuildOptions({ outDir: dest, sourcemap: true, plugin }));
    },
  }
}

export { sassPluginUnified };
//# sourceMappingURL=sass.js.map
