<?php
// =============================================================================
// ops_checks.php - shared operational/security self-check helpers
// =============================================================================

require_once __DIR__ . '/config.php';
require_once __DIR__ . '/admin_security.php';

function ops_table_exists(PDO $db, string $table): bool {
    $stmt = $db->prepare("
        SELECT COUNT(*)
        FROM information_schema.TABLES
        WHERE TABLE_SCHEMA = DATABASE()
          AND TABLE_NAME = :table
    ");
    $stmt->execute([':table' => $table]);
    return (int)$stmt->fetchColumn() > 0;
}

function ops_column_exists(PDO $db, string $table, string $column): bool {
    $stmt = $db->prepare("
        SELECT COUNT(*)
        FROM information_schema.COLUMNS
        WHERE TABLE_SCHEMA = DATABASE()
          AND TABLE_NAME = :table
          AND COLUMN_NAME = :column
    ");
    $stmt->execute([':table' => $table, ':column' => $column]);
    return (int)$stmt->fetchColumn() > 0;
}

function ops_check(string $category, string $label, bool $ok, string $detail, string $fix = '', string $severity = 'warn', string $evidence = ''): array {
    return [
        'category' => $category,
        'label' => $label,
        'ok' => $ok,
        'detail' => $detail,
        'fix' => $fix,
        'severity' => $severity,
        'evidence' => $evidence,
    ];
}

function collect_ops_checks(): array {
    $checks = [];

    try {
        $db = get_db();
        $checks[] = ops_check('서버', 'DB 연결', true, DB_NAME . ' 연결 성공', '', 'critical', 'setup_wizard.php 또는 security_evidence.php 화면');

        $required_tables = ['sensor_log', 'sensor_status', 'sensor_metric', 'sms_log'];
        foreach ($required_tables as $table) {
            $exists = ops_table_exists($db, $table);
            $checks[] = ops_check(
                'DB',
                "테이블 {$table}",
                $exists,
                $exists ? '확인됨' : '누락됨',
                'sql/schema_sht30.sql 실행',
                'critical',
                'DB 테이블 목록 또는 설치 점검 화면'
            );
        }

        // 최근 온습도 측정 수신 여부(센서 동작/수집 확인)
        if (ops_table_exists($db, 'sensor_metric')) {
            $recent_metric_stmt = $db->query("
                SELECT COUNT(*)
                FROM sensor_metric
                WHERE measured_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR)
            ");
            $recent_metric = (int)$recent_metric_stmt->fetchColumn();
            $checks[] = ops_check('센서', '최근 1시간 온습도 수신', $recent_metric > 0, "{$recent_metric}건", 'SHT30 기기 전원/네트워크/배선 및 sensor_data.php 수신 확인', 'warn', 'sensor_metric 최근 수신');
        }

        if (ops_table_exists($db, 'sms_log')) {
            $recent_sms_fail_stmt = $db->query("
                SELECT COUNT(*)
                FROM sms_log
                WHERE status = 'fail'
                  AND sent_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
            ");
            $recent_sms_fail = (int)$recent_sms_fail_stmt->fetchColumn();
            $checks[] = ops_check('SMS', '최근 30일 SMS 실패', $recent_sms_fail === 0, "{$recent_sms_fail}건", 'Cafe24 SMS 계정, 잔액, 발신번호, 수신자 확인', 'warn', 'sms_log 최근 실패 건수');
        }
    } catch (Throwable $e) {
        $checks[] = ops_check('서버', 'DB 연결', false, '연결 실패: ' . $e->getMessage(), 'config.local.php DB 설정 확인', 'critical', 'DB 연결 오류 화면');
    }

    $default_recipients = count(SMS_RECIPIENTS) === 1 && preg_replace('/\D+/', '', SMS_RECIPIENTS[0]) === '01000000000';
    $checks[] = ops_check('설정', 'API 키', API_KEY !== 'change-this-api-key', 'API_KEY 운영값 설정 여부', 'config.local.php와 /etc/sht30-sensor.env(또는 펌웨어 secrets.h)의 API 키를 동일하게 설정', 'critical', 'config.local.php 원문 미공개 설정 확인');
    $checks[] = ops_check('설정', '관리자 비밀번호', ADMIN_PASSWORD_HASH !== '', 'ADMIN_PASSWORD_HASH 설정 여부', 'php setup_hash.php "new-password" 실행 후 config.local.php에 반영', 'critical', '관리자 해시 설정 확인');
    $checks[] = ops_check('관리자 인증', '관리자 MFA', admin_mfa_configured(), 'ADMIN_TOTP_SECRET 설정 여부', '인증 앱용 Base32 비밀키를 config.local.php에 설정', 'critical', 'MFA 등록 확인표');
    $checks[] = ops_check('관리자 인증', '감사로그', is_file(admin_audit_log_path()) || is_writable(__DIR__ . '/var'), admin_audit_log_path(), '로그인 성공/실패, 로그아웃, 관리자 활동 기록 확인', 'warn', 'php/var/admin_audit.log');
    $checks[] = ops_check('개인정보', 'SMS 수신자', !empty(SMS_RECIPIENTS) && !$default_recipients, count(SMS_RECIPIENTS) . '명 설정', 'config.local.php의 SMS_RECIPIENTS 현행화', 'critical', '수신자 목록 현행화 확인표');
    $thr_ok = (METRIC_TEMP_LOW_C < METRIC_TEMP_HIGH_C) && (METRIC_RH_LOW < METRIC_RH_HIGH);
    $checks[] = ops_check('설정', '온습도 임계치', $thr_ok, sprintf('온도 %s~%s℃ / 습도 %s~%s%%', METRIC_TEMP_LOW_C, METRIC_TEMP_HIGH_C, METRIC_RH_LOW, METRIC_RH_HIGH), 'config.local.php의 METRIC_* 임계치를 운영값으로 설정(저온<고온, 저습<고습)', 'warn', '임계치 설정 확인');
    $checks[] = ops_check('파일', '상태 파일 폴더', is_dir(__DIR__ . '/var') && is_writable(__DIR__ . '/var'), __DIR__ . '/var', 'php/var 생성 및 쓰기 권한 확인', 'warn', '폴더 권한 화면');
    $checks[] = ops_check('보관기간', '보관기간 정리', is_file(__DIR__ . '/retention_cleanup.php'), 'SMS ' . SMS_LOG_RETENTION_DAYS . '일 / 센서로그 ' . SENSOR_LOG_RETENTION_DAYS . '일 / 측정 ' . SENSOR_METRIC_RETENTION_DAYS . '일', 'php retention_cleanup.php --dry-run 결과 확인 후 --execute 수행', 'warn', 'retention_cleanup.php 실행 결과');
    $checks[] = ops_check('백업', '백업 증적', is_file(dirname(__DIR__) . '/scripts/backup_evidence.php'), 'scripts/backup_evidence.php', '백업 파일 목록과 복구 테스트 결과를 Markdown으로 보관', 'warn', 'backup-evidence 보고서');

    return $checks;
}

function ops_failed_count(array $checks): int {
    return count(array_filter($checks, fn($check) => !$check['ok']));
}
