#!/usr/bin/env node

import { execFileSync } from 'node:child_process';
import { statSync } from 'node:fs';
import path from 'node:path';

const repoRoot = process.cwd();
const allowAssetCommit = process.env.ALLOW_ASSET_COMMIT === '1';
const maxBinarySizeBytes = 5 * 1024 * 1024;
const tmpCapturePattern = /^tmp-.*\.(png|jpe?g|webp|gif|mp4|mov|webm)$/i;
const binaryAssetPattern = /\.(png|jpe?g|webp|gif|svg|mp4|mov|webm|pdf|zip|7z)$/i;

const blockedAssetPrefixes = ['public/assets/'];

function getStagedPaths() {
  const output = execFileSync(
    'git',
    ['diff', '--cached', '--name-only', '--diff-filter=ACMR'],
    { cwd: repoRoot, encoding: 'utf8' }
  );

  return output
    .split('\n')
    .map((line) => line.trim())
    .filter(Boolean);
}

function toDisplaySize(bytes) {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

function isBlockedAssetPath(filePath) {
  return blockedAssetPrefixes.some((prefix) => filePath.startsWith(prefix));
}

function main() {
  const stagedPaths = getStagedPaths();
  const violations = [];

  for (const relativePath of stagedPaths) {
    const absolutePath = path.join(repoRoot, relativePath);
    let stats;

    try {
      stats = statSync(absolutePath);
    } catch {
      continue;
    }

    if (!stats.isFile()) continue;

    const baseName = path.basename(relativePath);
    const isTmpCapture = tmpCapturePattern.test(baseName);
    const isBinaryAsset = binaryAssetPattern.test(relativePath);
    const isOversizedBinary = isBinaryAsset && stats.size > maxBinarySizeBytes;
    const isBlockedAsset = isBinaryAsset && isBlockedAssetPath(relativePath);

    if (!isTmpCapture && !isOversizedBinary && !isBlockedAsset) continue;

    const reasons = [];
    if (isTmpCapture) reasons.push('temporary capture file');
    if (isBlockedAsset) reasons.push('asset path under public/assets');
    if (isOversizedBinary) {
      reasons.push(`binary file exceeds ${toDisplaySize(maxBinarySizeBytes)}`);
    }

    violations.push({
      relativePath,
      size: stats.size,
      reasons,
    });
  }

  if (violations.length === 0 || allowAssetCommit) {
    return;
  }

  console.error('');
  console.error('Blocked commit: staged asset files need an explicit override.');
  console.error('Set ALLOW_ASSET_COMMIT=1 only when the asset commit is intentional.');
  console.error('');

  for (const violation of violations) {
    console.error(
      `- ${violation.relativePath} (${toDisplaySize(violation.size)}): ${violation.reasons.join(', ')}`
    );
  }

  console.error('');
  process.exit(1);
}

main();
