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

ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
REFERENCE_DIR="$ROOT/reference"
THEMES_DIR="$ROOT/themes"
DIST_DIR="$ROOT/dist"
STATE_DIR="$ROOT/.theme-factory-state"
WORK_DIR="$ROOT/.theme-factory-work"
ZIP_TOOL="$ROOT/tools/theme_factory.py"
ASK_GPT="$ROOT/tools/ask-gpt-local"
PROMPT="$ROOT/PROMPT.md"
EXPECTED_ORIGIN='https://github.com/alexcodeplace/ThemeFactory'
MIN_LAUNCH_DELAY=55
MAX_LAUNCH_DELAY=85

fail() {
  printf 'theme-factory: %s\n' "$*" >&2
  exit 1
}

check_environment() {
  local command origin branch
  for command in git zip python3 flock shuf systemctl systemd-run; do
    command -v "$command" >/dev/null || fail "required command is missing: $command"
  done
  [[ -r "$PROMPT" ]] || fail "missing readable prompt: $PROMPT"
  [[ -x "$ASK_GPT" ]] || fail "helper is not executable: $ASK_GPT"
  [[ -x "$ZIP_TOOL" ]] || fail "helper is not executable: $ZIP_TOOL"
  [[ -d "$REFERENCE_DIR" ]] || fail "missing reference directory: $REFERENCE_DIR"
  branch=$(git -C "$ROOT" branch --show-current)
  [[ -n "$branch" && "$branch" != main ]] || fail "refusing to run in shared main checkout"
  origin=$(git -C "$ROOT" remote get-url origin)
  [[ "${origin%.git}" == "$EXPECTED_ORIGIN" ]] || fail "unexpected origin: $origin"
  git -C "$ROOT" diff --quiet || fail "tracked files are modified"
  git -C "$ROOT" diff --cached --quiet || fail "Git index contains staged changes"
  mkdir -p "$THEMES_DIR" "$DIST_DIR" "$STATE_DIR/logs" "$WORK_DIR"
}

available_themes() {
  find "$REFERENCE_DIR" -mindepth 1 -maxdepth 1 -type d ! -name artifacts -printf '%f\0' | sort -z -f
}

select_themes() {
  local requested theme found
  mapfile -d '' available < <(available_themes)
  ((${#available[@]} > 0)) || fail "no reference themes found"
  selected=()
  if (($#)); then
    declare -A seen=()
    for requested in "$@"; do
      [[ -z "${seen[$requested]+x}" ]] || fail "theme requested more than once: $requested"
      seen[$requested]=1
      found=false
      for theme in "${available[@]}"; do
        if [[ "$theme" == "$requested" ]]; then
          selected+=("$theme")
          found=true
          break
        fi
      done
      $found || fail "unknown reference theme: $requested"
    done
  else
    selected=("${available[@]}")
  fi
}

write_generation_record() {
  python3 - "$1" "$2" "$3" <<'PY'
import json
import sys
from pathlib import Path

result = json.loads(Path(sys.argv[1]).read_text())
destination = Path(sys.argv[2])
theme = sys.argv[3]
(destination / "response.md").write_text(str(result.get("text", "")).rstrip() + "\n")
record = {
    "source_theme": theme,
    "conversation": result.get("conversation", ""),
    "generated_files": [Path(value).name for value in result.get("files", [])],
}
(destination / "generation.json").write_text(json.dumps(record, indent=2) + "\n")
PY
}

is_complete() {
  local theme=$1
  [[ -d "$THEMES_DIR/$theme" && -f "$DIST_DIR/$theme.zip" ]] || return 1
  python3 "$ZIP_TOOL" audit "$THEMES_DIR/$theme" "$DIST_DIR/$theme.zip" >/dev/null
  [[ -n "$(git -C "$ROOT" ls-files -- "themes/$theme/")" ]] || return 1
  git -C "$ROOT" ls-files --error-unmatch "dist/$theme.zip" >/dev/null 2>&1
}

publish_theme() {
  local theme=$1 theme_work source_zip output_dir result_json generated_zip
  local publish_dir publish_zip root_name browser_state rc
  theme_work="$WORK_DIR/$theme"
  rm -rf -- "$theme_work"
  mkdir -p "$theme_work"
  source_zip="$theme_work/$theme.zip"
  output_dir="$theme_work/generated"
  result_json="$theme_work/result.json"
  publish_dir="$theme_work/publish"
  publish_zip="$theme_work/publish.zip"
  browser_state="$STATE_DIR/browser"
  mkdir -p -m 700 "$output_dir" "$browser_state"
  chmod 700 "$browser_state"

  printf 'Packing reference: %s\n' "$theme"
  (
    cd "$REFERENCE_DIR"
    zip -q -r "$source_zip" "$theme" -x '*/Thumbs.db' '*/.DS_Store' '*/.vscode/*'
  )

  printf 'Dispatching: %s\n' "$theme"
  exec {browser_lock}>"$STATE_DIR/browser.lock"
  flock -w 86400 "$browser_lock" || fail "timed out waiting for browser: $theme"
  set +e
  GPTBRIDGE_STATE_DIR="$browser_state" \
    SOLWEBD_TOKEN_FILE="$browser_state/no-daemon.token" \
    "$ASK_GPT" --mode headless --effort pro --json --timeout 10800 --out "$output_dir" -a "$source_zip" \
    < "$PROMPT" > "$result_json"
  rc=$?
  set -e
  flock -u "$browser_lock"
  ((rc == 0)) || fail "ask-gpt failed for $theme with exit $rc"

  generated_zip=$(python3 "$ZIP_TOOL" result "$result_json" "$output_dir")
  root_name=$(python3 "$ZIP_TOOL" install "$generated_zip" "$publish_dir")
  write_generation_record "$result_json" "$publish_dir" "$theme"
  python3 "$ZIP_TOOL" pack "$publish_dir" "$publish_zip" >/dev/null

  exec {publish_lock}>"$STATE_DIR/publish.lock"
  flock -w 86400 "$publish_lock" || fail "timed out waiting for publish queue: $theme"
  if is_complete "$theme"; then
    printf 'Already published by another worker: %s\n' "$theme"
    flock -u "$publish_lock"
    rm -rf -- "$theme_work"
    return 0
  fi
  [[ ! -e "$THEMES_DIR/$theme" ]] || fail "refusing to replace existing theme output: $theme"
  [[ ! -e "$DIST_DIR/$theme.zip" ]] || fail "refusing to replace existing dist archive: $theme"
  mv -- "$publish_dir" "$THEMES_DIR/$theme"
  mv -- "$publish_zip" "$DIST_DIR/$theme.zip"
  python3 "$ZIP_TOOL" audit "$THEMES_DIR/$theme" "$DIST_DIR/$theme.zip"

  git -C "$ROOT" diff --quiet || fail "tracked files changed outside generated output"
  git -C "$ROOT" diff --cached --quiet || fail "Git index contains staged changes"
  git -C "$ROOT" add -- "themes/$theme" "dist/$theme.zip"
  git -C "$ROOT" diff --cached --quiet && fail "no generated files staged for $theme"
  git -C "$ROOT" commit -m "Generate $theme theme"
  git -C "$ROOT" fetch origin main
  if ! git -C "$ROOT" merge-base --is-ancestor origin/main HEAD; then
    git -C "$ROOT" rebase origin/main
  fi
  git -C "$ROOT" push origin HEAD:main
  git -C "$ROOT" merge-base --is-ancestor HEAD origin/main \
    || fail "origin/main does not contain completed theme: $theme"
  flock -u "$publish_lock"
  rm -rf -- "$theme_work"
  printf 'Published: %s (%s)\n' "$theme" "$root_name"
}

run_worker() {
  (($# == 1)) || fail "worker requires exactly one theme"
  select_themes "$1"
  if is_complete "$1"; then
    printf 'Already complete: %s\n' "$1"
    return 0
  fi
  if [[ -e "$THEMES_DIR/$1" || -e "$DIST_DIR/$1.zip" ]]; then
    fail "partial or invalid published output requires inspection: $1"
  fi
  publish_theme "$1"
}

run_coordinator() {
  local theme delay index
  local -a pending=()
  systemctl --user is-active --quiet 'themefactory-worker-*.service' \
    && fail "legacy concurrent factory workers are active"
  exec {coordinator_lock}>"$STATE_DIR/coordinator.lock"
  flock -n "$coordinator_lock" || fail "another factory coordinator is active"
  select_themes "$@"
  for theme in "${selected[@]}"; do
    if is_complete "$theme"; then
      printf 'Already complete: %s\n' "$theme"
    else
      pending+=("$theme")
    fi
  done
  if ((${#pending[@]} == 0)); then
    printf 'All selected themes are already complete.\n'
    return 0
  fi

  for index in "${!pending[@]}"; do
    theme=${pending[$index]}
    run_worker "$theme"
    if ((index + 1 < ${#pending[@]})); then
      delay=$(shuf -i "$MIN_LAUNCH_DELAY-$MAX_LAUNCH_DELAY" -n 1)
      printf 'Next theme in %s seconds\n' "$delay"
      sleep "$delay"
    fi
  done
  printf 'All selected themes were published.\n'
}

launch_coordinator() {
  local unit=themefactory-coordinator
  systemctl --user is-active --quiet 'themefactory-worker-*.service' \
    && fail "legacy concurrent factory workers are active"
  if systemctl --user is-active --quiet "$unit.service"; then
    fail "theme factory coordinator is already active"
  fi
  systemctl --user reset-failed "$unit.service" >/dev/null 2>&1 || true
  systemd-run --user --quiet --collect --unit="$unit" \
    --working-directory="$ROOT" --setenv="PATH=$PATH" \
    --property="StandardOutput=append:$STATE_DIR/factory.log" \
    --property="StandardError=append:$STATE_DIR/factory.log" \
    "$0" --coordinator "$@"
  printf 'Theme factory started as %s.service; log=%s\n' "$unit" "$STATE_DIR/factory.log"
}

check_environment
case "${1:-}" in
  --coordinator)
    shift
    run_coordinator "$@"
    ;;
  *)
    launch_coordinator "$@"
    ;;
esac
