#!/bin/bash

# backup-env-files.sh - Backup .env files to private Gitea repository
# Author: Shell Repository
# Description: Securely backup and version control .env files from ~/docker/* directories

set -e

# Load the unified backup metrics library
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LIB_DIR="$SCRIPT_DIR/lib"
if [[ -f "$LIB_DIR/unified-backup-metrics.sh" ]]; then
    # shellcheck source=lib/unified-backup-metrics.sh
    source "$LIB_DIR/unified-backup-metrics.sh"
    METRICS_ENABLED=true
else
    echo "Warning: Unified backup metrics library not found at $LIB_DIR/unified-backup-metrics.sh"
    METRICS_ENABLED=false
fi

# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOCKER_DIR="$HOME/docker"
BACKUP_REPO_NAME="docker-env-backup"
BACKUP_DIR="$HOME/.env-backup"
LOG_FILE="$SCRIPT_DIR/logs/env-backup.log"

# Ensure logs directory exists
mkdir -p "$(dirname "$LOG_FILE")"

# Logging function
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# Display usage information
usage() {
    echo "Usage: $0 [OPTIONS]"
    echo ""
    echo "Backup .env files from ~/docker/* to private Gitea repository"
    echo ""
    echo "Options:"
    echo "  -h, --help              Show this help message"
    echo "  -i, --init              Initialize the backup repository"
    echo "  -f, --force             Force overwrite existing files"
    echo "  -d, --dry-run           Show what would be backed up without doing it"
    echo "  -r, --restore           Restore .env files from backup"
    echo "  -l, --list              List all .env files found"
    echo "  -g, --gitea-url URL     Set Gitea instance URL"
    echo "  -u, --username USER     Set Gitea username"
    echo ""
    echo "Examples:"
    echo "  $0 --init                    # First time setup"
    echo "  $0                           # Regular backup"
    echo "  $0 --dry-run                 # See what would be backed up"
    echo "  $0 --restore                 # Restore files from backup"
}

# Check dependencies
check_dependencies() {
    local missing_deps=()

    command -v git >/dev/null 2>&1 || missing_deps+=("git")
    command -v find >/dev/null 2>&1 || missing_deps+=("find")

    if [ ${#missing_deps[@]} -ne 0 ]; then
        echo -e "${RED}Error: Missing required dependencies: ${missing_deps[*]}${NC}"
        echo "Please install the missing dependencies and try again."
        exit 1
    fi
}

# Find all .env files in docker directories
find_env_files() {
    local base_dir="$1"

    if [ ! -d "$base_dir" ]; then
        echo -e "${YELLOW}Warning: Docker directory $base_dir does not exist${NC}" >&2
        return 0
    fi

    # Find all .env files, including hidden ones and those with different extensions
    find "$base_dir" -type f \( -name "*.env" -o -name ".env*" -o -name "env.*" \) 2>/dev/null | sort
}

# List all .env files
list_env_files() {
    echo -e "${BLUE}=== Environment Files Found ===${NC}"
    local count=0

    # Use a temp file to avoid subshell issues
    local temp_file
    temp_file=$(mktemp)
    find_env_files "$DOCKER_DIR" > "$temp_file"

    while IFS= read -r env_file; do
        if [ -n "$env_file" ]; then
            local rel_path="${env_file#"$DOCKER_DIR"/}"
            local size
            local modified
            size=$(du -h "$env_file" 2>/dev/null | cut -f1)
            modified=$(stat -c %y "$env_file" 2>/dev/null | cut -d' ' -f1)

            echo -e "${GREEN}📄 $rel_path${NC}"
            echo "   Size: $size | Modified: $modified"
            echo "   Full path: $env_file"
            echo ""
            count=$((count + 1))
        fi
    done < "$temp_file"

    # Clean up temp file
    rm -f "$temp_file"

    echo -e "${BLUE}Total .env files found: $count${NC}"

    if [ $count -eq 0 ]; then
        echo -e "${YELLOW}No .env files found in $DOCKER_DIR${NC}"
        echo "Make sure you have Docker containers with .env files in subdirectories."
    fi
}

# Initialize backup repository
init_backup_repo() {
    echo -e "${YELLOW}Initializing .env backup repository...${NC}"

    # Prompt for Gitea details if not provided
    if [ -z "$GITEA_URL" ]; then
        read -r -p "Enter your Gitea instance URL (e.g., https://git.yourdomain.com): " GITEA_URL
    fi

    if [ -z "$GITEA_USERNAME" ]; then
        read -r -p "Enter your Gitea username: " GITEA_USERNAME
    fi

    # Create backup directory
    mkdir -p "$BACKUP_DIR"
    cd "$BACKUP_DIR"

    # Initialize git repository if not already done
    if [ ! -d ".git" ]; then
        git init
        echo -e "${GREEN}Initialized local git repository${NC}"
    fi

    # Create .gitignore for additional security
    cat > .gitignore << 'EOF'
# Temporary files
*.tmp
*.swp
*.bak
*~

# OS generated files
.DS_Store
Thumbs.db

# Logs
*.log
EOF

    # Create README with important information
    cat > README.md << 'EOF'
# Docker Environment Files Backup

This repository contains backup copies of .env files from Docker containers.

## ⚠️ SECURITY WARNING ⚠️

This repository contains sensitive configuration files including:
- API keys
- Database passwords
- Secret tokens
- Private configurations

**NEVER make this repository public!**

## Structure

$()`
docker-containers/
├── container1/
│   ├── .env
│   └── docker-compose.yml (reference only)
├── container2/
│   └── .env
└── ...
$()`

## Usage

- Files are organized by container/service name
- Only .env files are backed up (no other sensitive files)
- Restore using the backup-env-files.sh script

## Last Backup

This information is updated automatically by the backup script.
EOF

    # Create directory structure
    mkdir -p docker-containers

    # Set up remote if URL provided
    if [ -n "$GITEA_URL" ] && [ -n "$GITEA_USERNAME" ]; then
        local remote_url="${GITEA_URL%/}/${GITEA_USERNAME}/${BACKUP_REPO_NAME}.git"

        # Check if remote already exists
        if ! git remote get-url origin >/dev/null 2>&1; then
            git remote add origin "$remote_url"
            echo -e "${GREEN}Added remote origin: $remote_url${NC}"
        fi

        # Save configuration
        cat > .env-backup-config << EOF
GITEA_URL="$GITEA_URL"
GITEA_USERNAME="$GITEA_USERNAME"
BACKUP_REPO_NAME="$BACKUP_REPO_NAME"
EOF

        echo -e "${YELLOW}Configuration saved to .env-backup-config${NC}"
        echo -e "${BLUE}Next steps:${NC}"
        echo "1. Create a private repository '$BACKUP_REPO_NAME' in your Gitea instance"
        echo "2. Run the backup script to perform your first backup"
        echo "3. The script will attempt to push to the remote repository"
    fi

    # Initial commit
    git add .
    git commit -m "Initial setup of .env backup repository" || echo "Nothing to commit"

    log "Backup repository initialized at $BACKUP_DIR"
}

# Cleanup function for metrics finalization
cleanup() {
    if [[ "$METRICS_ENABLED" == "true" ]]; then
        if [[ -n "$1" && "$1" == "error" ]]; then
            metrics_backup_complete "failed" "Backup failed during execution"
        else
            metrics_backup_complete "success" "Environment files backup completed successfully"
        fi
    fi
}

# Set up cleanup trap
trap 'cleanup error' ERR

# Load configuration
load_config() {
    local config_file="$BACKUP_DIR/.env-backup-config"

    if [ -f "$config_file" ]; then
        # shellcheck source=/dev/null
        source "$config_file"
    fi
}

# Backup .env files
backup_env_files() {
    local dry_run="$1"
    local force="$2"

    echo -e "${YELLOW}Starting .env files backup...${NC}"

    # Initialize metrics if enabled
    if [[ "$METRICS_ENABLED" == "true" ]]; then
        metrics_backup_start "env-files" "$DOCKER_DIR" "$BACKUP_DIR"
        metrics_status_update "initializing" "Preparing environment files backup"
    fi

    # Check if backup directory exists
    if [ ! -d "$BACKUP_DIR" ]; then
        echo -e "${RED}Backup directory not found. Run with --init first.${NC}"
        if [[ "$METRICS_ENABLED" == "true" ]]; then
            metrics_backup_complete "failed" "Backup directory not found"
        fi
        exit 1
    fi

    cd "$BACKUP_DIR"
    load_config

    # Create timestamp
    local timestamp
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    local backup_count=0
    local unchanged_count=0

    # Update metrics for scanning phase
    if [[ "$METRICS_ENABLED" == "true" ]]; then
        metrics_status_update "scanning" "Scanning for environment files"
    fi

    # Process each .env file using a temp file to avoid subshell issues
    local temp_file
    temp_file=$(mktemp)
    find_env_files "$DOCKER_DIR" > "$temp_file"

    # Update metrics for copying phase
    if [[ "$METRICS_ENABLED" == "true" ]]; then
        metrics_status_update "copying" "Backing up environment files"
    fi

    while IFS= read -r env_file; do
        if [ -n "$env_file" ]; then
            # Determine relative path and backup location
            local rel_path="${env_file#"$DOCKER_DIR"/}"
            local backup_path="docker-containers/$rel_path"
            local backup_dir
            backup_dir=$(dirname "$backup_path")

            if [ "$dry_run" = "true" ]; then
                echo -e "${BLUE}Would backup: $rel_path${NC}"
                continue
            fi

            # Create backup directory structure
            mkdir -p "$backup_dir"

            # Check if file has changed
            local needs_backup=true
            if [ -f "$backup_path" ] && [ "$force" != "true" ]; then
                if cmp -s "$env_file" "$backup_path"; then
                    needs_backup=false
                    unchanged_count=$((unchanged_count + 1))
                fi
            fi

            if [ "$needs_backup" = "true" ]; then
                # Copy the file
                if cp "$env_file" "$backup_path"; then
                    echo -e "${GREEN}✓ Backed up: $rel_path${NC}"
                    backup_count=$((backup_count + 1))

                    # Track file completion in metrics
                    if [[ "$METRICS_ENABLED" == "true" ]]; then
                        local file_size
                        file_size=$(stat -c%s "$env_file" 2>/dev/null || echo "0")
                        metrics_file_backup_complete "$rel_path" "$file_size" "copied"
                    fi
                else
                    echo -e "${RED}✗ Failed to backup: $rel_path${NC}"
                    if [[ "$METRICS_ENABLED" == "true" ]]; then
                        local file_size
                        file_size=$(stat -c%s "$env_file" 2>/dev/null || echo "0")
                        metrics_file_backup_complete "$rel_path" "$file_size" "failed"
                    fi
                fi

                # Also create a reference docker-compose.yml if it exists
                local compose_file
                compose_file=$(dirname "$env_file")/docker-compose.yml
                local compose_backup="$backup_dir/docker-compose.yml.ref"

                if [ -f "$compose_file" ] && [ ! -f "$compose_backup" ]; then
                    cp "$compose_file" "$compose_backup"
                    echo -e "${BLUE}  + Reference: docker-compose.yml${NC}"
                fi
            else
                echo -e "${YELLOW}- Unchanged: $rel_path${NC}"
                
                # Track unchanged file in metrics
                if [[ "$METRICS_ENABLED" == "true" ]]; then
                    local file_size
                    file_size=$(stat -c%s "$env_file" 2>/dev/null || echo "0")
                    metrics_file_backup_complete "$rel_path" "$file_size" "unchanged"
                fi
            fi
        fi
    done < "$temp_file"

    # Clean up temp file
    rm -f "$temp_file"

    if [ "$dry_run" = "true" ]; then
        echo -e "${BLUE}Dry run completed. No files were actually backed up.${NC}"
        # Update metrics for dry run completion
        if [[ "$METRICS_ENABLED" == "true" ]]; then
            metrics_status_update "completed" "Dry run completed successfully"
        fi
        return 0
    fi

    # Update metrics for committing phase
    if [[ "$METRICS_ENABLED" == "true" ]]; then
        metrics_status_update "committing" "Committing changes to repository"
    fi

    # Update README with backup information
    sed -i "/^## Last Backup/,$ d" README.md
    cat >> README.md << EOF

## Last Backup

- **Date**: $timestamp
- **Files backed up**: $backup_count
- **Files unchanged**: $unchanged_count
- **Total files**: $((backup_count + unchanged_count))

Generated by backup-env-files.sh
EOF

    # Commit changes
    git add .

    if git diff --staged --quiet; then
        echo -e "${YELLOW}No changes to commit${NC}"
        log "Backup completed - no changes detected"
    else
        git commit -m "Backup .env files - $timestamp

- Files backed up: $backup_count
- Files unchanged: $unchanged_count
- Total files: $((backup_count + unchanged_count))"

        echo -e "${GREEN}Changes committed to local repository${NC}"

        # Update metrics for pushing phase
        if [[ "$METRICS_ENABLED" == "true" ]]; then
            metrics_status_update "pushing" "Pushing changes to remote repository"
        fi

        # Push to remote if configured
        if git remote get-url origin >/dev/null 2>&1; then
            echo -e "${YELLOW}Pushing to remote repository...${NC}"
            if git push origin main 2>/dev/null || git push origin master 2>/dev/null; then
                echo -e "${GREEN}✓ Successfully pushed to remote repository${NC}"
                log "Backup completed and pushed to remote - $backup_count files backed up, $unchanged_count unchanged"
                
                # Update metrics for successful push
                if [[ "$METRICS_ENABLED" == "true" ]]; then
                    metrics_status_update "completed" "Backup completed and pushed to remote"
                fi
            else
                echo -e "${YELLOW}Warning: Could not push to remote repository${NC}"
                echo "You may need to:"
                echo "1. Create the repository in Gitea first"
                echo "2. Set up authentication (SSH key or token)"
                log "Backup completed locally but failed to push to remote - $backup_count files backed up"
                
                # Update metrics for push failure
                if [[ "$METRICS_ENABLED" == "true" ]]; then
                    metrics_status_update "completed_with_warnings" "Backup completed but failed to push to remote"
                fi
            fi
        else
            echo -e "${YELLOW}No remote repository configured${NC}"
            log "Backup completed locally - $backup_count files backed up, $unchanged_count unchanged"
            
            # Update metrics for local-only backup
            if [[ "$METRICS_ENABLED" == "true" ]]; then
                metrics_status_update "completed" "Backup completed locally (no remote configured)"
            fi
        fi
    fi

    echo -e "${GREEN}Backup completed!${NC}"
    echo -e "${BLUE}Summary:${NC}"
    echo "  - Files backed up: $backup_count"
    echo "  - Files unchanged: $unchanged_count"
    echo "  - Backup location: $BACKUP_DIR"

    # Finalize metrics
    if [[ "$METRICS_ENABLED" == "true" ]]; then
        cleanup
    fi
}

# Restore .env files
restore_env_files() {
    echo -e "${YELLOW}Starting .env files restore...${NC}"

    # Initialize metrics if enabled
    if [[ "$METRICS_ENABLED" == "true" ]]; then
        metrics_backup_start "env-files-restore" "$BACKUP_DIR" "$DOCKER_DIR"
        metrics_status_update "initializing" "Preparing environment files restore"
    fi

    if [ ! -d "$BACKUP_DIR" ]; then
        echo -e "${RED}Backup directory not found at $BACKUP_DIR${NC}"
        echo "Either run --init first or clone your backup repository to this location."
        exit 1
    fi

    cd "$BACKUP_DIR"
    load_config

    # Update metrics for pulling phase
    if [[ "$METRICS_ENABLED" == "true" ]]; then
        metrics_status_update "pulling" "Pulling latest changes from remote"
    fi

    # Pull latest changes if remote is configured
    if git remote get-url origin >/dev/null 2>&1; then
        echo -e "${YELLOW}Pulling latest changes from remote...${NC}"
        git pull origin main 2>/dev/null || git pull origin master 2>/dev/null || true
    fi

    local restore_count=0
    local error_count=0

    # Update metrics for restoring phase
    if [[ "$METRICS_ENABLED" == "true" ]]; then
        metrics_status_update "restoring" "Restoring environment files"
    fi

    # Use a temp file to avoid subshell issues
    local temp_file
    temp_file=$(mktemp)
    find docker-containers -name "*.env" -type f 2>/dev/null > "$temp_file"

    while IFS= read -r backup_file; do
        if [ -n "$backup_file" ]; then
            # Determine target path
            local rel_path="${backup_file#docker-containers/}"
            local target_file="$DOCKER_DIR/$rel_path"
            local target_dir
            target_dir=$(dirname "$target_file")

            # Create target directory if it doesn't exist
            if [ ! -d "$target_dir" ]; then
                echo -e "${YELLOW}Creating directory: $target_dir${NC}"
                mkdir -p "$target_dir"
            fi

            # Ask for confirmation if file exists and is different
            if [ -f "$target_file" ]; then
                if ! cmp -s "$backup_file" "$target_file"; then
                    echo -e "${YELLOW}File exists and differs: $rel_path${NC}"
                    read -p "Overwrite? (y/N): " -n 1 -r
                    echo
                    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
                        echo -e "${YELLOW}Skipped: $rel_path${NC}"
                        continue
                    fi
                else
                    echo -e "${GREEN}Identical: $rel_path${NC}"
                    continue
                fi
            fi

            # Copy the file
            if cp "$backup_file" "$target_file"; then
                echo -e "${GREEN}✓ Restored: $rel_path${NC}"
                restore_count=$((restore_count + 1))
                
                # Track file restoration in metrics
                if [[ "$METRICS_ENABLED" == "true" ]]; then
                    local file_size
                    file_size=$(stat -c%s "$target_file" 2>/dev/null || echo "0")
                    metrics_file_backup_complete "$rel_path" "$file_size" "restored"
                fi
            else
                echo -e "${RED}✗ Failed to restore: $rel_path${NC}"
                error_count=$((error_count + 1))
                
                # Track failed restoration in metrics
                if [[ "$METRICS_ENABLED" == "true" ]]; then
                    local file_size
                    file_size=$(stat -c%s "$backup_file" 2>/dev/null || echo "0")
                    metrics_file_backup_complete "$rel_path" "$file_size" "restore_failed"
                fi
            fi
        fi
    done < "$temp_file"

    # Clean up temp file
    rm -f "$temp_file"

    echo -e "${GREEN}Restore completed!${NC}"
    echo -e "${BLUE}Summary:${NC}"
    echo "  - Files restored: $restore_count"
    echo "  - Errors: $error_count"

    log "Restore completed - $restore_count files restored, $error_count errors"
    
    # Finalize metrics for restore
    if [[ "$METRICS_ENABLED" == "true" ]]; then
        if [[ $error_count -gt 0 ]]; then
            metrics_backup_complete "completed_with_errors" "Restore completed with $error_count errors"
        else
            metrics_backup_complete "success" "Environment files restore completed successfully"
        fi
    fi
}

# Main function
main() {
    local init_repo=false
    local dry_run=false
    local force=false
    local restore=false
    local list_files=false

    # Parse command line arguments
    while [[ $# -gt 0 ]]; do
        case $1 in
            -h|--help)
                usage
                exit 0
                ;;
            -i|--init)
                init_repo=true
                shift
                ;;
            -f|--force)
                force=true
                shift
                ;;
            -d|--dry-run)
                dry_run=true
                shift
                ;;
            -r|--restore)
                restore=true
                shift
                ;;
            -l|--list)
                list_files=true
                shift
                ;;
            -g|--gitea-url)
                GITEA_URL="$2"
                shift 2
                ;;
            -u|--username)
                GITEA_USERNAME="$2"
                shift 2
                ;;
            *)
                echo "Unknown option: $1"
                usage
                exit 1
                ;;
        esac
    done

    # Check dependencies
    check_dependencies

    # Execute requested action
    if [ "$list_files" = true ]; then
        list_env_files
    elif [ "$init_repo" = true ]; then
        init_backup_repo
    elif [ "$restore" = true ]; then
        restore_env_files
    else
        backup_env_files "$dry_run" "$force"
    fi
}

# Run main function with all arguments
main "$@"
