#!/usr/bin/env node
import { mkdir } from 'node:fs/promises'
import { existsSync, statSync } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import esbuild from 'esbuild'

const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const outdir = path.join(root, 'dist')
const minify = process.argv.includes('--minify')
const watch = process.argv.includes('--watch')

const srcAliasPlugin = {
  name: 'src-alias',
  setup(build) {
    build.onResolve({ filter: /.*/ }, args => {
      if (args.resolveDir.includes(`${path.sep}node_modules${path.sep}`)) return
      if (!args.path.startsWith('src/') && !args.path.startsWith('.')) return

      const basePath = args.path.startsWith('src/')
        ? path.join(root, args.path.slice(4))
        : path.resolve(args.resolveDir, args.path)

      const resolved = resolveSourcePath(basePath)
      return resolved
        ? { path: resolved }
        : {
            path: `${args.resolveDir}:${args.path}`,
            namespace: 'missing-local',
            pluginData: { request: args.path },
          }
    })

    build.onLoad({ filter: /.*/, namespace: 'missing-local' }, args => {
      const request = args.pluginData?.request ?? args.path
      if (String(request).endsWith('.md')) {
        return { loader: 'js', contents: 'export default ""' }
      }
      return {
        loader: 'js',
        contents: `
const noop = new Proxy(function () {}, {
  get: () => noop,
  apply: () => undefined,
  construct: () => ({})
})
module.exports = new Proxy({}, {
  get: (_target, prop) => prop === '__esModule' ? true : noop
})
`,
      }
    })
  },
}

function resolveSourcePath(candidate) {
  const candidates = [candidate]
  if (candidate.endsWith('.js')) {
    candidates.push(
      candidate.slice(0, -3) + '.ts',
      candidate.slice(0, -3) + '.tsx',
    )
  }
  if (!path.extname(candidate)) {
    candidates.push(
      candidate + '.ts',
      candidate + '.tsx',
      candidate + '.js',
      path.join(candidate, 'index.ts'),
      path.join(candidate, 'index.tsx'),
      path.join(candidate, 'index.js'),
    )
  }
  return candidates.find(file => existsSync(file) && statSync(file).isFile())
}

const bunBundleShimPlugin = {
  name: 'bun-bundle-shim',
  setup(build) {
    build.onResolve({ filter: /^bun:bundle$/ }, () => ({
      path: 'bun:bundle',
      namespace: 'bun-bundle-shim',
    }))
    build.onLoad({ filter: /.*/, namespace: 'bun-bundle-shim' }, () => ({
      loader: 'js',
      contents: 'export function feature() { return false }',
    }))
  },
}

const nativeShimPlugin = {
  name: 'native-shims',
  setup(build) {
    build.onResolve({ filter: /^color-diff-napi$/ }, () => ({
      path: path.join(root, 'native-ts/color-diff/index.ts'),
    }))
    build.onResolve(
      {
        filter:
          /^(?:@ant\/|@anthropic-ai\/(?:bedrock-sdk|foundry-sdk|vertex-sdk|mcpb|sandbox-runtime)$|audio-capture-napi$|modifiers-napi$|sharp$)/,
      },
      args => ({
        path: args.path,
        namespace:
          args.path === '@anthropic-ai/sandbox-runtime'
            ? 'sandbox-runtime-shim'
            : args.path === '@ant/claude-for-chrome-mcp'
              ? 'chrome-mcp-shim'
            : 'missing-package',
      }),
    )
    build.onLoad({ filter: /.*/, namespace: 'sandbox-runtime-shim' }, () => ({
      loader: 'js',
      contents: `
export class SandboxViolationStore {
  getRecentViolations() { return [] }
  getViolationCount() { return 0 }
  getTotalCount() { return 0 }
  subscribe(_cb) { return () => {} }
  clear() {}
}

const emptyStore = new SandboxViolationStore()

export const SandboxRuntimeConfigSchema = {
  parse(value) { return value },
  safeParse(value) { return { success: true, data: value } },
}

export const SandboxManager = {
  checkDependencies() { return { errors: [], warnings: [] } },
  isSupportedPlatform() { return false },
  async initialize() {},
  updateConfig() {},
  async reset() {},
  getFsReadConfig() { return { allowed: [], denied: [] } },
  getFsWriteConfig() { return { allowOnly: [], denyWithinAllow: [] } },
  getNetworkRestrictionConfig() { return { allowed: [], denied: [] } },
  getIgnoreViolations() { return undefined },
  getAllowUnixSockets() { return undefined },
  getAllowLocalBinding() { return undefined },
  getEnableWeakerNestedSandbox() { return undefined },
  getProxyPort() { return undefined },
  getSocksProxyPort() { return undefined },
  getLinuxHttpSocketPath() { return undefined },
  getLinuxSocksSocketPath() { return undefined },
  async waitForNetworkInitialization() { return false },
  getSandboxViolationStore() { return emptyStore },
  annotateStderrWithSandboxFailures(_command, stderr) { return stderr },
  cleanupAfterCommand() {},
  async wrapWithSandbox(command) { return command },
}
`,
    }))
    build.onLoad({ filter: /.*/, namespace: 'chrome-mcp-shim' }, () => ({
      loader: 'js',
      contents: `
export const BROWSER_TOOLS = []
export function createClaudeForChromeMcpServer() {
  return { async connect() {} }
}
export default { BROWSER_TOOLS, createClaudeForChromeMcpServer }
`,
    }))
    build.onLoad({ filter: /.*/, namespace: 'missing-package' }, () => ({
      loader: 'js',
      contents: `
const noop = new Proxy(function () {}, {
  get: () => noop,
  apply: () => undefined,
  construct: () => ({})
})
module.exports = new Proxy({}, {
  get: (_target, prop) => prop === '__esModule' ? true : noop
})
`,
    }))
  },
}

await mkdir(outdir, { recursive: true })

const options = {
  entryPoints: [path.join(root, 'entrypoints/cli.tsx')],
  outfile: path.join(outdir, 'claude.js'),
  bundle: true,
  platform: 'node',
  target: 'node22',
  format: 'esm',
  jsx: 'automatic',
  minify,
  sourcemap: !minify,
  banner: {
    js: `#!/usr/bin/env node
import { createRequire as __createRequire } from "node:module";
const require = __createRequire(import.meta.url);
const MACRO = {
  VERSION: "1.0.0",
  BUILD_TIME: ${JSON.stringify(new Date().toISOString())},
  PACKAGE_URL: "@anthropic-ai/claude-code",
  NATIVE_PACKAGE_URL: "@anthropic-ai/claude-code-native",
  FEEDBACK_CHANNEL: "https://github.com/anthropics/claude-code/issues",
  ISSUES_EXPLAINER: "file an issue at https://github.com/anthropics/claude-code/issues",
  VERSION_CHANGELOG: ""
};`,
  },
  define: {
    'MACRO.VERSION': JSON.stringify('1.0.0'),
    'MACRO.BUILD_TIME': JSON.stringify(new Date().toISOString()),
  },
  external: [
    'node-pty',
    'fsevents',
    '@aws-sdk/*',
    '@smithy/*',
    '@azure/identity',
    'google-auth-library',
  ],
  loader: {
    '.md': 'text',
  },
  plugins: [bunBundleShimPlugin, nativeShimPlugin, srcAliasPlugin],
  logLevel: 'info',
}

if (watch) {
  const ctx = await esbuild.context(options)
  await ctx.watch()
  console.log('watching...')
} else {
  await esbuild.build(options)
}
