#!/bin/bash

##
# Care API - Quality Gates Validator
#
# Valida métricas de qualidade e coverage para deployment
#
# @package Care_API
# @author Descomplicar® Crescimento Digital
# @version 1.0.0
# @since 2025-09-14
##

set -euo pipefail

# Configurações de Quality Gates
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_DIR="$(dirname "$SCRIPT_DIR")"

# Thresholds de qualidade
readonly COVERAGE_MIN=70          # Coverage mínimo global
readonly COVERAGE_CRITICAL=80     # Coverage para classes críticas
readonly COMPLEXITY_MAX=10        # Complexidade ciclomática máxima
readonly DUPLICATION_MAX=5        # Percentagem máxima de duplicação
readonly MAINTAINABILITY_MIN=70   # Índice de manutenibilidade mínimo

# Cores
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m'

log() {
    local level="$1"
    shift
    local message="$*"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')

    case "$level" in
        "INFO")  echo -e "${GREEN}[INFO]${NC}  [$timestamp] $message" ;;
        "WARN")  echo -e "${YELLOW}[WARN]${NC}  [$timestamp] $message" ;;
        "ERROR") echo -e "${RED}[ERROR]${NC} [$timestamp] $message" >&2 ;;
        "DEBUG") echo -e "${BLUE}[DEBUG]${NC} [$timestamp] $message" ;;
        "PASS")  echo -e "${GREEN}[PASS]${NC}  [$timestamp] $message" ;;
        "FAIL")  echo -e "${RED}[FAIL]${NC}  [$timestamp] $message" ;;
    esac
}

# Estrutura para armazenar resultados
declare -A gate_results
declare -A gate_scores
declare -i total_gates=0
declare -i passed_gates=0

# Função para registar resultado de gate
register_gate() {
    local gate_name="$1"
    local status="$2"          # pass|fail|warn
    local score="$3"           # 0-100
    local threshold="$4"       # valor mínimo
    local actual="$5"          # valor actual
    local message="$6"         # mensagem descriptiva

    gate_results["$gate_name"]="$status"
    gate_scores["$gate_name"]="$score"

    ((total_gates++))

    case "$status" in
        "pass")
            ((passed_gates++))
            log "PASS" "✅ $gate_name: $actual (≥ $threshold) - $message"
            ;;
        "warn")
            log "WARN" "⚠️ $gate_name: $actual (< $threshold) - $message"
            ;;
        "fail")
            log "FAIL" "❌ $gate_name: $actual (< $threshold) - $message"
            ;;
    esac
}

# Gate 1: Coverage Global
validate_global_coverage() {
    log "INFO" "Validando coverage global..."

    local clover_file="$PROJECT_DIR/coverage-reports/clover.xml"

    if [[ ! -f "$clover_file" ]]; then
        register_gate "coverage_global" "fail" 0 "$COVERAGE_MIN" "0" "Arquivo clover.xml não encontrado"
        return
    fi

    local coverage=$(php -r "
        \$xml = simplexml_load_file('$clover_file');
        if (\$xml === false) { echo '0'; exit; }
        \$elements = (int) \$xml->project->metrics['elements'];
        \$covered = (int) \$xml->project->metrics['coveredelements'];
        echo \$elements > 0 ? round((\$covered / \$elements) * 100, 2) : 0;
    ")

    local status="pass"
    if (( $(echo "$coverage < $COVERAGE_MIN" | bc -l) )); then
        status="fail"
    elif (( $(echo "$coverage < $(($COVERAGE_MIN + 10))" | bc -l) )); then
        status="warn"
    fi

    register_gate "coverage_global" "$status" "$coverage" "$COVERAGE_MIN" "${coverage}%" "Coverage global do projecto"
}

# Gate 2: Coverage de Classes Críticas
validate_critical_class_coverage() {
    log "INFO" "Validando coverage de classes críticas..."

    local clover_file="$PROJECT_DIR/coverage-reports/clover.xml"

    if [[ ! -f "$clover_file" ]]; then
        register_gate "coverage_critical" "fail" 0 "$COVERAGE_CRITICAL" "0" "Dados não disponíveis"
        return
    fi

    # Lista de classes críticas (endpoints e serviços principais)
    local critical_classes=(
        "class-auth-endpoints.php"
        "class-patient-endpoints.php"
        "class-appointment-endpoints.php"
        "class-security-manager.php"
    )

    local total_critical=0
    local covered_critical=0
    local low_coverage_classes=()

    for class_file in "${critical_classes[@]}"; do
        local class_coverage=$(php << PHP
<?php
\$cloverFile = '$clover_file';
\$targetClass = '$class_file';

if (!file_exists(\$cloverFile)) { echo '0'; exit; }

\$xml = simplexml_load_file(\$cloverFile);
if (\$xml === false) { echo '0'; exit; }

foreach (\$xml->xpath('//file') as \$file) {
    \$filename = (string) \$file['name'];
    if (strpos(\$filename, \$targetClass) !== false) {
        \$metrics = \$file->metrics;
        if (\$metrics) {
            \$elements = (int) \$metrics['elements'];
            \$covered = (int) \$metrics['coveredelements'];
            echo \$elements > 0 ? round((\$covered / \$elements) * 100, 2) : 0;
            exit;
        }
    }
}
echo '0';
?>
PHP
        )

        ((total_critical++))
        if (( $(echo "$class_coverage >= $COVERAGE_CRITICAL" | bc -l) )); then
            ((covered_critical++))
        else
            low_coverage_classes+=("$class_file:${class_coverage}%")
        fi
    done

    local critical_score=$(( (covered_critical * 100) / total_critical ))
    local status="pass"

    if [[ $covered_critical -lt $((total_critical * 8 / 10)) ]]; then
        status="fail"
    elif [[ $covered_critical -lt $total_critical ]]; then
        status="warn"
    fi

    local message="$covered_critical/$total_critical classes críticas"
    if [[ ${#low_coverage_classes[@]} -gt 0 ]]; then
        message="$message (baixa: ${low_coverage_classes[*]})"
    fi

    register_gate "coverage_critical" "$status" "$critical_score" "80" "${critical_score}%" "$message"
}

# Gate 3: Complexidade Ciclomática
validate_cyclomatic_complexity() {
    log "INFO" "Validando complexidade ciclomática..."

    # Usar PHPLOC se disponível
    if ! command -v phploc >/dev/null 2>&1; then
        register_gate "complexity" "warn" 50 "$COMPLEXITY_MAX" "N/A" "PHPLOC não disponível"
        return
    fi

    local complexity_report="$PROJECT_DIR/coverage-reports/metrics/phploc.txt"

    if [[ ! -f "$complexity_report" ]]; then
        phploc "$PROJECT_DIR/src" > "$complexity_report" 2>/dev/null || {
            register_gate "complexity" "warn" 50 "$COMPLEXITY_MAX" "N/A" "Erro ao gerar relatório"
            return
        }
    fi

    # Extrair complexidade média
    local avg_complexity=$(grep "Average Complexity" "$complexity_report" | awk '{print $4}' | head -1)
    avg_complexity=${avg_complexity:-10}

    local status="pass"
    local score=100

    if (( $(echo "$avg_complexity > $COMPLEXITY_MAX" | bc -l) )); then
        status="fail"
        score=$((100 - ($(echo "($avg_complexity - $COMPLEXITY_MAX) * 10" | bc -l | cut -d'.' -f1))))
        [[ $score -lt 0 ]] && score=0
    elif (( $(echo "$avg_complexity > $(($COMPLEXITY_MAX - 2))" | bc -l) )); then
        status="warn"
        score=75
    fi

    register_gate "complexity" "$status" "$score" "$COMPLEXITY_MAX" "$avg_complexity" "Complexidade ciclomática média"
}

# Gate 4: Duplicação de Código
validate_code_duplication() {
    log "INFO" "Validando duplicação de código..."

    if ! command -v phpcpd >/dev/null 2>&1; then
        register_gate "duplication" "warn" 50 "$DUPLICATION_MAX" "N/A" "PHPCPD não disponível"
        return
    fi

    local duplication_report="$PROJECT_DIR/coverage-reports/metrics/phpcpd.txt"

    if [[ ! -f "$duplication_report" ]]; then
        phpcpd "$PROJECT_DIR/src" > "$duplication_report" 2>/dev/null || {
            register_gate "duplication" "pass" 100 "$DUPLICATION_MAX" "0%" "Nenhuma duplicação encontrada"
            return
        }
    fi

    # Extrair percentagem de duplicação
    local duplication_percent=$(grep -oP '\d+\.\d+(?=% duplicated lines)' "$duplication_report" | head -1)
    duplication_percent=${duplication_percent:-0}

    local status="pass"
    local score=100

    if (( $(echo "$duplication_percent > $DUPLICATION_MAX" | bc -l) )); then
        status="fail"
        score=$((100 - $(echo "$duplication_percent * 10" | bc -l | cut -d'.' -f1)))
        [[ $score -lt 0 ]] && score=0
    elif (( $(echo "$duplication_percent > $((DUPLICATION_MAX - 2))" | bc -l) )); then
        status="warn"
        score=75
    fi

    register_gate "duplication" "$status" "$score" "$DUPLICATION_MAX" "${duplication_percent}%" "Código duplicado"
}

# Gate 5: Sintaxe e Standards
validate_code_standards() {
    log "INFO" "Validando padrões de código..."

    cd "$PROJECT_DIR"

    # Executar PHPCS
    local phpcs_output=$(composer run phpcs 2>&1 || true)
    local phpcs_errors=$(echo "$phpcs_output" | grep -oP '\d+(?= ERRORS?)' | head -1)
    local phpcs_warnings=$(echo "$phpcs_output" | grep -oP '\d+(?= WARNINGS?)' | head -1)

    phpcs_errors=${phpcs_errors:-0}
    phpcs_warnings=${phpcs_warnings:-0}

    local total_issues=$((phpcs_errors + phpcs_warnings))
    local status="pass"
    local score=100

    if [[ $phpcs_errors -gt 0 ]]; then
        status="fail"
        score=$((100 - (phpcs_errors * 10)))
        [[ $score -lt 0 ]] && score=0
    elif [[ $phpcs_warnings -gt 5 ]]; then
        status="warn"
        score=$((100 - (phpcs_warnings * 2)))
        [[ $score -lt 60 ]] && score=60
    fi

    register_gate "code_standards" "$status" "$score" "0" "${total_issues}" "${phpcs_errors} erros, ${phpcs_warnings} avisos"
}

# Gate 6: Cobertura de Testes por Suite
validate_test_suites_coverage() {
    log "INFO" "Validando cobertura por suite de testes..."

    local suites=("unit" "integration" "contract")
    local suite_thresholds=(85 70 75)  # Thresholds específicos por suite

    for i in "${!suites[@]}"; do
        local suite="${suites[i]}"
        local threshold="${suite_thresholds[i]}"
        local suite_clover="$PROJECT_DIR/coverage-${suite}.xml"

        if [[ -f "$suite_clover" ]]; then
            local coverage=$(php -r "
                \$xml = simplexml_load_file('$suite_clover');
                if (\$xml === false) { echo '0'; exit; }
                \$elements = (int) \$xml->project->metrics['elements'];
                \$covered = (int) \$xml->project->metrics['coveredelements'];
                echo \$elements > 0 ? round((\$covered / \$elements) * 100, 2) : 0;
            ")

            local status="pass"
            if (( $(echo "$coverage < $threshold" | bc -l) )); then
                status=$( [[ $suite == "unit" ]] && echo "fail" || echo "warn" )
            fi

            register_gate "coverage_${suite}" "$status" "$coverage" "$threshold" "${coverage}%" "Suite $suite"
        else
            register_gate "coverage_${suite}" "warn" 0 "$threshold" "N/A" "Suite $suite - dados não disponíveis"
        fi
    done
}

# Função para gerar relatório final
generate_quality_report() {
    log "INFO" "📊 RELATÓRIO DE QUALITY GATES"

    local overall_score=$((passed_gates * 100 / total_gates))
    local status_emoji

    if [[ $overall_score -ge 80 ]]; then
        status_emoji="🟢"
    elif [[ $overall_score -ge 60 ]]; then
        status_emoji="🟡"
    else
        status_emoji="🔴"
    fi

    echo ""
    echo "======================================"
    echo "$status_emoji QUALITY GATES SUMMARY"
    echo "======================================"
    echo "Passed Gates: $passed_gates/$total_gates"
    echo "Overall Score: $overall_score%"
    echo ""

    # Detalhar cada gate
    for gate in "${!gate_results[@]}"; do
        local status="${gate_results[$gate]}"
        local score="${gate_scores[$gate]}"

        case "$status" in
            "pass") echo "✅ $gate: $score%" ;;
            "warn") echo "⚠️ $gate: $score%" ;;
            "fail") echo "❌ $gate: $score%" ;;
        esac
    done

    echo ""

    # Determinar se deployment pode prosseguir
    local can_deploy=true
    local blocking_gates=()

    for gate in "${!gate_results[@]}"; do
        if [[ "${gate_results[$gate]}" == "fail" ]]; then
            # Gates críticos que bloqueiam deployment
            case "$gate" in
                "coverage_global"|"code_standards"|"coverage_critical")
                    can_deploy=false
                    blocking_gates+=("$gate")
                    ;;
            esac
        fi
    done

    if [[ "$can_deploy" == "true" ]]; then
        log "PASS" "🚀 DEPLOYMENT AUTORIZADO - Todos os gates críticos passaram"
        return 0
    else
        log "FAIL" "🚫 DEPLOYMENT BLOQUEADO - Gates críticos falharam: ${blocking_gates[*]}"
        return 1
    fi
}

# Função principal
main() {
    local mode="${1:-validate}"

    log "INFO" "🏥 Care API - Quality Gates Validator iniciado"

    cd "$PROJECT_DIR"

    case "$mode" in
        "validate")
            # Executar todos os quality gates
            validate_global_coverage
            validate_critical_class_coverage
            validate_cyclomatic_complexity
            validate_code_duplication
            validate_code_standards
            validate_test_suites_coverage

            # Gerar relatório final
            generate_quality_report
            ;;
        "coverage-only")
            validate_global_coverage
            validate_critical_class_coverage
            validate_test_suites_coverage
            generate_quality_report
            ;;
        "standards-only")
            validate_code_standards
            validate_cyclomatic_complexity
            validate_code_duplication
            generate_quality_report
            ;;
        *)
            echo "Uso: $0 {validate|coverage-only|standards-only}"
            echo ""
            echo "  validate       - Executar todos os quality gates"
            echo "  coverage-only  - Validar apenas coverage"
            echo "  standards-only - Validar apenas padrões de código"
            echo ""
            exit 1
            ;;
    esac
}

# Executar se chamado directamente
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
fi