#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FRAMEWORK_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
SOURCE_VERSION="unknown"
if [[ -f "${FRAMEWORK_ROOT}/VERSION" ]]; then
  SOURCE_VERSION="$(tr -d '[:space:]' < "${FRAMEWORK_ROOT}/VERSION")"
fi

STACK=""
WORKSPACE_ONLY=false
UPDATE_MODE=false
DRY_RUN=false
FORCE=false

COPIED_COUNT=0
SKIPPED_COUNT=0
CREATED_COUNT=0

VERSION_MARKER_PATH=".github/.edenred-copilot-framework-version"
STACK_MARKER_PATH=".github/.edenred-copilot-framework-stack"

usage() {
  cat <<USAGE
Usage:
  scripts/setup.sh --stack <stack> [--workspace] [--update] [--dry-run] [--force]

Flags:
  --stack <stack>   Stack to install (angular|react|java-spring|java-spring-adapter)
  --workspace       Install workspace layer only when --stack is omitted
  --update          Update shared files only
  --dry-run         Show actions without writing
  --force           Overwrite existing files without prompting
USAGE
}

log_info() { echo "[INFO] $*"; }
log_warn() { echo "[WARN] $*"; }
log_error() { echo "[ERROR] $*" >&2; }

is_valid_stack() {
  case "$1" in
    angular|react|java-spring|java-spring-adapter) return 0 ;;
    *) return 1 ;;
  esac
}

parse_args() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --stack)
        [[ $# -ge 2 ]] || { log_error "--stack requires a value"; usage; exit 1; }
        STACK="$2"
        shift 2
        ;;
      --workspace)
        WORKSPACE_ONLY=true
        shift
        ;;
      --update)
        UPDATE_MODE=true
        shift
        ;;
      --dry-run)
        DRY_RUN=true
        shift
        ;;
      --force)
        FORCE=true
        shift
        ;;
      -h|--help)
        usage
        exit 0
        ;;
      *)
        log_error "Unknown argument: $1"
        usage
        exit 1
        ;;
    esac
  done
}

validate_args() {
  if [[ -n "${STACK}" ]] && ! is_valid_stack "${STACK}"; then
    log_error "Invalid stack '${STACK}'. Valid: angular, react, java-spring, java-spring-adapter"
    exit 1
  fi

  if [[ -z "${STACK}" && "${WORKSPACE_ONLY}" == false && "${UPDATE_MODE}" == false ]]; then
    log_error "--stack is required unless --workspace or --update is used"
    exit 1
  fi

  if [[ "${UPDATE_MODE}" == true && -n "${STACK}" ]]; then
    log_warn "--stack is ignored in --update mode"
  fi
}

enforce_stack_switch_guard() {
  if [[ -z "${STACK}" || "${UPDATE_MODE}" == true ]]; then
    return
  fi

  if [[ ! -f "${STACK_MARKER_PATH}" ]]; then
    return
  fi

  local existing_stack
  existing_stack="$(tr -d '[:space:]' < "${STACK_MARKER_PATH}")"
  if [[ -z "${existing_stack}" || "${existing_stack}" == "${STACK}" ]]; then
    return
  fi

  if [[ "${FORCE}" != true ]]; then
    log_error "Detected existing stack '${existing_stack}', requested '${STACK}'."
    log_error "Use --force to intentionally switch stack and refresh scaffold files."
    exit 1
  fi

  log_warn "Switching stack from '${existing_stack}' to '${STACK}' because --force was provided."
}

preflight() {
  if [[ ! -d .git ]]; then
    log_error "Current directory is not a repository root (.git directory not found)"
    exit 1
  fi

  if [[ -n "$(git --no-pager status --porcelain)" ]]; then
    log_warn "Repository has uncommitted changes"
  fi

  local existing_version="none"
  if [[ -f .github/.edenred-copilot-framework-version ]]; then
    existing_version="$(tr -d '[:space:]' < .github/.edenred-copilot-framework-version)"
  elif [[ -f .github/instructions/contract-first.instructions.md ]]; then
    existing_version="unknown (framework files detected)"
  fi

  log_info "Source framework version: ${SOURCE_VERSION}"
  log_info "Detected installed framework version: ${existing_version}"
}

prompt_overwrite() {
  local src="$1"
  local dst="$2"

  if [[ "${FORCE}" == true ]]; then
    echo "overwrite"
    return
  fi

  if [[ "${DRY_RUN}" == true ]]; then
    echo "skip"
    return
  fi

  while true; do
    read -r -p "${dst} exists. [O]verwrite / [S]kip / [D]iff? " answer || {
      log_warn "No interactive input available. Skipping ${dst}."
      echo "skip"
      return
    }
    case "${answer}" in
      [Oo]|[Oo][Vv][Ee][Rr][Ww][Rr][Ii][Tt][Ee])
        echo "overwrite"
        return
        ;;
      [Ss]|[Ss][Kk][Ii][Pp])
        echo "skip"
        return
        ;;
      [Dd]|[Dd][Ii][Ff][Ff])
        git --no-pager diff --no-index -- "${dst}" "${src}" || true
        ;;
      *)
        echo "Please answer O, S, or D."
        ;;
    esac
  done
}

copy_file() {
  local src="$1"
  local dst="$2"

  if [[ ! -f "${src}" ]]; then
    log_warn "Source file not found, skipping: ${src}"
    return
  fi

  if [[ -f "${dst}" ]]; then
    if [[ "${UPDATE_MODE}" == true && "${FORCE}" != true ]]; then
      log_info "Skip ${dst}"
      SKIPPED_COUNT=$((SKIPPED_COUNT + 1))
      return
    fi

    local action
    action="$(prompt_overwrite "${src}" "${dst}")"
    if [[ "${action}" == "skip" ]]; then
      log_info "Skip ${dst}"
      SKIPPED_COUNT=$((SKIPPED_COUNT + 1))
      return
    fi
  fi

  if [[ "${DRY_RUN}" == true ]]; then
    echo "[DRY-RUN] copy ${src} -> ${dst}"
    COPIED_COUNT=$((COPIED_COUNT + 1))
    return
  fi

  mkdir -p "$(dirname "${dst}")"
  cp -p "${src}" "${dst}"
  log_info "Copied ${dst}"
  COPIED_COUNT=$((COPIED_COUNT + 1))
}

copy_tree_merge() {
  local src_root="$1"
  local dst_root="$2"

  if [[ ! -d "${src_root}" ]]; then
    log_warn "Source directory not found, skipping: ${src_root}"
    return
  fi

  while IFS= read -r -d '' src_file; do
    local rel
    rel="${src_file#${src_root}/}"
    copy_file "${src_file}" "${dst_root}/${rel}"
  done < <(find "${src_root}" -type f -print0)
}

copy_contract_agents_only() {
  local src_root="$1"
  local dst_root="$2"

  if [[ ! -d "${src_root}" ]]; then
    return
  fi

  while IFS= read -r -d '' src_file; do
    local rel
    rel="${src_file#${src_root}/}"
    copy_file "${src_file}" "${dst_root}/${rel}"
  done < <(find "${src_root}" -maxdepth 1 -type f -name 'contract-*.agent.md' -print0)
}

ensure_framework_gitignore() {
  local gitignore_path=".gitignore"
  local marker="# edenred-copilot-framework local artifacts"
  local -a entries=(
    ".ai-context/"
    ".ai-context/codemap/"
    ".ai-context/codemap/*.json"
    ".codegraph/"
    ".codegraph/**"
    "context/"
    ".github/*.md"
    ".github/AGENTS.md"
    ".github/copilot-instructions.md"
    ".github/instructions/"
    ".github/agents/"
    ".github/prompts/"
    ".github/skills/"
    ".github/.edenred-copilot-framework-version"
    ".github/.edenred-copilot-framework-stack"
    ".vscode/mcp.json"
    "docs/context/"
  )
  local changed=false

  if [[ "${DRY_RUN}" == true ]]; then
    echo "[DRY-RUN] ensure framework local artifact block in ${gitignore_path}"
    CREATED_COUNT=$((CREATED_COUNT + 1))
    return
  fi

  if [[ ! -f "${gitignore_path}" ]]; then
    : > "${gitignore_path}"
  fi

  if ! grep -Fq "${marker}" "${gitignore_path}"; then
    if [[ -s "${gitignore_path}" ]] && [[ "$(tail -c1 "${gitignore_path}" 2>/dev/null || true)" != $'\n' ]]; then
      printf "\n" >> "${gitignore_path}"
    fi
    printf "%s\n" "${marker}" >> "${gitignore_path}"
    changed=true
  fi

  for entry in "${entries[@]}"; do
    if ! grep -Fxq "${entry}" "${gitignore_path}"; then
      printf "%s\n" "${entry}" >> "${gitignore_path}"
      changed=true
    fi
  done

  if [[ "${changed}" == true ]]; then
    log_info "Ensured framework local artifact block in .gitignore"
    CREATED_COUNT=$((CREATED_COUNT + 1))
  else
    log_info "Framework local artifact block already up to date in .gitignore"
  fi
}

write_markers() {
  if [[ "${DRY_RUN}" == true ]]; then
    echo "[DRY-RUN] write ${VERSION_MARKER_PATH} (${SOURCE_VERSION})"
    if [[ -n "${STACK}" ]]; then
      echo "[DRY-RUN] write ${STACK_MARKER_PATH} (${STACK})"
    fi
    return
  fi

  mkdir -p "$(dirname "${VERSION_MARKER_PATH}")"
  printf "%s\n" "${SOURCE_VERSION}" > "${VERSION_MARKER_PATH}"
  log_info "Updated ${VERSION_MARKER_PATH} (${SOURCE_VERSION})"

  if [[ -n "${STACK}" ]]; then
    printf "%s\n" "${STACK}" > "${STACK_MARKER_PATH}"
    log_info "Updated ${STACK_MARKER_PATH} (${STACK})"
  fi
}

create_file_if_missing() {
  local target="$1"
  local template_source="$2"

  if [[ -f "${target}" ]]; then
    log_info "Exists ${target}"
    return
  fi

  if [[ "${DRY_RUN}" == true ]]; then
    echo "[DRY-RUN] create ${target}"
    CREATED_COUNT=$((CREATED_COUNT + 1))
    return
  fi

  mkdir -p "$(dirname "${target}")"
  cp -p "${template_source}" "${target}"
  log_info "Created ${target}"
  CREATED_COUNT=$((CREATED_COUNT + 1))
}

create_decisions_if_missing() {
  local target="docs/context/decisions.md"

  if [[ -f "${target}" ]]; then
    log_info "Exists ${target}"
    return
  fi

  if [[ "${DRY_RUN}" == true ]]; then
    echo "[DRY-RUN] create ${target}"
    CREATED_COUNT=$((CREATED_COUNT + 1))
    return
  fi

  mkdir -p "docs/context"
  cat > "${target}" <<'DECISIONS_TEMPLATE'
# Decisions Log

## YYYY-MM-DD — Decision title
- **Context**:
- **Decision**:
- **Consequences**:
DECISIONS_TEMPLATE
  log_info "Created ${target}"
  CREATED_COUNT=$((CREATED_COUNT + 1))
}

run_update_mode() {
  log_info "Running update mode (shared files only)"
  copy_tree_merge "${FRAMEWORK_ROOT}/workspace/.github/instructions" ".github/instructions"
  copy_tree_merge "${FRAMEWORK_ROOT}/workspace/.github/prompts" ".github/prompts"
  copy_contract_agents_only "${FRAMEWORK_ROOT}/workspace/.github/agents" ".github/agents"
  copy_tree_merge "${FRAMEWORK_ROOT}/workspace/.github/skills" ".github/skills"
}

run_workspace_layer() {
  log_info "Applying workspace layer"
  copy_tree_merge "${FRAMEWORK_ROOT}/workspace/.github" ".github"
}

run_stack_layer() {
  local stack="$1"
  log_info "Applying stack layer: ${stack}"
  copy_file "${FRAMEWORK_ROOT}/stacks/${stack}/AGENTS.md" ".github/AGENTS.md"
  copy_file "${FRAMEWORK_ROOT}/stacks/${stack}/copilot-instructions.md" ".github/copilot-instructions.md"
  copy_tree_merge "${FRAMEWORK_ROOT}/stacks/${stack}/agents" ".github/agents"
  copy_tree_merge "${FRAMEWORK_ROOT}/stacks/${stack}/prompts" ".github/prompts"
}

run_context_layer() {
  log_info "Applying context layer"
  copy_tree_merge "${FRAMEWORK_ROOT}/context-template" "context"
  ensure_framework_gitignore
}

run_docs_scaffold() {
  if [[ "${DRY_RUN}" == true ]]; then
    echo "[DRY-RUN] ensure docs/context"
  else
    mkdir -p "docs/context"
  fi

  create_file_if_missing "docs/context/project.md" "${FRAMEWORK_ROOT}/context-template/projects/_template.md"
  create_decisions_if_missing
  create_file_if_missing "docs/context/top-of-mind.md" "${FRAMEWORK_ROOT}/context-template/top-of-mind.md"
}

print_checklist() {
  echo "Framework installed. Now customize:"
  echo "- .github/AGENTS.md — fill in §2 stack versions and §3 design tokens"
  echo "- .github/AGENTS.md — fill in §5 module boundaries from your Architecture-Blueprint"
  echo "- docs/shared-ai-context-platform-v2.md — review the local-first platform baseline before implementation"
  echo "- context/top-of-mind.md — set your current focus"
  echo "- Run /start-session in Copilot to verify setup"
}

print_summary() {
  echo "Summary: copied=${COPIED_COUNT}, created=${CREATED_COUNT}, skipped=${SKIPPED_COUNT}, dry-run=${DRY_RUN}"
}

main() {
  parse_args "$@"
  validate_args
  preflight
  enforce_stack_switch_guard

  if [[ "${UPDATE_MODE}" == true ]]; then
    run_update_mode
    print_summary
    return
  fi

  run_workspace_layer

  if [[ -n "${STACK}" ]]; then
    run_stack_layer "${STACK}"
  elif [[ "${WORKSPACE_ONLY}" == true ]]; then
    log_info "Workspace-only mode: stack layer skipped"
  fi

  run_context_layer
  run_docs_scaffold
  write_markers
  print_summary
  print_checklist
}

main "$@"
