#!/bin/bash

# Complete Secrets Management Implementation
# Comprehensive Docker secrets management for HomeAudit infrastructure

set -euo pipefail

# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
SECRETS_DIR="$PROJECT_ROOT/secrets"
LOG_FILE="$PROJECT_ROOT/logs/secrets-management-$(date +%Y%m%d-%H%M%S).log"

# Create directories
mkdir -p "$SECRETS_DIR"/{env,files,docker,validation} "$(dirname "$LOG_FILE")"

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

# Generate secure random password
generate_password() {
    local length="${1:-32}"
    openssl rand -base64 "$length" | tr -d "=+/" | cut -c1-"$length"
}

# Create Docker secret safely
create_docker_secret() {
    local secret_name="$1"
    local secret_value="$2"
    local overwrite="${3:-false}"
    
    # Check if secret already exists
    if docker secret inspect "$secret_name" >/dev/null 2>&1; then
        if [[ "$overwrite" == "true" ]]; then
            log "⚠️  Secret $secret_name exists, removing..."
            docker secret rm "$secret_name" || true
            sleep 1
        else
            log "✅ Secret $secret_name already exists, skipping"
            return 0
        fi
    fi
    
    # Create the secret
    echo "$secret_value" | docker secret create "$secret_name" - >/dev/null
    log "✅ Created Docker secret: $secret_name"
}

# Collect existing secrets from running containers
collect_existing_secrets() {
    log "Collecting existing secrets from running containers..."
    
    local secrets_inventory="$SECRETS_DIR/existing-secrets-inventory.yaml"
    cat > "$secrets_inventory" << 'EOF'
# Existing Secrets Inventory
# Collected from running containers
secrets_found:
EOF
    
    # Scan running containers
    docker ps --format "{{.Names}}" | while read -r container; do
        if [[ -z "$container" ]]; then continue; fi
        
        log "Scanning container: $container"
        
        # Extract environment variables (sanitized)
        local env_file="$SECRETS_DIR/env/${container}.env"
        docker exec "$container" env 2>/dev/null | \
            grep -iE "(password|secret|key|token|api)" | \
            sed 's/=.*$/=REDACTED/' > "$env_file" || touch "$env_file"
        
        # Check for mounted secret files
        local mounts_file="$SECRETS_DIR/files/${container}-mounts.txt"
        docker inspect "$container" 2>/dev/null | \
            jq -r '.[].Mounts[]? | select(.Type=="bind") | .Source' | \
            grep -iE "(secret|key|cert|password)" > "$mounts_file" 2>/dev/null || touch "$mounts_file"
        
        # Add to inventory
        if [[ -s "$env_file" || -s "$mounts_file" ]]; then
            cat >> "$secrets_inventory" << EOF
  $container:
    env_secrets: $(wc -l < "$env_file")
    mounted_secrets: $(wc -l < "$mounts_file")
    env_file: "$env_file"
    mounts_file: "$mounts_file"
EOF
        fi
    done
    
    log "✅ Secrets inventory created: $secrets_inventory"
}

# Generate all required Docker secrets
generate_docker_secrets() {
    log "Generating Docker secrets for all services..."
    
    # Database secrets
    create_docker_secret "pg_root_password" "$(generate_password 32)"
    create_docker_secret "mariadb_root_password" "$(generate_password 32)"
    create_docker_secret "redis_password" "$(generate_password 24)"
    
    # Application secrets
    create_docker_secret "nextcloud_db_password" "$(generate_password 32)"
    create_docker_secret "nextcloud_admin_password" "$(generate_password 24)"
    create_docker_secret "immich_db_password" "$(generate_password 32)"
    create_docker_secret "paperless_secret_key" "$(generate_password 64)"
    create_docker_secret "vaultwarden_admin_token" "$(generate_password 48)"
    create_docker_secret "grafana_admin_password" "$(generate_password 24)"
    
    # API tokens and keys
    create_docker_secret "ha_api_token" "$(generate_password 64)"
    create_docker_secret "jellyfin_api_key" "$(generate_password 32)"
    create_docker_secret "gitea_secret_key" "$(generate_password 64)"
    create_docker_secret "traefik_dashboard_password" "$(htpasswd -nbB admin $(generate_password 16) | cut -d: -f2)"
    
    # SSL/TLS certificates (if not using Let's Encrypt)
    if [[ ! -f "$SECRETS_DIR/files/tls.crt" ]]; then
        log "Generating self-signed SSL certificate..."
        openssl req -x509 -newkey rsa:4096 -keyout "$SECRETS_DIR/files/tls.key" -out "$SECRETS_DIR/files/tls.crt" -days 365 -nodes -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" >/dev/null 2>&1
        create_docker_secret "tls_certificate" "$(cat "$SECRETS_DIR/files/tls.crt")"
        create_docker_secret "tls_private_key" "$(cat "$SECRETS_DIR/files/tls.key")"
    fi
    
    log "✅ All Docker secrets generated successfully"
}

# Create secrets mapping file for stack updates
create_secrets_mapping() {
    log "Creating secrets mapping configuration..."
    
    local mapping_file="$SECRETS_DIR/docker-secrets-mapping.yaml"
    cat > "$mapping_file" << 'EOF'
# Docker Secrets Mapping
# Maps environment variables to Docker secrets

secrets_mapping:
  postgresql:
    POSTGRES_PASSWORD: pg_root_password
    POSTGRES_DB_PASSWORD: pg_root_password
    
  mariadb:
    MYSQL_ROOT_PASSWORD: mariadb_root_password
    MARIADB_ROOT_PASSWORD: mariadb_root_password
    
  redis:
    REDIS_PASSWORD: redis_password
    
  nextcloud:
    MYSQL_PASSWORD: nextcloud_db_password
    NEXTCLOUD_ADMIN_PASSWORD: nextcloud_admin_password
    
  immich:
    DB_PASSWORD: immich_db_password
    
  paperless:
    PAPERLESS_SECRET_KEY: paperless_secret_key
    
  vaultwarden:
    ADMIN_TOKEN: vaultwarden_admin_token
    
  homeassistant:
    SUPERVISOR_TOKEN: ha_api_token
    
  grafana:
    GF_SECURITY_ADMIN_PASSWORD: grafana_admin_password
    
  jellyfin:
    JELLYFIN_API_KEY: jellyfin_api_key
    
  gitea:
    GITEA__security__SECRET_KEY: gitea_secret_key

# File secrets (certificates, keys)
file_secrets:
  tls_certificate: /run/secrets/tls_certificate
  tls_private_key: /run/secrets/tls_private_key
EOF
    
    log "✅ Secrets mapping created: $mapping_file"
}

# Update stack files to use Docker secrets
update_stacks_with_secrets() {
    log "Updating stack files to use Docker secrets..."
    
    local stacks_dir="$PROJECT_ROOT/stacks"
    local backup_dir="$PROJECT_ROOT/backups/stacks-pre-secrets-$(date +%Y%m%d-%H%M%S)"
    
    # Create backup
    mkdir -p "$backup_dir"
    find "$stacks_dir" -name "*.yml" -exec cp {} "$backup_dir/" \;
    log "✅ Stack files backed up to: $backup_dir"
    
    # Update each stack file
    find "$stacks_dir" -name "*.yml" | while read -r stack_file; do
        local stack_name
        stack_name=$(basename "$stack_file" .yml)
        log "Updating stack file: $stack_name"
        
        # Create updated stack with secrets
        python3 << PYTHON_SCRIPT
import yaml
import re
import sys

stack_file = "$stack_file"
try:
    # Load the stack file
    with open(stack_file, 'r') as f:
        stack_data = yaml.safe_load(f)
    
    # Ensure secrets section exists
    if 'secrets' not in stack_data:
        stack_data['secrets'] = {}
    
    # Process services
    if 'services' in stack_data:
        for service_name, service_config in stack_data['services'].items():
            if 'environment' in service_config:
                env_vars = service_config['environment']
                
                # Convert environment list to dict if needed
                if isinstance(env_vars, list):
                    env_dict = {}
                    for env in env_vars:
                        if '=' in env:
                            key, value = env.split('=', 1)
                            env_dict[key] = value
                        else:
                            env_dict[env] = ''
                    env_vars = env_dict
                    service_config['environment'] = env_vars
                
                # Update password/secret environment variables
                secrets_added = []
                for env_key, env_value in list(env_vars.items()):
                    if any(keyword in env_key.lower() for keyword in ['password', 'secret', 'key', 'token']):
                        # Convert to _FILE pattern for Docker secrets
                        file_env_key = env_key + '_FILE'
                        secret_name = env_key.lower().replace('_', '_')
                        
                        # Map common secret names
                        secret_mappings = {
                            'postgres_password': 'pg_root_password',
                            'mysql_password': 'nextcloud_db_password',
                            'mysql_root_password': 'mariadb_root_password',
                            'db_password': service_name + '_db_password',
                            'admin_password': service_name + '_admin_password',
                            'secret_key': service_name + '_secret_key',
                            'api_token': service_name + '_api_token'
                        }
                        
                        mapped_secret = secret_mappings.get(secret_name, secret_name)
                        
                        # Update environment to use secrets file
                        env_vars[file_env_key] = f'/run/secrets/{mapped_secret}'
                        if env_key in env_vars:
                            del env_vars[env_key]
                        
                        # Add to secrets section
                        stack_data['secrets'][mapped_secret] = {'external': True}
                        secrets_added.append(mapped_secret)
                
                # Add secrets to service if any were added
                if secrets_added:
                    if 'secrets' not in service_config:
                        service_config['secrets'] = []
                    service_config['secrets'].extend(secrets_added)
    
    # Write updated stack file
    with open(stack_file, 'w') as f:
        yaml.dump(stack_data, f, default_flow_style=False, indent=2, sort_keys=False)
    
    print(f"✅ Updated {stack_file} with Docker secrets")
    
except Exception as e:
    print(f"❌ Error updating {stack_file}: {e}")
    sys.exit(1)
PYTHON_SCRIPT
    done
    
    log "✅ All stack files updated to use Docker secrets"
}

# Validate secrets configuration
validate_secrets() {
    log "Validating secrets configuration..."
    
    local validation_report="$SECRETS_DIR/validation-report.yaml"
    cat > "$validation_report" << EOF
secrets_validation:
  timestamp: "$(date -Iseconds)"
  docker_secrets:
EOF
    
    # Check each secret
    local total_secrets=0
    local valid_secrets=0
    
    docker secret ls --format "{{.Name}}" | while read -r secret_name; do
        if [[ -n "$secret_name" ]]; then
            ((total_secrets++))
            if docker secret inspect "$secret_name" >/dev/null 2>&1; then
                ((valid_secrets++))
                echo "    - name: \"$secret_name\"" >> "$validation_report"
                echo "      status: \"valid\"" >> "$validation_report"
                echo "      created: \"$(docker secret inspect "$secret_name" --format '{{.CreatedAt}}')\"" >> "$validation_report"
            else
                echo "    - name: \"$secret_name\"" >> "$validation_report"
                echo "      status: \"invalid\"" >> "$validation_report"
            fi
        fi
    done
    
    # Add summary
    cat >> "$validation_report" << EOF
  summary:
    total_secrets: $total_secrets
    valid_secrets: $valid_secrets
    validation_passed: $([ $total_secrets -eq $valid_secrets ] && echo "true" || echo "false")
EOF
    
    log "✅ Secrets validation completed: $validation_report"
    
    if [[ $total_secrets -eq $valid_secrets ]]; then
        log "🎉 All secrets validated successfully"
    else
        log "❌ Some secrets failed validation"
        return 1
    fi
}

# Create secrets rotation script
create_rotation_script() {
    log "Creating secrets rotation automation..."
    
    cat > "$PROJECT_ROOT/scripts/rotate-secrets.sh" << 'EOF'
#!/bin/bash
# Automated secrets rotation script

set -euo pipefail

LOG_FILE="/var/log/secrets-rotation-$(date +%Y%m%d).log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

generate_password() {
    openssl rand -base64 32 | tr -d "=+/" | cut -c1-32
}

rotate_secret() {
    local secret_name="$1"
    local new_value="$2"
    
    log "Rotating secret: $secret_name"
    
    # Remove old secret
    if docker secret inspect "$secret_name" >/dev/null 2>&1; then
        # Get services using this secret
        local services
        services=$(docker service ls --format "{{.Name}}" | xargs -I {} docker service inspect {} --format '{{.Spec.TaskTemplate.ContainerSpec.Secrets}}' | grep -l "$secret_name" | wc -l || echo "0")
        
        if [[ $services -gt 0 ]]; then
            log "Warning: $services services are using $secret_name"
            log "Manual intervention required for rotation"
            return 1
        fi
        
        docker secret rm "$secret_name"
        sleep 2
    fi
    
    # Create new secret
    echo "$new_value" | docker secret create "$secret_name" -
    log "✅ Secret $secret_name rotated successfully"
}

# Rotate non-critical secrets (quarterly)
rotate_secret "grafana_admin_password" "$(generate_password)"
rotate_secret "traefik_dashboard_password" "$(htpasswd -nbB admin $(generate_password 16) | cut -d: -f2)"

log "✅ Secrets rotation completed"
EOF
    
    chmod +x "$PROJECT_ROOT/scripts/rotate-secrets.sh"
    
    # Schedule quarterly rotation (first day of quarter at 3 AM)
    local rotation_cron="0 3 1 1,4,7,10 * $PROJECT_ROOT/scripts/rotate-secrets.sh"
    if ! crontab -l 2>/dev/null | grep -q "rotate-secrets.sh"; then
        (crontab -l 2>/dev/null; echo "$rotation_cron") | crontab -
        log "✅ Quarterly secrets rotation scheduled"
    fi
}

# Generate comprehensive documentation
generate_documentation() {
    log "Generating secrets management documentation..."
    
    local docs_file="$SECRETS_DIR/SECRETS_MANAGEMENT.md"
    cat > "$docs_file" << 'EOF'
# Secrets Management Documentation

## Overview
This document describes the comprehensive secrets management implementation for the HomeAudit infrastructure using Docker Secrets.

## Architecture
- **Docker Secrets**: Encrypted storage and distribution of sensitive data
- **File-based secrets**: Environment variables read from files in `/run/secrets/`
- **Automated rotation**: Quarterly rotation of non-critical secrets
- **Validation**: Regular integrity checks of secrets configuration

## Secrets Inventory

### Database Secrets
- `pg_root_password`: PostgreSQL root password
- `mariadb_root_password`: MariaDB root password
- `redis_password`: Redis authentication password

### Application Secrets
- `nextcloud_db_password`: Nextcloud database password
- `nextcloud_admin_password`: Nextcloud admin user password
- `immich_db_password`: Immich database password
- `paperless_secret_key`: Paperless-NGX secret key
- `vaultwarden_admin_token`: Vaultwarden admin access token
- `grafana_admin_password`: Grafana admin password

### API Tokens
- `ha_api_token`: Home Assistant API token
- `jellyfin_api_key`: Jellyfin API key
- `gitea_secret_key`: Gitea secret key

### TLS Certificates
- `tls_certificate`: TLS certificate for HTTPS
- `tls_private_key`: TLS private key

## Usage in Stack Files

### Environment Variables
```yaml
environment:
  - POSTGRES_PASSWORD_FILE=/run/secrets/pg_root_password
  - MYSQL_PASSWORD_FILE=/run/secrets/nextcloud_db_password
```

### Secrets Section
```yaml
secrets:
  - pg_root_password
  - nextcloud_db_password

# At the bottom of the stack file
secrets:
  pg_root_password:
    external: true
  nextcloud_db_password:
    external: true
```

## Management Commands

### Create Secret
```bash
echo "my-secret-value" | docker secret create my_secret_name -
```

### List Secrets
```bash
docker secret ls
```

### Inspect Secret (metadata only)
```bash
docker secret inspect my_secret_name
```

### Remove Secret
```bash
docker secret rm my_secret_name
```

## Rotation Process
1. Identify services using the secret
2. Plan maintenance window if needed
3. Generate new secret value
4. Remove old secret
5. Create new secret with same name
6. Update services if required (usually automatic)

## Security Best Practices
1. **Never log secret values**
2. **Use Docker Secrets for all sensitive data**
3. **Rotate secrets regularly**
4. **Monitor secret access**
5. **Use strong, unique passwords**
6. **Backup secret metadata (not values)**

## Troubleshooting

### Secret Not Found
- Check if secret exists: `docker secret ls`
- Verify secret name matches stack file
- Ensure secret is marked as external

### Permission Denied
- Check if service has access to secret
- Verify secret is listed in service's secrets section
- Check Docker Swarm permissions

### Service Won't Start
- Check logs: `docker service logs <service-name>`
- Verify secret file path is correct
- Test secret access in container

## Backup and Recovery
- **Metadata backup**: Export secret names and creation dates
- **Values backup**: Store encrypted copies of secret values securely
- **Recovery**: Recreate secrets from encrypted backup values

## Monitoring and Alerts
- Monitor secret creation/deletion
- Alert on failed secret access
- Track secret rotation schedule
- Validate secret integrity regularly
EOF
    
    log "✅ Documentation created: $docs_file"
}

# Main execution
main() {
    case "${1:-complete}" in
        "--collect")
            collect_existing_secrets
            ;;
        "--generate")
            generate_docker_secrets
            create_secrets_mapping
            ;;
        "--update-stacks")
            update_stacks_with_secrets
            ;;
        "--validate")
            validate_secrets
            ;;
        "--rotate")
            create_rotation_script
            ;;
        "--complete"|"")
            log "Starting complete secrets management implementation..."
            collect_existing_secrets
            generate_docker_secrets
            create_secrets_mapping
            update_stacks_with_secrets
            validate_secrets
            create_rotation_script
            generate_documentation
            log "🎉 Complete secrets management implementation finished!"
            ;;
        "--help"|"-h")
            cat << 'EOF'
Complete Secrets Management Implementation

USAGE:
  complete-secrets-management.sh [OPTIONS]

OPTIONS:
  --collect        Collect existing secrets from running containers
  --generate       Generate all required Docker secrets
  --update-stacks  Update stack files to use Docker secrets
  --validate       Validate secrets configuration
  --rotate         Set up secrets rotation automation
  --complete       Run complete implementation (default)
  --help, -h       Show this help message

EXAMPLES:
  # Complete implementation
  ./complete-secrets-management.sh
  
  # Just generate secrets
  ./complete-secrets-management.sh --generate
  
  # Validate current configuration
  ./complete-secrets-management.sh --validate

NOTES:
  - Requires Docker Swarm mode
  - Creates backups before modifying files
  - All secrets are encrypted at rest
  - Documentation generated automatically
EOF
            ;;
        *)
            log "❌ Unknown option: $1"
            log "Use --help for usage information"
            exit 1
            ;;
    esac
}

# Execute main function
main "$@"