#!/bin/bash
#
# SSL Certificate Setup Script
# Runs only the nginx-proxy role to set up SSL certificates for production domain
#
# Usage: ./setup-ssl.sh [OPTIONS]
#

set -euo pipefail

# Script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INFRA_DIR="${SCRIPT_DIR}/infrastructure"

# Default values
ENVIRONMENT="production"
DOMAIN_NAME="michaelschiemer.de"
VAULT_PASSWORD_FILE=""

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

# Logging functions
log_info() {
    echo -e "${BLUE}[INFO]${NC} $1" >&2
}

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

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

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

# Help function
show_help() {
    cat << EOF
SSL Certificate Setup Script for Custom PHP Framework

USAGE:
    $0 [OPTIONS]

OPTIONS:
    --domain DOMAIN       Domain name (default: $DOMAIN_NAME)
    --vault-password-file FILE  Specify vault password file path
    --help               Show this help message

EXAMPLES:
    # Setup SSL for default domain
    $0

    # Setup SSL for custom domain
    $0 --domain example.com

ENVIRONMENT VARIABLES:
    ANSIBLE_VAULT_PASSWORD_FILE  Vault password file (overrides --vault-password-file)
    DOMAIN_NAME                  Domain name (overrides --domain)

REQUIREMENTS:
    - Ansible 2.9+
    - SSH access to production server
    - Domain must point to production server IP
    - Vault password file or ANSIBLE_VAULT_PASSWORD_FILE environment variable
EOF
}

# Parse command line arguments
parse_args() {
    while [[ $# -gt 0 ]]; do
        case $1 in
            --help|-h)
                show_help
                exit 0
                ;;
            --domain)
                if [[ -z "${2:-}" ]] || [[ "$2" =~ ^-- ]]; then
                    log_error "--domain requires a domain name"
                    exit 1
                fi
                DOMAIN_NAME="$2"
                shift 2
                ;;
            --vault-password-file)
                if [[ -z "${2:-}" ]] || [[ "$2" =~ ^-- ]]; then
                    log_error "--vault-password-file requires a file path"
                    exit 1
                fi
                VAULT_PASSWORD_FILE="$2"
                shift 2
                ;;
            -*)
                log_error "Unknown option: $1"
                show_help
                exit 1
                ;;
            *)
                log_error "Unexpected positional argument: $1"
                show_help
                exit 1
                ;;
        esac
    done
}

# Validate environment and requirements
validate_environment() {
    log_info "Validating SSL setup environment..."

    # Override with environment variables if set
    DOMAIN_NAME="${DOMAIN_NAME:-michaelschiemer.de}"

    # Check if ansible is available
    if ! command -v ansible-playbook &> /dev/null; then
        log_error "ansible-playbook not found. Please install Ansible."
        exit 1
    fi

    # Check vault password file
    if [[ -n "${ANSIBLE_VAULT_PASSWORD_FILE:-}" ]]; then
        VAULT_PASSWORD_FILE="$ANSIBLE_VAULT_PASSWORD_FILE"
    fi

    if [[ -z "$VAULT_PASSWORD_FILE" ]]; then
        log_warning "No vault password file specified. Ansible will prompt for vault password."
    elif [[ ! -f "$VAULT_PASSWORD_FILE" ]]; then
        log_error "Vault password file not found: $VAULT_PASSWORD_FILE"
        exit 1
    fi

    # Check infrastructure directory
    if [[ ! -d "$INFRA_DIR" ]]; then
        log_error "Infrastructure directory not found: $INFRA_DIR"
        exit 1
    fi

    # Check inventory file
    local inventory_file="${INFRA_DIR}/inventories/production/hosts.yml"
    if [[ ! -f "$inventory_file" ]]; then
        log_error "Production inventory not found: $inventory_file"
        exit 1
    fi

    log_success "Environment validation complete"
}

# Check domain DNS resolution
check_dns_resolution() {
    log_info "Checking DNS resolution for $DOMAIN_NAME..."
    
    if command -v dig &> /dev/null; then
        local resolved_ip=$(dig +short "$DOMAIN_NAME" | head -1)
        log_info "Domain $DOMAIN_NAME resolves to: $resolved_ip"
    elif command -v nslookup &> /dev/null; then
        log_info "Checking DNS with nslookup..."
        nslookup "$DOMAIN_NAME"
    else
        log_warning "No DNS lookup tools available, skipping DNS check"
    fi
}

# Create SSL-specific playbook
create_ssl_playbook() {
    local ssl_playbook="${INFRA_DIR}/ssl-setup.yml"
    
    log_info "Creating SSL setup playbook..."
    
    cat > "$ssl_playbook" << 'EOF'
---
# SSL Certificate Setup Playbook
# Sets up nginx with Let's Encrypt SSL certificates

- name: Setup SSL Certificates for Custom PHP Framework
  hosts: web_servers
  become: true
  gather_facts: true
  
  vars:
    deploy_env: "{{ environment }}"
    ssl_provider: letsencrypt
    letsencrypt_enabled: true
    nginx_enabled: true
    
  pre_tasks:
    - name: Display SSL setup information
      debug:
        msg:
          - "Setting up SSL certificates for {{ domain_name }}"
          - "Environment: {{ environment | upper }}"
          - "SSL Provider: {{ ssl_provider }}"
          - "Target Host: {{ inventory_hostname }}"
      tags: always

    - name: Verify SSL requirements
      assert:
        that:
          - domain_name is defined
          - ssl_email is defined
          - ssl_provider == 'letsencrypt'
          - environment == 'production'
        fail_msg: "SSL requirements not met"
        success_msg: "SSL requirements verified"
      tags: always

    - name: Test domain connectivity
      uri:
        url: "http://{{ domain_name }}"
        method: HEAD
        status_code: [200, 301, 302, 404, 500]
        timeout: 10
      register: domain_test
      ignore_errors: true
      tags: always

    - name: Display domain connectivity results
      debug:
        msg:
          - "Domain connectivity test: {{ 'OK' if domain_test.status is defined else 'Failed' }}"
          - "Status code: {{ domain_test.status | default('N/A') }}"
      tags: always

  roles:
    # Install nginx with SSL certificates
    - role: nginx-proxy
      tags:
        - nginx
        - ssl
        - letsencrypt

  post_tasks:
    - name: Test SSL certificate installation
      command: nginx -t
      register: nginx_test
      changed_when: false
      tags: verification

    - name: Check nginx service status
      service_facts:
      tags: verification

    - name: Verify nginx is running
      assert:
        that:
          - ansible_facts.services['nginx.service'].state == 'running'
        fail_msg: "Nginx is not running properly"
        success_msg: "Nginx is running successfully"
      tags: verification

    - name: Test HTTPS connectivity
      uri:
        url: "https://{{ domain_name }}/health"
        method: GET
        status_code: [200, 404, 500]
        timeout: 30
        validate_certs: true
      register: https_test
      retries: 3
      delay: 10
      until: https_test.status is defined
      tags: verification

    - name: Display SSL setup results
      debug:
        msg:
          - "=== SSL CERTIFICATE SETUP COMPLETED ==="
          - "Domain: {{ domain_name }}"
          - "SSL Provider: {{ ssl_provider }}"
          - "Nginx Config: {{ 'Valid' if nginx_test.rc == 0 else 'Invalid' }}"
          - "Nginx Status: {{ ansible_facts.services['nginx.service'].state }}"
          - "HTTPS Test: {{ 'Success' if https_test.status is defined else 'Failed' }}"
          - "HTTPS URL: https://{{ domain_name }}"
          - "======================================"
      tags: always
EOF

    log_success "SSL setup playbook created: $ssl_playbook"
}

# Execute SSL setup
run_ssl_setup() {
    log_info "Starting SSL certificate setup..."

    local ansible_cmd="ansible-playbook"
    local inventory="${INFRA_DIR}/inventories/production/hosts.yml"
    local playbook="${INFRA_DIR}/ssl-setup.yml"
    local extra_vars="-e domain_name=$DOMAIN_NAME -e environment=production -e deploy_env=production"

    # Build ansible command
    local cmd="$ansible_cmd -i $inventory $playbook $extra_vars"

    # Add vault password file if specified
    if [[ -n "$VAULT_PASSWORD_FILE" ]]; then
        cmd+=" --vault-password-file $VAULT_PASSWORD_FILE"
    fi

    # Add tags to focus on SSL setup
    cmd+=" --tags nginx,ssl,letsencrypt,verification"

    # Change to infrastructure directory
    cd "$INFRA_DIR"

    log_info "Executing: $cmd"
    
    # Run SSL setup
    if eval "$cmd"; then
        log_success "SSL setup completed successfully!"
        log_success "HTTPS is now available at: https://$DOMAIN_NAME"
        return 0
    else
        log_error "SSL setup failed!"
        return 1
    fi
}

# Cleanup function
cleanup() {
    local exit_code=$?
    if [[ $exit_code -ne 0 ]]; then
        log_error "SSL setup failed with exit code: $exit_code"
        log_info "Check the logs above for details"
    fi
    
    # Clean up temporary playbook
    if [[ -f "${INFRA_DIR}/ssl-setup.yml" ]]; then
        rm -f "${INFRA_DIR}/ssl-setup.yml"
        log_info "Cleaned up temporary SSL playbook"
    fi
    
    exit $exit_code
}

# Main execution
main() {
    # Set trap for cleanup
    trap cleanup EXIT

    # Parse command line arguments
    parse_args "$@"

    # Validate environment
    validate_environment

    # Check DNS resolution
    check_dns_resolution

    # Create SSL-specific playbook
    create_ssl_playbook

    # Run SSL setup
    run_ssl_setup

    log_success "SSL certificate setup completed successfully!"
    log_info "You can now access your site securely at: https://$DOMAIN_NAME"
}

# Execute main function if script is run directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
fi