import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { createHash } from 'node:crypto';
import { execFileSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';

const dirname = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(dirname, '..');
const submoduleDir = path.join(repoRoot, 'vendor', 'subminer-yomitan');
const submodulePackagePath = path.join(submoduleDir, 'package.json');
const submodulePackageLockPath = path.join(submoduleDir, 'package-lock.json');
const buildOutputDir = path.join(repoRoot, 'build', 'yomitan');
const stampPath = path.join(buildOutputDir, '.subminer-build.json');
const zipPath = path.join(submoduleDir, 'builds', 'yomitan-chrome.zip');
const bunCommand = process.versions.bun ? process.execPath : 'bun';
const dependencyStampPath = path.join(submoduleDir, 'node_modules', '.subminer-package-lock-hash');

function run(command, args, cwd) {
  execFileSync(command, args, { cwd, stdio: 'inherit' });
}

function escapePowerShellString(value) {
  return value.replaceAll("'", "''");
}

function readCommand(command, args, cwd) {
  return execFileSync(command, args, { cwd, encoding: 'utf8' }).trim();
}

function readStamp() {
  try {
    return JSON.parse(fs.readFileSync(stampPath, 'utf8'));
  } catch {
    return null;
  }
}

function hashFile(filePath) {
  const hash = createHash('sha256');
  hash.update(fs.readFileSync(filePath));
  return hash.digest('hex');
}

function ensureSubmodulePresent() {
  if (!fs.existsSync(submodulePackagePath)) {
    throw new Error(
      'Missing vendor/subminer-yomitan submodule. Run `git submodule update --init --recursive`.',
    );
  }
}

function getSourceState() {
  try {
    const revision = readCommand('git', ['rev-parse', 'HEAD'], submoduleDir);
    const dirty = readCommand('git', ['status', '--short', '--untracked-files=no'], submoduleDir);
    return { revision, dirty };
  } catch (error) {
    if (process.env.SUBMINER_YOMITAN_ALLOW_MISSING_GIT === '1') {
      return { revision: 'unknown', dirty: '' };
    }
    throw error;
  }
}

function isBuildCurrent(force) {
  if (force) {
    return false;
  }
  if (!fs.existsSync(path.join(buildOutputDir, 'manifest.json'))) {
    return false;
  }

  const stamp = readStamp();
  if (!stamp) {
    return false;
  }

  const currentState = getSourceState();
  return stamp.revision === currentState.revision && stamp.dirty === currentState.dirty;
}

function ensureDependenciesInstalled() {
  const nodeModulesDir = path.join(submoduleDir, 'node_modules');
  const currentLockHash = hashFile(submodulePackageLockPath);
  let installedLockHash = '';
  try {
    installedLockHash = fs.readFileSync(dependencyStampPath, 'utf8').trim();
  } catch {}

  if (!fs.existsSync(nodeModulesDir) || installedLockHash !== currentLockHash) {
    run(bunCommand, ['install', '--no-save'], submoduleDir);
    fs.mkdirSync(nodeModulesDir, { recursive: true });
    fs.writeFileSync(dependencyStampPath, `${currentLockHash}\n`, 'utf8');
  }
}

function installAndBuild() {
  ensureDependenciesInstalled();
  run(bunCommand, ['./dev/bin/build.js', '--target', 'chrome'], submoduleDir);
}

function extractBuild() {
  if (!fs.existsSync(zipPath)) {
    throw new Error(`Expected Yomitan build artifact at ${zipPath}`);
  }

  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-yomitan-'));
  try {
    if (process.platform === 'win32') {
      run(
        'powershell.exe',
        [
          '-NoProfile',
          '-NonInteractive',
          '-ExecutionPolicy',
          'Bypass',
          '-Command',
          `Expand-Archive -LiteralPath '${escapePowerShellString(zipPath)}' -DestinationPath '${escapePowerShellString(tempDir)}' -Force`,
        ],
        repoRoot,
      );
    } else {
      run('unzip', ['-qo', zipPath, '-d', tempDir], repoRoot);
    }
    fs.rmSync(buildOutputDir, { recursive: true, force: true });
    fs.mkdirSync(path.dirname(buildOutputDir), { recursive: true });
    fs.cpSync(tempDir, buildOutputDir, { recursive: true });
    if (!fs.existsSync(path.join(buildOutputDir, 'manifest.json'))) {
      throw new Error(`Extracted Yomitan build missing manifest.json in ${buildOutputDir}`);
    }
  } finally {
    fs.rmSync(tempDir, { recursive: true, force: true });
  }
}

function writeStamp() {
  const state = getSourceState();
  fs.writeFileSync(
    stampPath,
    `${JSON.stringify(
      {
        revision: state.revision,
        dirty: state.dirty,
        builtAt: new Date().toISOString(),
      },
      null,
      2,
    )}\n`,
    'utf8',
  );
}

function main() {
  const force = process.argv.includes('--force');
  ensureSubmodulePresent();

  if (isBuildCurrent(force)) {
    process.stdout.write(`Yomitan build current: ${buildOutputDir}\n`);
    return;
  }

  process.stdout.write('Building Yomitan Chrome artifact...\n');
  installAndBuild();
  extractBuild();
  writeStamp();
  process.stdout.write(`Yomitan extracted to ${buildOutputDir}\n`);
}

main();
