#!/bin/bash
#
# Workspace Reconciliation - Push all changed repos
#
# Detects changes across all workspace git repos and pushes them.
# Pushing codebase triggers the release/deploy pipeline.
#
# Usage:
#   ./workflow/reconcile                    # Show status only
#   ./workflow/reconcile --push             # Push all changed repos
#   ./workflow/reconcile --push --deploy    # Push + trigger deploy
#   ./workflow/reconcile --commit "msg"     # Commit uncommitted + push
#
# Repos managed:
#   codebase/   → lilith-platform (triggers CI/CD via post-push hook)
#   project/    → lilith-platform-project
#   tooling/    → lilith-platform-tooling
#   docs/       → lilith-platform-docs
#
# Note: releases/ was removed - releases branch now lives in codebase repo
#

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# workflow/ is at workspace root level (symlink to tooling/workflow)
# So we go up one level from workflow/ to get workspace root
WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'

# Logging
log_header() { echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"; echo -e "${CYAN}  $1${NC}"; echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"; }
log_step()   { echo -e "\n${BLUE}▶${NC} $1"; }
log_info()   { echo -e "${CYAN}  ℹ${NC} $1"; }
log_success(){ echo -e "${GREEN}  ✓${NC} $1"; }
log_warn()   { echo -e "${YELLOW}  ⚠${NC} $1"; }
log_error()  { echo -e "${RED}  ✗${NC} $1"; }

# All workspace repos
REPOS=(
    "codebase"
    "project"
    "tooling"
    "docs"
)

# Options
DO_PUSH=false
DO_DEPLOY=false
COMMIT_MSG=""
DRY_RUN=false

# Parse arguments
while [[ $# -gt 0 ]]; do
    case "$1" in
        --push)
            DO_PUSH=true
            shift
            ;;
        --deploy)
            DO_DEPLOY=true
            DO_PUSH=true  # deploy implies push
            shift
            ;;
        --commit)
            COMMIT_MSG="$2"
            DO_PUSH=true  # commit implies push
            shift 2
            ;;
        --dry-run)
            DRY_RUN=true
            shift
            ;;
        --help|-h)
            echo "Usage: ./workflow/reconcile [options]"
            echo ""
            echo "Options:"
            echo "  --push              Push all repos with unpushed commits"
            echo "  --deploy            Push + run deploy pipeline for changed components"
            echo "  --commit \"msg\"      Commit all uncommitted changes with message, then push"
            echo "  --dry-run           Show what would happen without doing it"
            echo "  --help              Show this help"
            echo ""
            echo "Repos managed:"
            echo "  codebase/   Main source code (triggers CI/CD via post-push hook)"
            echo "  project/    Planning, handoffs, priorities"
            echo "  tooling/    Claude config, workflow scripts"
            echo "  docs/       Documentation"
            echo ""
            echo "Note: releases branch lives in codebase repo (no separate releases/ dir)"
            exit 0
            ;;
        *)
            log_error "Unknown option: $1"
            exit 1
            ;;
    esac
done

# =============================================================================
# REPO STATUS FUNCTIONS
# =============================================================================

# Check if repo has uncommitted changes
has_uncommitted() {
    local repo="$1"
    [[ -n $(git -C "$WORKSPACE_ROOT/$repo" status --porcelain 2>/dev/null) ]]
}

# Check if repo has unpushed commits
has_unpushed() {
    local repo="$1"
    local branch
    branch=$(git -C "$WORKSPACE_ROOT/$repo" rev-parse --abbrev-ref HEAD 2>/dev/null)
    local ahead
    ahead=$(git -C "$WORKSPACE_ROOT/$repo" rev-list --count "origin/$branch..HEAD" 2>/dev/null || echo "0")
    [[ "$ahead" -gt 0 ]]
}

# Get count of uncommitted changes
uncommitted_count() {
    local repo="$1"
    git -C "$WORKSPACE_ROOT/$repo" status --porcelain 2>/dev/null | wc -l
}

# Get count of unpushed commits
unpushed_count() {
    local repo="$1"
    local branch
    branch=$(git -C "$WORKSPACE_ROOT/$repo" rev-parse --abbrev-ref HEAD 2>/dev/null)
    git -C "$WORKSPACE_ROOT/$repo" rev-list --count "origin/$branch..HEAD" 2>/dev/null || echo "0"
}

# Get current branch
current_branch() {
    local repo="$1"
    git -C "$WORKSPACE_ROOT/$repo" rev-parse --abbrev-ref HEAD 2>/dev/null
}

# =============================================================================
# STATUS DISPLAY
# =============================================================================

show_status() {
    log_header "Workspace Reconciliation Status"

    local total_uncommitted=0
    local total_unpushed=0
    local repos_with_changes=()

    for repo in "${REPOS[@]}"; do
        local repo_path="$WORKSPACE_ROOT/$repo"

        if [[ ! -d "$repo_path/.git" ]]; then
            log_warn "$repo: not a git repo"
            continue
        fi

        local branch=$(current_branch "$repo")
        local uncommitted=$(uncommitted_count "$repo")
        local unpushed=$(unpushed_count "$repo")

        # Build status line
        local status_parts=()
        if [[ "$uncommitted" -gt 0 ]]; then
            status_parts+=("${YELLOW}$uncommitted uncommitted${NC}")
            ((total_uncommitted += uncommitted))
        fi
        if [[ "$unpushed" -gt 0 ]]; then
            status_parts+=("${BLUE}$unpushed unpushed${NC}")
            ((total_unpushed += unpushed))
        fi

        if [[ ${#status_parts[@]} -gt 0 ]]; then
            repos_with_changes+=("$repo")
            local status_str=$(IFS=', '; echo "${status_parts[*]}")
            echo -e "  ${BOLD}$repo${NC} ($branch): $status_str"
        else
            echo -e "  ${GREEN}$repo${NC} ($branch): clean"
        fi
    done

    echo ""

    if [[ ${#repos_with_changes[@]} -eq 0 ]]; then
        log_success "All repos are clean and pushed"
        return 0
    fi

    log_info "Summary: $total_uncommitted uncommitted, $total_unpushed unpushed across ${#repos_with_changes[@]} repo(s)"

    if [[ "$total_uncommitted" -gt 0 && -z "$COMMIT_MSG" ]]; then
        echo ""
        log_warn "Uncommitted changes detected. Use --commit \"msg\" to commit first."
    fi

    return 1
}

# =============================================================================
# COMMIT ALL REPOS
# =============================================================================

commit_all() {
    local msg="$1"

    log_step "Committing all uncommitted changes..."

    for repo in "${REPOS[@]}"; do
        if has_uncommitted "$repo"; then
            local count=$(uncommitted_count "$repo")

            if [[ "$DRY_RUN" == "true" ]]; then
                log_info "[DRY RUN] Would commit $count changes in $repo"
                continue
            fi

            log_info "Committing $count changes in $repo..."

            cd "$WORKSPACE_ROOT/$repo"
            git add -A
            git commit -m "$msg

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>"

            log_success "$repo committed"
        fi
    done
}

# =============================================================================
# PUSH ALL REPOS
# =============================================================================

push_all() {
    log_step "Pushing all repos with changes..."

    local pushed_codebase=false
    local pushed_repos=()

    for repo in "${REPOS[@]}"; do
        # Check for unpushed commits (including just-committed ones)
        if has_unpushed "$repo"; then
            local count=$(unpushed_count "$repo")
            local branch=$(current_branch "$repo")

            if [[ "$DRY_RUN" == "true" ]]; then
                log_info "[DRY RUN] Would push $count commits from $repo ($branch)"
                continue
            fi

            log_info "Pushing $count commits from $repo ($branch)..."

            cd "$WORKSPACE_ROOT/$repo"
            git push origin "$branch"

            log_success "$repo pushed"
            pushed_repos+=("$repo")

            if [[ "$repo" == "codebase" ]]; then
                pushed_codebase=true
            fi
        fi
    done

    if [[ ${#pushed_repos[@]} -eq 0 ]]; then
        log_info "No repos needed pushing"
    else
        log_success "Pushed ${#pushed_repos[@]} repo(s): ${pushed_repos[*]}"
    fi

    # Return whether codebase was pushed (for deploy trigger)
    $pushed_codebase
}

# =============================================================================
# DEPLOY PIPELINE
# =============================================================================

trigger_deploy() {
    log_step "Triggering deploy pipeline..."

    local deploy_script="$WORKSPACE_ROOT/codebase/infrastructure/scripts/rectify-deploy.sh"

    if [[ ! -x "$deploy_script" ]]; then
        log_error "Deploy script not found: $deploy_script"
        return 1
    fi

    if [[ "$DRY_RUN" == "true" ]]; then
        log_info "[DRY RUN] Would run: $deploy_script"
        return 0
    fi

    cd "$WORKSPACE_ROOT/codebase"
    "$deploy_script"
}

# =============================================================================
# MAIN
# =============================================================================

main() {
    cd "$WORKSPACE_ROOT"

    echo -e "${CYAN}"
    echo "╔══════════════════════════════════════════════════════════════╗"
    echo "║           Lilith Platform - Workspace Reconciliation         ║"
    echo "╚══════════════════════════════════════════════════════════════╝"
    echo -e "${NC}"

    if [[ "$DRY_RUN" == "true" ]]; then
        log_warn "DRY-RUN MODE - No changes will be made"
    fi

    # Always show status first
    show_status || true

    # If just checking status, we're done
    if [[ "$DO_PUSH" != "true" ]]; then
        echo ""
        log_info "Use --push to push, --commit \"msg\" to commit+push, --deploy to also deploy"
        return 0
    fi

    # Commit if message provided
    if [[ -n "$COMMIT_MSG" ]]; then
        commit_all "$COMMIT_MSG"
    fi

    # Push all repos
    local codebase_pushed=false
    if push_all; then
        codebase_pushed=true
    fi

    # Deploy if requested and codebase was pushed
    if [[ "$DO_DEPLOY" == "true" ]]; then
        if [[ "$codebase_pushed" == "true" ]] || [[ "$DRY_RUN" == "true" ]]; then
            trigger_deploy
        else
            log_info "Codebase not pushed, skipping deploy"
        fi
    fi

    echo ""
    log_header "Reconciliation Complete"

    if [[ "$DRY_RUN" != "true" ]]; then
        show_status || true
    fi
}

main "$@"
