#!/usr/bin/env bash

set -euo pipefail

usage() {
  cat <<'USAGE'
Usage:
  ./create-chatgpt-archive.sh [archive.zip]
  ./create-chatgpt-archive.sh --list

Creates a zip archive with project files useful for code analysis.

Environment:
  MAX_FILE_SIZE_BYTES  Per-file size limit. Default: 1048576

The script includes source, configuration, build metadata, CI, Helm and docs.
It excludes build outputs, logs, local caches, IDE files, archives, binaries and env files.
USAGE
}

mode="create"
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
  usage
  exit 0
fi

if [[ "${1:-}" == "--list" ]]; then
  mode="list"
  shift
fi

if (( $# > 1 )); then
  usage >&2
  exit 2
fi

project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
archive_arg="${1:-pcp-chatgpt-context.zip}"
max_file_size_bytes="${MAX_FILE_SIZE_BYTES:-1048576}"

if [[ "$archive_arg" = /* ]]; then
  archive_path="$archive_arg"
else
  archive_path="$project_root/$archive_arg"
fi

include_paths=(
  "AGENTS.md"
  "README.md"
  "HELP.md"
  ".dockerignore"
  ".gitattributes"
  ".gitignore"
  ".gitlab-ci.yml"
  "build.gradle.kts"
  "settings.gradle.kts"
  "gradle.properties"
  "gradlew"
  "gradlew.bat"
  "create-chatgpt-archive.sh"
  "changeLogsDynamicSlots.md"
  ".gitlab"
  "config-repo"
  "docs"
  "gradle"
  "helm"
  "libs"
  "schemes"
  "services"
)

should_skip_path() {
  local path="$1"

  case "$path" in
    .git|.git/*|.gradle|.gradle/*|.gradle-local|.gradle-local/*|.idea|.idea/*|.kotlin|.kotlin/*|.vscode|.vscode/*)
      return 0
      ;;
    build|build/*|logs|logs/*|out|out/*|bin|bin/*|target|target/*|node_modules|node_modules/*)
      return 0
      ;;
    */build|*/build/*|*/logs|*/logs/*|*/out|*/out/*|*/bin|*/bin/*|*/target|*/target/*|*/node_modules|*/node_modules/*)
      return 0
      ;;
  esac

  case "$path" in
    .env|.env.*|*/.env|*/.env.*)
      return 0
      ;;
    *.log|*.tmp|*.bak|*.orig|*.class|*.jar|*.war|*.ear|*.zip|*.tar|*.tgz|*.tar.gz|*.gz|*.7z|*.rar)
      return 0
      ;;
    *.png|*.jpg|*.jpeg|*.gif|*.webp|*.ico|*.pdf|*.doc|*.docx|*.xls|*.xlsx|*.ppt|*.pptx)
      return 0
      ;;
    *.keystore|*.jks|*.p12|*.pem|*.key|*.crt)
      return 0
      ;;
  esac

  return 1
}

is_allowed_file() {
  local path="$1"

  case "$path" in
    AGENTS.md|README.md|HELP.md|.dockerignore|.gitattributes|.gitignore|.gitlab-ci.yml)
      return 0
      ;;
    build.gradle.kts|settings.gradle.kts|gradle.properties|gradlew|gradlew.bat|create-chatgpt-archive.sh|changeLogsDynamicSlots.md)
      return 0
      ;;
    Dockerfile|Makefile|Jenkinsfile)
      return 0
      ;;
    *.kt|*.kts|*.java|*.groovy|*.xml|*.yaml|*.yml|*.properties|*.json|*.toml|*.md|*.txt)
      return 0
      ;;
    *.sql|*.graphql|*.graphqls|*.proto|*.bpmn|*.dmn|*.tpl|*.sh)
      return 0
      ;;
    *.html|*.css|*.scss|*.js|*.jsx|*.ts|*.tsx|*.vue)
      return 0
      ;;
  esac

  return 1
}

manifest_file="$(mktemp)"
trap 'rm -f "$manifest_file"' EXIT

collect_files() {
  local root_path
  local relative_path
  local file_size

  for root_path in "${include_paths[@]}"; do
    if [[ ! -e "$project_root/$root_path" ]]; then
      continue
    fi

    if [[ -f "$project_root/$root_path" ]]; then
      printf '%s\n' "$root_path"
    else
      (cd "$project_root" && find "$root_path" -type f -print)
    fi
  done | while IFS= read -r relative_path; do
    relative_path="${relative_path#./}"

    if should_skip_path "$relative_path"; then
      continue
    fi

    if ! is_allowed_file "$relative_path"; then
      continue
    fi

    file_size="$(stat -c '%s' "$project_root/$relative_path")"
    if (( file_size > max_file_size_bytes )); then
      continue
    fi

    printf '%s\n' "$relative_path"
  done | sort -u
}

collect_files > "$manifest_file"

file_count="$(wc -l < "$manifest_file" | tr -d ' ')"
if (( file_count == 0 )); then
  echo "No files selected for archive." >&2
  exit 1
fi

if [[ "$mode" == "list" ]]; then
  cat "$manifest_file"
  echo
  echo "Selected files: $file_count" >&2
  exit 0
fi

if ! command -v zip >/dev/null 2>&1; then
  echo "zip command is required but was not found." >&2
  exit 1
fi

mkdir -p "$(dirname "$archive_path")"
rm -f "$archive_path"

(cd "$project_root" && zip -q -X "$archive_path" -@ < "$manifest_file")

archive_size="$(du -h "$archive_path" | awk '{print $1}')"
echo "Archive created: $archive_path"
echo "Selected files: $file_count"
echo "Archive size: $archive_size"
