#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

# ╭─────────────────────────────────────╮
# │          gitfield-local             │
# ╰─────────────────────────────────────╮
# Manages a local bare Git repository as a sacred push target for redundancy.
# Commands: configure, status, push
# Creates and maintains a bare repository in ~/git-local-repos/git-sigil.git
# Generates metadata in .gitfield/local.sigil.md and updates .gitfield/push_log.json

# ╭─────────────────────────────────────╮
# │          CONFIGURATION              │
# ╰─────────────────────────────────────╮
REPO_PATH=$(git rev-parse --show-toplevel 2>/dev/null) || { echo -e "\e[1;31m[ERROR]\e[0m Not inside a Git repository" >&2; exit 1; }
LOCAL_REPO="$HOME/git-local-repos/git-sigil.git"
METADATA_DIR="$REPO_PATH/.gitfield"
METADATA_FILE="$METADATA_DIR/local.sigil.md"
PUSH_LOG="$METADATA_DIR/push_log.json"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
SCRIPT_VERSION="1.0"

# ╭─────────────────────────────────────╮
# │           LOGGING UTILS             │
# ╰─────────────────────────────────────╮
info()  { echo -e "\e[1;34m[INFO]\e[0m $*" >&2; }
warn()  { echo -e "\e[1;33m[WARN]\e[0m $*" >&2; }
error() { echo -e "\e[1;31m[ERROR]\e[0m $*" >&2; exit 1; }

# ╭─────────────────────────────────────╮
# │         SELF-HEALING CHECKS         │
# ╰─────────────────────────────────────╮
self_heal() {
    info "Running self-healing checks..."

    # Check if Git is installed
    if ! command -v git >/dev/null 2>&1; then
        error "Git is not installed. Please install Git and try again."
    fi

    # Ensure working repository is valid
    if ! git -C "$REPO_PATH" rev-parse --git-dir >/dev/null 2>&1; then
        error "Invalid Git repository at $REPO_PATH"
    fi

    # Create metadata directory if missing
    if [[ ! -d "$METADATA_DIR" ]]; then
        info "Creating metadata directory: $METADATA_DIR"
        mkdir -p "$METADATA_DIR" || error "Failed to create $METADATA_DIR"
    fi

    # Create push log if missing
    if [[ ! -f "$PUSH_LOG" ]]; then
        info "Creating push log: $PUSH_LOG"
        echo "{}" > "$PUSH_LOG" || error "Failed to create $PUSH_LOG"
    fi

    # Ensure local bare repository exists
    if [[ ! -d "$LOCAL_REPO" ]]; then
        info "Creating local bare repository: $LOCAL_REPO"
        mkdir -p "$LOCAL_REPO" || error "Failed to create $LOCAL_REPO"
        git init --bare "$LOCAL_REPO" || error "Failed to initialize bare repository"
    fi

    # Verify local repository is a valid bare repo
    if ! git -C "$LOCAL_REPO" rev-parse --is-bare-repository >/dev/null 2>&1; then
        warn "Local repository $LOCAL_REPO is not a valid bare repository. Reinitializing..."
        rm -rf "$LOCAL_REPO" || error "Failed to remove invalid $LOCAL_REPO"
        mkdir -p "$LOCAL_REPO" || error "Failed to create $LOCAL_REPO"
        git init --bare "$LOCAL_REPO" || error "Failed to reinitialize bare repository"
    fi

    # Ensure permissions are correct
    chmod -R u+rwX "$LOCAL_REPO" || warn "Failed to set permissions on $LOCAL_REPO"
}

# ╭─────────────────────────────────────╮
# │         CONFIGURE REMOTE            │
# ╰─────────────────────────────────────╮
configure() {
    info "Configuring local remote..."

    # Check if 'local' remote exists
    if git -C "$REPO_PATH" remote | grep -q '^local$'; then
        info "Local remote already exists. Verifying URL..."
        current_url=$(git -C "$REPO_PATH" remote get-url local)
        if [[ "$current_url" != "file://$LOCAL_REPO" ]]; then
            warn "Local remote URL is incorrect ($current_url). Updating to file://$LOCAL_REPO"
            git -C "$REPO_PATH" remote set-url local "file://$LOCAL_REPO" || error "Failed to update local remote URL"
        fi
    else
        info "Adding local remote: file://$LOCAL_REPO"
        git -C "$REPO_PATH" remote add local "file://$LOCAL_REPO" || error "Failed to add local remote"
    fi

    # Set upstream for current branch if not set
    current_branch=$(git -C "$REPO_PATH" rev-parse --abbrev-ref HEAD)
    if ! git -C "$REPO_PATH" rev-parse --abbrev-ref --symbolic-full-name "@{u}" >/dev/null 2>&1; then
        info "Setting upstream for $current_branch to local/$current_branch"
        git -C "$REPO_PATH" push --set-upstream local "$current_branch" || error "Failed to set upstream"
    fi

    info "Local remote configured successfully."
}

# ╭─────────────────────────────────────╮
# │         STATUS CHECK                │
# ╰─────────────────────────────────────╮
status() {
    info "Checking local repository status..."

    # Verify local bare repository
    if [[ -d "$LOCAL_REPO" && $(git -C "$LOCAL_REPO" rev-parse --is-bare-repository) == "true" ]]; then
        info "Local bare repository: $LOCAL_REPO"
        latest_commit=$(git -C "$LOCAL_REPO" log -1 --format="%h %s (%cr)" 2>/dev/null || echo "No commits")
        info "Latest commit: $latest_commit"
    else
        warn "Local bare repository not found or invalid: $LOCAL_REPO"
    fi

    # Check remote configuration
    if git -C "$REPO_PATH" remote | grep -q '^local$'; then
        remote_url=$(git -C "$REPO_PATH" remote get-url local)
        info "Local remote URL: $remote_url"
    else
        warn "Local remote not configured."
    fi

    # Check working repository status
    info "Working repository: $REPO_PATH"
    git -C "$REPO_PATH" status --short
}

# ╭─────────────────────────────────────╮
# │         PUSH TO LOCAL               │
# ╰─────────────────────────────────────╮
push() {
    info "Pushing to local bare repository..."

    # Ensure remote is configured
    if ! git -C "$REPO_PATH" remote | grep -q '^local$'; then
        warn "Local remote not configured. Running configure..."
        configure
    fi

    # Get current branch
    current_branch=$(git -C "$REPO_PATH" rev-parse --abbrev-ref HEAD)

    # Push to local remote
    if git -C "$REPO_PATH" push local "$current_branch"; then
        info "Successfully pushed to local/$current_branch"
    else
        warn "Push failed. Attempting to recover..."
        configure
        git -C "$REPO_PATH" push local "$current_branch" || error "Failed to push to local/$current_branch after recovery"
    fi

    # Update metadata
    update_metadata
}

# ╭─────────────────────────────────────╮
# │         UPDATE METADATA             │
# ╰─────────────────────────────────────╮
update_metadata() {
    info "Updating metadata in $METADATA_FILE and $PUSH_LOG..."

    # Get repository details
    current_branch=$(git -C "$REPO_PATH" rev-parse --abbrev-ref HEAD)
    latest_commit=$(git -C "$REPO_PATH" log -1 --format="%h" 2>/dev/null || echo "Unknown")
    commit_message=$(git -C "$REPO_PATH" log -1 --format="%s" 2>/dev/null || echo "Unknown")
    repo_name=$(basename "$REPO_PATH")

    # Generate .gitfield/local.sigil.md
    cat << EOF > "$METADATA_FILE"
# Local Repository Metadata
- **Repository**: $repo_name
- **Local Remote Path**: file://$LOCAL_REPO
- **Branch**: $current_branch
- **Latest Commit**: $latest_commit
- **Commit Message**: $commit_message
- **Last Updated**: $TIMESTAMP
- **Script Version**: $SCRIPT_VERSION
EOF

    if [[ -f "$METADATA_FILE" ]]; then
        info "Generated metadata: $METADATA_FILE"
    else
        warn "Failed to generate $METADATA_FILE"
    fi

    # Update push_log.json
    if command -v jq >/dev/null 2>&1; then
        jq --arg ts "$TIMESTAMP" \
           --arg branch "$current_branch" \
           --arg commit "$latest_commit" \
           --arg msg "$commit_message" \
           '.local += [{"timestamp": $ts, "branch": $branch, "commit": $commit, "message": $msg}]' \
           "$PUSH_LOG" > "$PUSH_LOG.tmp" && mv "$PUSH_LOG.tmp" "$PUSH_LOG" || warn "Failed to update $PUSH_LOG"
        info "Updated push log: $PUSH_LOG"
    else
        warn "jq not installed. Skipping $PUSH_LOG update."
    fi
}

# ╭─────────────────────────────────────╮
# │            MAIN EXECUTION           │
# ╰─────────────────────────────────────╮
main() {
    self_heal

    case "${1:-status}" in
        configure)
            configure
            ;;
        status)
            status
            ;;
        push)
            push
            ;;
        *)
            error "Usage: $0 {configure|status|push}"
            ;;
    esac
}

main "$@"
