#!/usr/bin/env bash

set -euo pipefail

usage() {
  cat <<'USAGE'
Usage:
  ./scripts/sync-transcripts.sh [options]

Description:
  Mirrors local AI CLI transcripts into a normalized local mirror and optionally
  syncs mirror data to/from a peer machine over ssh+rsync.

Options:
  --machine-id ID         Machine identifier for local mirror folder.
                          Default: short hostname
  --mirror-dir PATH       Local mirror root.
                          Default: $HOME/.teleportation/transcript-mirror
  --peer HOST             Peer in ssh format (user@host or host).
  --peer-mirror-dir PATH  Peer mirror root path.
                          Default: same as --mirror-dir
  --mode MODE             Sync mode: local|push|pull|bidirectional
                          Default: local
  --dry-run               Print actions without writing files.
  -h, --help              Show this help text.

Examples:
  ./scripts/sync-transcripts.sh
  ./scripts/sync-transcripts.sh --machine-id mac-mini
  ./scripts/sync-transcripts.sh --peer user@laptop.local --mode push
  ./scripts/sync-transcripts.sh --peer user@mini.local --mode bidirectional
USAGE
}

require_cmd() {
  if ! command -v "$1" >/dev/null 2>&1; then
    echo "Error: required command not found: $1" >&2
    exit 1
  fi
}

human_bytes() {
  local bytes="$1"
  local units=(B KB MB GB TB)
  local i=0
  local value="$bytes"

  while [ "$value" -ge 1024 ] && [ "$i" -lt 4 ]; do
    value=$((value / 1024))
    i=$((i + 1))
  done

  printf "%s %s" "$value" "${units[$i]}"
}

copy_matches() {
  local base_dir="$1"
  local match_type="$2"
  local match_value="$3"
  local dest_dir="$4"
  local dry_run="$5"

  local count=0
  local total_bytes=0

  if [ ! -d "$base_dir" ]; then
    printf "%s\t%s\n" "0" "0"
    return 0
  fi

  local -a find_cmd=(find . -type f)
  case "$match_type" in
    name)
      find_cmd+=(-name "$match_value")
      ;;
    path)
      find_cmd+=(-path "$match_value")
      ;;
    *)
      echo "Error: unsupported match type '$match_type'" >&2
      return 1
      ;;
  esac

  while IFS= read -r -d '' rel_path; do
    rel_path="${rel_path#./}"
    local src="$base_dir/$rel_path"
    local dest="$dest_dir/$rel_path"
    local dest_parent
    dest_parent="$(dirname "$dest")"

    local file_size
    file_size="$(stat -f '%z' "$src" 2>/dev/null || stat -c '%s' "$src" 2>/dev/null || echo 0)"

    if [ "$dry_run" = "true" ]; then
      echo "[dry-run] copy $src -> $dest" >&2
    else
      mkdir -p "$dest_parent"
      rsync -a "$src" "$dest"
    fi

    count=$((count + 1))
    total_bytes=$((total_bytes + file_size))
  done < <(cd "$base_dir" && "${find_cmd[@]}" -print0)

  printf "%s\t%s\n" "$count" "$total_bytes"
}

sync_to_peer() {
  local mode="$1"
  local local_root="$2"
  local peer="$3"
  local peer_root="$4"
  local dry_run="$5"

  if [ -z "$peer" ]; then
    echo "Error: --peer is required for mode '$mode'" >&2
    exit 1
  fi

  if [[ "$peer" == -* ]] || [[ ! "$peer" =~ ^([A-Za-z0-9._-]+@)?[A-Za-z0-9._-]+$ ]]; then
    echo "Error: invalid --peer value '$peer'" >&2
    exit 1
  fi

  require_cmd ssh
  require_cmd rsync

  local rsync_flags=(-az --update)
  if [ "$dry_run" = "true" ]; then
    rsync_flags+=(--dry-run)
  fi

  if [ "$mode" = "push" ] || [ "$mode" = "bidirectional" ]; then
    if [[ "$peer_root" == "~/"* ]]; then
      ssh "$peer" "mkdir -p $peer_root"
    else
      ssh "$peer" "mkdir -p '$peer_root'"
    fi
    echo "Sync push: $local_root/ -> $peer:$peer_root/"
    rsync "${rsync_flags[@]}" "$local_root/" "$peer:$peer_root/"
  fi

  if [ "$mode" = "pull" ] || [ "$mode" = "bidirectional" ]; then
    mkdir -p "$local_root"
    echo "Sync pull: $peer:$peer_root/ -> $local_root/"
    rsync "${rsync_flags[@]}" "$peer:$peer_root/" "$local_root/"
  fi
}

main() {
  local machine_id
  machine_id="$(hostname -s 2>/dev/null || hostname)"
  local mirror_dir="${HOME}/.teleportation/transcript-mirror"
  local peer=""
  local peer_mirror_dir=""
  local mode="local"
  local dry_run="false"

  while [ "$#" -gt 0 ]; do
    case "$1" in
      --machine-id)
        machine_id="$2"
        shift 2
        ;;
      --mirror-dir)
        mirror_dir="$2"
        shift 2
        ;;
      --peer)
        peer="$2"
        shift 2
        ;;
      --peer-mirror-dir)
        peer_mirror_dir="$2"
        shift 2
        ;;
      --mode)
        mode="$2"
        shift 2
        ;;
      --dry-run)
        dry_run="true"
        shift
        ;;
      -h|--help)
        usage
        exit 0
        ;;
      *)
        echo "Error: unknown option '$1'" >&2
        usage
        exit 1
        ;;
    esac
  done

  case "$mode" in
    local|push|pull|bidirectional) ;;
    *)
      echo "Error: invalid --mode '$mode' (expected local|push|pull|bidirectional)" >&2
      exit 1
      ;;
  esac

  if [ -z "$peer_mirror_dir" ]; then
    peer_mirror_dir="$mirror_dir"
  fi

  require_cmd find
  require_cmd rsync

  local machine_root="$mirror_dir/$machine_id"
  local claude_dest="$machine_root/claude"
  local codex_dest="$machine_root/codex"
  local cursor_dest="$machine_root/cursor"
  local gemini_dest="$machine_root/gemini"

  local claude_src="${HOME}/.claude/projects"
  local codex_src="${HOME}/.codex/sessions"
  local cursor_src="${HOME}/.cursor/projects"
  local gemini_src="${HOME}/.gemini/tmp"

  if [ "$dry_run" = "false" ]; then
    mkdir -p "$claude_dest" "$codex_dest" "$cursor_dest" "$gemini_dest"
  fi

  echo "Transcript mirror"
  echo "  machine:   $machine_id"
  echo "  mirror:    $mirror_dir"
  echo "  mode:      $mode"
  [ -n "$peer" ] && echo "  peer:      $peer"
  echo ""

  local claude_out codex_out cursor_out gemini_out
  claude_out="$(copy_matches "$claude_src" "name" "*.jsonl" "$claude_dest" "$dry_run")"
  codex_out="$(copy_matches "$codex_src" "name" "*.jsonl" "$codex_dest" "$dry_run")"
  cursor_out="$(copy_matches "$cursor_src" "path" "*/agent-transcripts/*.txt" "$cursor_dest" "$dry_run")"
  gemini_out="$(copy_matches "$gemini_src" "path" "*/chats/session-*.json" "$gemini_dest" "$dry_run")"

  local claude_count codex_count cursor_count gemini_count
  local claude_bytes codex_bytes cursor_bytes gemini_bytes

  claude_count="$(echo "$claude_out" | cut -f1)"
  claude_bytes="$(echo "$claude_out" | cut -f2)"
  codex_count="$(echo "$codex_out" | cut -f1)"
  codex_bytes="$(echo "$codex_out" | cut -f2)"
  cursor_count="$(echo "$cursor_out" | cut -f1)"
  cursor_bytes="$(echo "$cursor_out" | cut -f2)"
  gemini_count="$(echo "$gemini_out" | cut -f1)"
  gemini_bytes="$(echo "$gemini_out" | cut -f2)"

  local total_count total_bytes
  total_count=$((claude_count + codex_count + cursor_count + gemini_count))
  total_bytes=$((claude_bytes + codex_bytes + cursor_bytes + gemini_bytes))

  echo "Local mirror summary (copied this run):"
  printf "  %-8s %6d files  %12d bytes\n" "claude" "$claude_count" "$claude_bytes"
  printf "  %-8s %6d files  %12d bytes\n" "codex" "$codex_count" "$codex_bytes"
  printf "  %-8s %6d files  %12d bytes\n" "cursor" "$cursor_count" "$cursor_bytes"
  printf "  %-8s %6d files  %12d bytes\n" "gemini" "$gemini_count" "$gemini_bytes"
  printf "  %-8s %6d files  %12d bytes (%s)\n" "total" "$total_count" "$total_bytes" "$(human_bytes "$total_bytes")"
  echo ""

  if [ "$mode" != "local" ]; then
    sync_to_peer "$mode" "$mirror_dir" "$peer" "$peer_mirror_dir" "$dry_run"
    echo ""
    echo "Peer sync complete."
  fi
}

main "$@"
