#!/bin/bash

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# BTM Koperasi - Automated Deployment Script
# Pull-based deployment from GitHub Releases
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
#
# Usage: 
#   ./deploy.sh [branch]              - Deploy from branch (main/develop)
#   ./deploy.sh --rollback            - Rollback to previous version
#   ./deploy.sh --list                - List available releases
#   ./deploy.sh --cleanup             - Clean old releases
#   ./deploy.sh --help                - Show help
#
# Examples:
#   ./deploy.sh main                  - Deploy latest from main
#   ./deploy.sh develop               - Deploy latest from develop
#   ./deploy.sh --rollback            - Rollback last deployment
#
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

set -e

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Configuration - EDIT THESE VALUES
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# GitHub repository (username/repo)
GITHUB_REPO="YOUR_GITHUB_USERNAME/btm-koperasi"

# GitHub Personal Access Token
# For security, store token in file and read from there
TOKEN_FILE="/root/.github_token"
if [ -f "$TOKEN_FILE" ]; then
    GITHUB_TOKEN=$(cat "$TOKEN_FILE")
else
    GITHUB_TOKEN="YOUR_GITHUB_TOKEN_HERE"
fi

# Application directories
APP_DIR="/www/wwwroot/btm-koperasi"
BACKUP_DIR="/www/wwwroot/btm-koperasi-backup"
RELEASES_DIR="/www/wwwroot/btm-koperasi-releases"
DOWNLOADS_DIR="/tmp/btm-deploy"

# Default branch
DEFAULT_BRANCH="main"

# Keep last N releases
KEEP_LAST_RELEASES=5

# Enable/disable features
AUTO_BACKUP=true
AUTO_CLEANUP=true
HEALTH_CHECK=true
RESTART_SERVICES=true

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Advanced Configuration (usually no need to change)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# PHP version
PHP_VERSION="8.3"

# Web server user/group
WEB_USER="www"
WEB_GROUP="www"

# Supervisor program name
SUPERVISOR_PROGRAM="btm-koperasi-worker"

# Systemd services
PHP_FPM_SERVICE="php${PHP_VERSION}-fpm"

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Script Variables (do not change)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

SCRIPT_VERSION="2.0.0"
SCRIPT_START_TIME=$(date +%s)

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
NC='\033[0m' # No Color

# Unicode symbols
SYM_INFO="ℹ️"
SYM_OK="✅"
SYM_WARN="⚠️"
SYM_ERR="❌"
SYM_DEPLOY="🚀"
SYM_BACKUP="💾"
SYM_CLEAN="🧹"
SYM_CHECK="✔️"

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Helper Functions
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

log() {
    echo -e "${CYAN}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"
}

log_info() {
    echo -e "${BLUE}${SYM_INFO} [INFO]${NC} $1"
}

log_success() {
    echo -e "${GREEN}${SYM_OK} [SUCCESS]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}${SYM_WARN} [WARNING]${NC} $1"
}

log_error() {
    echo -e "${RED}${SYM_ERR} [ERROR]${NC} $1" >&2
}

log_deploy() {
    echo -e "${MAGENTA}${SYM_DEPLOY} [DEPLOY]${NC} $1"
}

log_step() {
    echo -e "\n${WHITE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo -e "${WHITE}$1${NC}"
    echo -e "${WHITE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
}

elapsed_time() {
    local end_time=$(date +%s)
    local elapsed=$((end_time - SCRIPT_START_TIME))
    local minutes=$((elapsed / 60))
    local seconds=$((elapsed % 60))
    printf "%02d:%02d" $minutes $seconds
}

print_header() {
    echo ""
    echo -e "${WHITE}╔════════════════════════════════════════╗${NC}"
    echo -e "${WHITE}║   BTM Koperasi - Deployment Script    ║${NC}"
    echo -e "${WHITE}╚════════════════════════════════════════╝${NC}"
    echo ""
    echo -e "  ${SYM_INFO} Version:  ${WHITE}$SCRIPT_VERSION${NC}"
    echo -e "  ${SYM_INFO} Branch:   ${WHITE}$BRANCH${NC}"
    echo -e "  ${SYM_INFO} Time:     ${WHITE}$(date '+%Y-%m-%d %H:%M:%S')${NC}"
    echo -e "  ${SYM_INFO} Duration: ${WHITE}~5-10 minutes${NC}"
    echo ""
}

print_summary() {
    echo ""
    echo -e "${WHITE}╔════════════════════════════════════════╗${NC}"
    echo -e "${WHITE}║        Deployment Summary              ║${NC}"
    echo -e "${WHITE}╚════════════════════════════════════════╝${NC}"
    echo ""
    echo -e "  ${SYM_DEPLOY} Status:     ${GREEN}SUCCESS${NC}"
    echo -e "  ${SYM_INFO} Version:    ${WHITE}$DEPLOYED_VERSION${NC}"
    echo -e "  ${SYM_INFO} Branch:     ${WHITE}$BRANCH${NC}"
    echo -e "  ${SYM_INFO} Duration:   ${WHITE}$(elapsed_time)${NC}"
    echo -e "  ${SYM_INFO} Backup:     ${WHITE}$BACKUP_PATH${NC}"
    echo ""
}

check_root() {
    if [ "$EUID" -ne 0 ]; then
        log_error "This script must be run as root (use sudo)"
        exit 1
    fi
}

check_prerequisites() {
    log_step "Checking Prerequisites"
    
    local missing=()
    
    # Check required commands
    for cmd in curl jq tar systemctl; do
        if ! command -v $cmd &> /dev/null; then
            missing+=($cmd)
        fi
    done
    
    if [ ${#missing[@]} -ne 0 ]; then
        log_error "Missing required commands: ${missing[*]}"
        log_info "Install with: apt-get install -y ${missing[*]}"
        exit 1
    fi
    
    log_success "All required commands available"
    
    # Check directories
    if [ ! -d "$APP_DIR" ]; then
        log_error "Application directory not found: $APP_DIR"
        exit 1
    fi
    
    log_success "Application directory exists"
    
    # Check .env file
    if [ ! -f "$APP_DIR/.env" ]; then
        log_error ".env file not found in $APP_DIR"
        exit 1
    fi
    
    log_success ".env file found"
    
    # Check GitHub token
    if [ -z "$GITHUB_TOKEN" ] || [ "$GITHUB_TOKEN" == "YOUR_GITHUB_TOKEN_HERE" ]; then
        log_error "GitHub token not configured"
        log_info "Edit this script and set GITHUB_TOKEN or create $TOKEN_FILE"
        exit 1
    fi
    
    # Mask token in output
    MASKED_TOKEN="${GITHUB_TOKEN:0:4}...${GITHUB_TOKEN: -4}"
    log_success "GitHub token configured ($MASKED_TOKEN)"
    
    # Check internet connectivity
    if ! curl -s --connect-timeout 5 https://api.github.com > /dev/null; then
        log_error "No internet connectivity or GitHub API unreachable"
        exit 1
    fi
    
    log_success "Internet connectivity OK"
    
    echo ""
}

create_backup() {
    if [ "$AUTO_BACKUP" = false ]; then
        log_warning "Backup disabled, skipping..."
        return
    fi
    
    log_step "Creating Backup"
    
    TIMESTAMP=$(date +%Y%m%d_%H%M%S)
    BACKUP_PATH="$BACKUP_DIR/$TIMESTAMP"
    
    mkdir -p "$BACKUP_PATH"
    
    log_info "Backup location: $BACKUP_PATH"
    
    # Backup application files
    log_info "Backing up application files..."
    cp -r "$APP_DIR" "$BACKUP_PATH/app" 2>/dev/null || {
        log_warning "Some files could not be copied"
    }
    
    # Backup database
    log_info "Backing up database..."
    DB_NAME=$(grep "^DB_DATABASE=" "$APP_DIR/.env" | cut -d '=' -f2 | tr -d '"' | tr -d "'")
    DB_USER=$(grep "^DB_USERNAME=" "$APP_DIR/.env" | cut -d '=' -f2 | tr -d '"' | tr -d "'")
    DB_PASS=$(grep "^DB_PASSWORD=" "$APP_DIR/.env" | cut -d '=' -f2 | tr -d '"' | tr -d "'")
    
    if [ ! -z "$DB_NAME" ]; then
        if mysqldump -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" > "$BACKUP_PATH/database.sql" 2>/dev/null; then
            log_success "Database backup created"
        else
            log_warning "Database backup failed (MySQL might not be running)"
        fi
    else
        log_warning "Database name not found in .env"
    fi
    
    # Keep only last N backups
    log_info "Cleaning old backups (keeping last $KEEP_LAST_RELEASES)..."
    cd "$BACKUP_DIR"
    ls -dt */ 2>/dev/null | tail -n +$((KEEP_LAST_RELEASES + 1)) | xargs -r rm -rf
    
    log_success "Backup created: $BACKUP_PATH"
    echo ""
}

list_releases() {
    log_step "Available Releases"
    
    RESPONSE=$(curl -s \
        -H "Authorization: token $GITHUB_TOKEN" \
        -H "Accept: application/vnd.github.v3+json" \
        "https://api.github.com/repos/$GITHUB_REPO/releases")
    
    if [ $(echo "$RESPONSE" | jq -r '.message' 2>/dev/null) == "Bad credentials" ]; then
        log_error "Invalid GitHub token"
        exit 1
    fi
    
    echo "$RESPONSE" | jq -r '.[0:10] | .[] | "\(.tag_name) | \(.name) | \(.published_at)"' | \
    while IFS='|' read -r tag name date; do
        echo -e "  ${CYAN}$tag${NC}"
        echo -e "    $name"
        echo -e "    Published: $date"
        echo ""
    done
    
    exit 0
}

download_release() {
    log_step "Downloading Release"
    
    mkdir -p "$DOWNLOADS_DIR"
    cd "$DOWNLOADS_DIR"
    
    log_info "Fetching latest release for branch: $BRANCH"
    
    # Get latest release
    if [ "$BRANCH" == "main" ]; then
        RELEASE_INFO=$(curl -s \
            -H "Authorization: token $GITHUB_TOKEN" \
            -H "Accept: application/vnd.github.v3+json" \
            "https://api.github.com/repos/$GITHUB_REPO/releases/latest")
    else
        # Get releases and filter by branch
        RELEASE_INFO=$(curl -s \
            -H "Authorization: token $GITHUB_TOKEN" \
            -H "Accept: application/vnd.github.v3+json" \
            "https://api.github.com/repos/$GITHUB_REPO/releases" | \
            jq -r --arg branch "$BRANCH" '.[] | select(.target_commitish == $branch)' | \
            jq -s '.[0]')
    fi
    
    if [ -z "$RELEASE_INFO" ] || [ "$RELEASE_INFO" == "null" ]; then
        log_error "No release found for branch: $BRANCH"
        exit 1
    fi
    
    RELEASE_TAG=$(echo "$RELEASE_INFO" | jq -r '.tag_name')
    RELEASE_NAME=$(echo "$RELEASE_INFO" | jq -r '.name')
    PUBLISHED_AT=$(echo "$RELEASE_INFO" | jq -r '.published_at')
    
    log_info "Release: $RELEASE_TAG"
    log_info "Name: $RELEASE_NAME"
    log_info "Published: $PUBLISHED_AT"
    
    # Get asset
    ASSET=$(echo "$RELEASE_INFO" | jq -r '.assets[] | select(.name | endswith(".tar.gz"))')
    
    if [ -z "$ASSET" ]; then
        log_error "No release asset (.tar.gz) found"
        exit 1
    fi
    
    DOWNLOAD_URL=$(echo "$ASSET" | jq -r '.browser_download_url')
    ASSET_NAME=$(echo "$ASSET" | jq -r '.name')
    ASSET_SIZE=$(echo "$ASSET" | jq -r '.size')
    ASSET_SIZE_MB=$(echo "scale=2; $ASSET_SIZE / 1024 / 1024" | bc 2>/dev/null || echo "N/A")
    
    log_info "Asset: $ASSET_NAME ($ASSET_SIZE_MB MB)"
    
    # Download asset
    log_info "Downloading..."
    curl -L \
        -H "Authorization: token $GITHUB_TOKEN" \
        -H "Accept: application/octet-stream" \
        "$DOWNLOAD_URL" \
        -o "$ASSET_NAME" \
        --progress-bar
    
    # Download checksum
    CHECKSUM_FILE="${ASSET_NAME}.sha256"
    if curl -s --head "${DOWNLOAD_URL%.tar.gz}.tar.gz.sha256" | grep -q "200 OK"; then
        curl -L \
            -H "Authorization: token $GITHUB_TOKEN" \
            -H "Accept: application/octet-stream" \
            "${DOWNLOAD_URL%.tar.gz}.tar.gz.sha256" \
            -o "$CHECKSUM_FILE" 2>/dev/null
        
        # Verify checksum
        log_info "Verifying checksum..."
        if sha256sum -c "$CHECKSUM_FILE" &> /dev/null; then
            log_success "Checksum verified"
        else
            log_error "Checksum verification failed!"
            exit 1
        fi
    else
        log_warning "Checksum file not found, skipping verification"
    fi
    
    log_success "Downloaded: $ASSET_NAME"
    
    DEPLOYED_VERSION="$RELEASE_TAG"
    echo "$ASSET_NAME"
}

extract_release() {
    local ASSET_NAME="$1"
    
    log_step "Extracting Release"
    
    RELEASE_PATH="$RELEASES_DIR/$(date +%Y%m%d_%H%M%S)"
    mkdir -p "$RELEASE_PATH"
    
    log_info "Extracting to: $RELEASE_PATH"
    
    if tar -xzf "$DOWNLOADS_DIR/$ASSET_NAME" -C "$RELEASE_PATH"; then
        log_success "Extracted successfully"
    else
        log_error "Extraction failed"
        exit 1
    fi
    
    # Show deployment info if exists
    if [ -f "$RELEASE_PATH/DEPLOY_INFO.json" ]; then
        log_info "Deployment Information:"
        cat "$RELEASE_PATH/DEPLOY_INFO.json" | jq '.' 2>/dev/null || cat "$RELEASE_PATH/DEPLOY_INFO.json"
    fi
    
    echo "$RELEASE_PATH"
}

deploy_release() {
    local RELEASE_PATH="$1"
    
    log_step "Deploying Release"
    
    # Stop queue workers
    if [ "$RESTART_SERVICES" = true ]; then
        log_info "Stopping queue workers..."
        supervisorctl stop "$SUPERVISOR_PROGRAM":* 2>/dev/null || log_warning "Could not stop workers"
    fi
    
    # Backup current .env
    log_info "Preserving .env file..."
    if [ -f "$APP_DIR/.env" ]; then
        cp "$APP_DIR/.env" "$RELEASE_PATH/.env.backup"
    fi
    
    # Remove old application files (keep some directories)
    log_info "Removing old application files..."
    find "$APP_DIR" -mindepth 1 -maxdepth 1 \
        ! -name '.env' \
        ! -name 'storage' \
        ! -name 'bootstrap' \
        ! -name '.deployed_version' \
        -exec rm -rf {} \; 2>/dev/null || true
    
    # Copy new files
    log_info "Copying new files..."
    cp -r "$RELEASE_PATH/"* "$APP_DIR/"
    cp -r "$RELEASE_PATH"/.[!.]* "$APP_DIR/" 2>/dev/null || true
    
    # Restore .env if backup exists
    if [ -f "$RELEASE_PATH/.env.backup" ]; then
        log_info "Restoring .env configuration..."
        # Merge old .env with new .env.example if needed
        cp "$RELEASE_PATH/.env.backup" "$APP_DIR/.env"
    fi
    
    # Set permissions
    log_info "Setting permissions..."
    chown -R "$WEB_USER":"WEB_GROUP" "$APP_DIR"
    chmod -R 775 "$APP_DIR/storage"
    chmod -R 775 "$APP_DIR/bootstrap/cache"
    
    # Run deploy script if exists
    if [ -x "$APP_DIR/deploy.sh" ]; then
        log_info "Running post-deployment script..."
        bash "$APP_DIR/deploy.sh"
    fi
    
    # Restart services
    if [ "$RESTART_SERVICES" = true ]; then
        log_info "Starting queue workers..."
        supervisorctl start "$SUPERVISOR_PROGRAM":* 2>/dev/null || log_warning "Could not start workers"
    fi
    
    log_success "Deployment complete!"
    echo ""
}

health_check() {
    if [ "$HEALTH_CHECK" = false ]; then
        log_warning "Health check disabled, skipping..."
        return
    fi
    
    log_step "Running Health Check"
    
    # Wait for services to stabilize
    log_info "Waiting for services to stabilize..."
    sleep 5
    
    # Check if app is accessible
    log_info "Checking application accessibility..."
    RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost 2>/dev/null || echo "000")
    
    if [ "$RESPONSE" == "200" ]; then
        log_success "Application is accessible (HTTP $RESPONSE)"
    else
        log_error "Application returned HTTP $RESPONSE"
        log_warning "Check logs: tail -f /www/wwwlogs/btm-koperasi-error.log"
        return 1
    fi
    
    # Check database connection
    log_info "Checking database connection..."
    cd "$APP_DIR"
    if php artisan tinker --execute="DB::connection()->getPdo();" 2>/dev/null; then
        log_success "Database connection OK"
    else
        log_warning "Could not verify database connection"
    fi
    
    # Check storage permissions
    log_info "Checking storage permissions..."
    if [ -w "$APP_DIR/storage" ]; then
        log_success "Storage directory is writable"
    else
        log_error "Storage directory is not writable!"
        return 1
    fi
    
    log_success "All health checks passed!"
    echo ""
    return 0
}

cleanup() {
    if [ "$AUTO_CLEANUP" = false ]; then
        log_warning "Cleanup disabled, skipping..."
        return
    fi
    
    log_step "Cleaning Up"
    
    # Clean old releases
    log_info "Cleaning old releases (keeping last $KEEP_LAST_RELEASES)..."
    cd "$RELEASES_DIR"
    ls -dt */ 2>/dev/null | tail -n +$((KEEP_LAST_RELEASES + 1)) | xargs -r rm -rf
    
    # Clean downloads
    log_info "Cleaning download directory..."
    rm -f "$DOWNLOADS_DIR"/*.tar.gz "$DOWNLOADS_DIR"/*.sha256 2>/dev/null || true
    
    log_success "Cleanup complete"
    echo ""
}

rollback() {
    log_step "Rolling Back Deployment"
    
    cd "$RELEASES_DIR"
    
    # Get previous release
    PREVIOUS_RELEASE=$(ls -dt */ 2>/dev/null | head -n2 | tail -n1)
    
    if [ -z "$PREVIOUS_RELEASE" ]; then
        log_error "No previous release found for rollback"
        exit 1
    fi
    
    log_info "Rolling back to: $PREVIOUS_RELEASE"
    
    # Deploy previous release
    deploy_release "$RELEASES_DIR/$PREVIOUS_RELEASE"
    
    log_success "Rollback complete!"
    echo ""
}

show_help() {
    echo ""
    echo -e "${WHITE}BTM Koperasi Deployment Script${NC}"
    echo ""
    echo -e "${CYAN}Usage:${NC}"
    echo "  ./deploy.sh [OPTIONS] [BRANCH]"
    echo ""
    echo -e "${CYAN}Options:${NC}"
    echo "  --rollback          Rollback to previous version"
    echo "  --list              List available releases"
    echo "  --cleanup           Clean old releases"
    echo "  --no-backup         Skip backup"
    echo "  --no-check          Skip health check"
    echo "  --no-cleanup        Skip cleanup"
    echo "  --help              Show this help"
    echo ""
    echo -e "${CYAN}Examples:${NC}"
    echo "  ./deploy.sh main              Deploy from main branch"
    echo "  ./deploy.sh develop           Deploy from develop branch"
    echo "  ./deploy.sh --rollback        Rollback last deployment"
    echo "  ./deploy.sh --list            List available releases"
    echo "  ./deploy.sh --no-backup main  Deploy without backup"
    echo ""
    echo -e "${CYAN}Configuration:${NC}"
    echo "  Edit this script to change:"
    echo "  - GITHUB_REPO"
    echo "  - GITHUB_TOKEN"
    echo "  - APP_DIR"
    echo "  - Other settings"
    echo ""
}

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Main Execution
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

main() {
    # Parse arguments
    while [[ $# -gt 0 ]]; do
        case $1 in
            --rollback)
                ROLLBACK=true
                shift
                ;;
            --list)
                LIST_RELEASES=true
                shift
                ;;
            --cleanup)
                MANUAL_CLEANUP=true
                shift
                ;;
            --no-backup)
                AUTO_BACKUP=false
                shift
                ;;
            --no-check)
                HEALTH_CHECK=false
                shift
                ;;
            --no-cleanup)
                AUTO_CLEANUP=false
                shift
                ;;
            --help)
                SHOW_HELP=true
                shift
                ;;
            main|develop)
                BRANCH="$1"
                shift
                ;;
            *)
                log_error "Unknown option: $1"
                echo "Use --help for usage information"
                exit 1
                ;;
        esac
    done
    
    # Check root
    check_root
    
    # Show help if requested
    if [ "$SHOW_HELP" = true ]; then
        show_help
        exit 0
    fi
    
    # List releases if requested
    if [ "$LIST_RELEASES" = true ]; then
        print_header
        list_releases
    fi
    
    # Manual cleanup if requested
    if [ "$MANUAL_CLEANUP" = true ]; then
        print_header
        cleanup
        exit 0
    fi
    
    # Rollback if requested
    if [ "$ROLLBACK" = true ]; then
        print_header
        check_prerequisites
        rollback
        exit 0
    fi
    
    # Normal deployment
    print_header
    check_prerequisites
    
    BACKUP_PATH="N/A"
    
    create_backup
    
    ASSET_NAME=$(download_release)
    RELEASE_PATH=$(extract_release "$ASSET_NAME")
    
    deploy_release "$RELEASE_PATH"
    
    if health_check; then
        cleanup
        
        print_summary
        
        log_success "🎉 Deployment completed successfully!"
        echo ""
    else
        log_error "Health check failed!"
        log_warning "Consider rolling back:"
        echo "  $0 --rollback"
        exit 1
    fi
}

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