<?php
/**
 * AURA FARMING — Etapa 19
 * Câmera segura para contas menores.
 *
 * O navegador processa localmente as poses. O backend recebe somente o estado
 * resumido de segurança; vídeo, imagem e landmarks não são enviados.
 */

function afSafeCameraConfig(PDO $pdo, string $key, mixed $default = null): mixed
{
    if (function_exists('afMinorPolicyConfig')) {
        return afMinorPolicyConfig($pdo, $key, $default);
    }
    return $default;
}

function afSafeCameraProfile(PDO $pdo, int $usuarioId): array
{
    $ctx = function_exists('afMinorPolicyContext') ? afMinorPolicyContext($pdo, $usuarioId) : [];
    $isMinor = !empty($ctx['is_minor']);
    $required = $isMinor;
    $ageBand = (string)($ctx['age_band'] ?? ($isMinor ? 'unverified' : 'adult'));
    $privacy = ($isMinor && function_exists('afCameraPrivacySettingsEnsure')) ? afCameraPrivacySettingsEnsure($pdo,$usuarioId) : [];

    return [
        'required' => $required,
        'is_minor' => $isMinor,
        'age_band' => $ageBand,
        'policy_version' => (string)afSafeCameraConfig($pdo, 'minor_safe_camera_policy_version', '1.0'),
        'person_limit' => max(1, min(2, (int)afSafeCameraConfig($pdo, 'minor_safe_camera_person_limit', 1))),
        'blur_enabled' => $required && !empty($privacy['blur_background']) && filter_var(afSafeCameraConfig($pdo, 'minor_safe_camera_blur_enabled', true), FILTER_VALIDATE_BOOLEAN),
        'pause_on_multiple_people' => $required && filter_var(afSafeCameraConfig($pdo, 'minor_safe_camera_pause_on_multiple_people', true), FILTER_VALIDATE_BOOLEAN),
        'pause_on_no_person' => $required && filter_var(afSafeCameraConfig($pdo, 'minor_safe_camera_pause_on_no_person', true), FILTER_VALIDATE_BOOLEAN),
        'allow_fallback' => !$required || filter_var(afSafeCameraConfig($pdo, 'minor_safe_camera_allow_fallback', false), FILTER_VALIDATE_BOOLEAN),
        'no_person_grace_ms' => max(0, min(15000, (int)afSafeCameraConfig($pdo, 'minor_safe_camera_no_person_grace_ms', 1800))),
        'multiple_people_grace_ms' => max(0, min(10000, (int)afSafeCameraConfig($pdo, 'minor_safe_camera_multiple_people_grace_ms', 700))),
        'stable_ready_ms' => max(500, min(10000, (int)afSafeCameraConfig($pdo, 'minor_safe_camera_stable_ready_ms', 1200))),
        'lighting_check_enabled' => $required && filter_var(afSafeCameraConfig($pdo, 'minor_safe_camera_lighting_check_enabled', true), FILTER_VALIDATE_BOOLEAN),
        'lighting_min_score' => max(10, min(80, (int)afSafeCameraConfig($pdo, 'minor_safe_camera_lighting_min_score', 28))),
        'lighting_max_score' => max(60, min(100, (int)afSafeCameraConfig($pdo, 'minor_safe_camera_lighting_max_score', 96))),
        'additional_face_blur' => $required && !empty($privacy['blur_additional_faces']) && filter_var(afSafeCameraConfig($pdo, 'minor_safe_camera_additional_face_blur', true), FILTER_VALIDATE_BOOLEAN),
        'recording_enabled' => !$required || !empty($privacy['recording_enabled']),
        'processing_mode' => $required ? (string)($privacy['processing_mode'] ?? 'device') : 'device',
        'webrtc_allowed' => !$required || !empty($privacy['transmission_allowed']),
    ];
}

function afSafeCameraReasonMessage(string $reason): string
{
    return match ($reason) {
        'no_person' => 'Entre sozinho no enquadramento para continuar pontuando.',
        'multiple_people' => 'Mais de uma pessoa foi detectada. A pontuação foi pausada.',
        'tab_hidden' => 'Volte para a tela da partida para continuar pontuando.',
        'camera_lost' => 'A câmera foi interrompida. Reative-a para continuar.',
        'detector_unavailable' => 'A proteção de pessoas não está disponível neste aparelho.',
        'privacy_unavailable' => 'A proteção visual do ambiente não pôde ser ativada.',
        'poor_lighting' => 'A iluminação está insuficiente ou excessiva. Ajuste o ambiente para continuar.',
        'preflight' => 'A câmera segura ainda está verificando o enquadramento.',
        default => 'Câmera segura ativa.',
    };
}

function afSafeCameraIssueToken(PDO $pdo, array $partida, int $usuarioId): array
{
    $profile = afSafeCameraProfile($pdo, $usuarioId);
    if (empty($profile['required'])) {
        return ['token' => '', 'profile' => $profile, 'session' => null];
    }

    $partidaId = (int)($partida['id'] ?? 0);
    if ($partidaId <= 0 || (int)($partida['usuario_id'] ?? 0) !== $usuarioId) {
        throw new RuntimeException('Partida inválida para a câmera segura.');
    }

    $token = bin2hex(random_bytes(32));
    $hash = hash('sha256', $token);
    $privacyMode = !empty($profile['blur_enabled']) ? 'protected_blur' : 'protected';

    $sql = "INSERT INTO af_safe_camera_sessions
        (partida_id,usuario_id,token_hash,policy_version,is_minor,privacy_mode,detector_status,state,pause_reason,person_count,blur_enabled,privacy_rendering,tab_visible,camera_active,lighting_score,lighting_ok,additional_faces_blurred,pause_total_seconds,criado_em,atualizado_em)
        VALUES (?,?,?,?,1,?,'pending','pending','preflight',0,?,0,1,0,0,0,0,0,NOW(),NOW())
        ON DUPLICATE KEY UPDATE
          usuario_id=VALUES(usuario_id),token_hash=VALUES(token_hash),policy_version=VALUES(policy_version),
          is_minor=1,privacy_mode=VALUES(privacy_mode),detector_status='pending',state='pending',pause_reason='preflight',
          person_count=0,blur_enabled=VALUES(blur_enabled),privacy_rendering=0,tab_visible=1,camera_active=0,lighting_score=0,lighting_ok=0,additional_faces_blurred=0,ready_started_at=NULL,
          pause_total_seconds=pause_total_seconds+IF(pause_started_at IS NULL,0,TIMESTAMPDIFF(SECOND,pause_started_at,NOW())),
          pause_started_at=NULL,last_frame_at=NULL,atualizado_em=NOW()";
    $pdo->prepare($sql)->execute([
        $partidaId,
        $usuarioId,
        $hash,
        (string)$profile['policy_version'],
        $privacyMode,
        !empty($profile['blur_enabled']) ? 1 : 0,
    ]);

    $st = $pdo->prepare('SELECT * FROM af_safe_camera_sessions WHERE partida_id=? LIMIT 1');
    $st->execute([$partidaId]);
    $session = $st->fetch() ?: null;
    afSafeCameraInsertEvent($pdo, $session ?: ['id'=>0,'partida_id'=>$partidaId,'usuario_id'=>$usuarioId], 'session_token_issued', 0, ['policy_version'=>$profile['policy_version']]);

    return ['token' => $token, 'profile' => $profile, 'session' => $session];
}

function afSafeCameraFindSession(PDO $pdo, int $usuarioId, string $sessaoId, string $token, bool $forUpdate = false): ?array
{
    if (strlen($token) < 40) return null;
    $sql = "SELECT sc.*,p.sessao_id,p.status partida_status,p.dispositivo_id
            FROM af_safe_camera_sessions sc
            JOIN af_partidas p ON p.id=sc.partida_id
            WHERE sc.usuario_id=? AND p.sessao_id=? LIMIT 1" . ($forUpdate ? ' FOR UPDATE' : '');
    $st = $pdo->prepare($sql);
    $st->execute([$usuarioId, $sessaoId]);
    $row = $st->fetch() ?: null;
    if (!$row || !hash_equals((string)$row['token_hash'], hash('sha256', $token))) return null;
    return $row;
}

function afSafeCameraInsertEvent(PDO $pdo, array $session, string $type, int $personCount = 0, array $details = []): void
{
    $sessionId = (int)($session['id'] ?? 0);
    if ($sessionId <= 0) return;
    try {
        $pdo->prepare("INSERT INTO af_safe_camera_events
            (safe_session_id,partida_id,usuario_id,event_type,person_count,details_json,ip_address,user_agent,device_uuid,criado_em)
            VALUES (?,?,?,?,?,?,?,?,?,NOW())")->execute([
                $sessionId,
                (int)$session['partida_id'],
                (int)$session['usuario_id'],
                substr($type, 0, 60),
                max(0, min(3, $personCount)),
                $details ? json_encode($details, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES) : null,
                clientIp(),
                substr((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 1000),
                deviceUuid(),
            ]);
        if($type==='multiple_people' && function_exists('afSafetyTableExists') && afSafetyTableExists($pdo,'af_third_party_detections')){
            $pdo->prepare("INSERT INTO af_third_party_detections (safe_session_id,partida_id,minor_usuario_id,detected_count,action_taken,details_json,detected_at,created_at) VALUES (?,?,?,?,'scoring_paused',?,NOW(),NOW())")
                ->execute([$sessionId,(int)$session['partida_id'],(int)$session['usuario_id'],max(1,$personCount-1),$details?json_encode($details,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES):null]);
        }
        if(function_exists('afGuardianDashboardCreateAlert')){
            $alert=match($type){
                'multiple_people'=>['critical','Mais de uma pessoa detectada','A câmera segura pausou a pontuação porque detectou mais de uma pessoa.'],
                'poor_lighting'=>['warning','Iluminação inadequada','A câmera segura pausou a pontuação até a iluminação ser corrigida.'],
                'camera_lost'=>['warning','Câmera interrompida','A câmera do dependente foi interrompida durante a sessão.'],
                'detector_unavailable'=>['warning','Proteção indisponível','A detecção de pessoas não ficou disponível durante a sessão.'],
                'privacy_unavailable'=>['critical','Proteção visual indisponível','O modo de privacidade visual não pôde ser mantido e a pontuação foi pausada.'],
                default=>null,
            };
            if($alert)afGuardianDashboardCreateAlert($pdo,(int)$session['usuario_id'],'security_'.$type,$alert[0],$alert[1],$alert[2],['person_count'=>$personCount,'safe_session_id'=>$sessionId],'partida',(int)$session['partida_id']);
        }
        if(function_exists('afStage24SecurityAlert')){
            $stage24Alert=match($type){
                'multiple_people'=>['critical','Câmera segura detectou terceiros','A pontuação foi pausada porque havia mais de uma pessoa no enquadramento.',20],
                'poor_lighting'=>['medium','Iluminação inadequada recorrente','A pontuação foi pausada até a iluminação melhorar.',5],
                'camera_lost'=>['medium','Câmera interrompida','A sessão perdeu a câmera durante uso protegido.',5],
                'privacy_unavailable'=>['critical','Privacidade visual indisponível','O sistema não conseguiu manter a proteção visual exigida.',18],
                'detector_unavailable'=>['high','Detector indisponível','A proteção técnica de pessoas ficou indisponível durante a sessão.',12],
                default=>null,
            };
            if($stage24Alert){
                afStage24SecurityAlert($pdo,'camera_'.$type,$stage24Alert[0],$stage24Alert[1],$stage24Alert[2],(int)$session['usuario_id'],(int)$session['usuario_id'],'safe_camera','partida',(int)$session['partida_id'],['person_count'=>$personCount,'safe_session_id'=>$sessionId,'details'=>$details],(int)$stage24Alert[3]);
            }
        }
    } catch (Throwable $e) {
        if (defined('APP_DEBUG') && APP_DEBUG) error_log('[AURA-SAFE-CAMERA-EVENT] '.$e->getMessage());
    }
}

function afSafeCameraNormalizeTelemetry(array $data): array
{
    $state = strtolower(preg_replace('/[^a-z_]/', '', (string)($data['safety_state'] ?? 'preflight')));
    $allowedStates = ['preflight','ready','active','no_person','multiple_people','poor_lighting','tab_hidden','camera_lost','detector_unavailable','privacy_unavailable'];
    if (!in_array($state, $allowedStates, true)) $state = 'preflight';
    return [
        'state' => $state,
        'person_count' => max(0, min(3, (int)($data['person_count'] ?? 0))),
        'detector_ready' => !empty($data['detector_ready']),
        'privacy_rendering' => !empty($data['privacy_rendering']),
        'tab_visible' => !array_key_exists('tab_visible', $data) || !empty($data['tab_visible']),
        'camera_active' => !empty($data['camera_active']),
        'lighting_score' => max(0, min(100, (int)($data['lighting_score'] ?? 0))),
        'lighting_ok' => !empty($data['lighting_ok']),
        'additional_faces_blurred' => max(0, min(3, (int)($data['additional_faces_blurred'] ?? 0))),
        'frame_ts' => max(0, (int)($data['safety_frame_ts'] ?? 0)),
    ];
}

function afSafeCameraEvaluate(array $profile, array $telemetry): array
{
    if (empty($profile['required'])) {
        return ['scoring_allowed'=>true,'paused'=>false,'reason'=>'none','message'=>'Câmera padrão.','state'=>'active'];
    }

    $reason = 'none';
    if (empty($telemetry['camera_active'])) $reason = 'camera_lost';
    elseif (empty($telemetry['tab_visible'])) $reason = 'tab_hidden';
    elseif (empty($telemetry['detector_ready'])) $reason = 'detector_unavailable';
    elseif (!empty($profile['blur_enabled']) && empty($telemetry['privacy_rendering'])) $reason = 'privacy_unavailable';
    elseif (!empty($profile['lighting_check_enabled']) && empty($telemetry['lighting_ok'])) $reason = 'poor_lighting';
    elseif (!empty($profile['pause_on_multiple_people']) && (int)$telemetry['person_count'] > (int)$profile['person_limit']) $reason = 'multiple_people';
    elseif (!empty($profile['pause_on_no_person']) && (int)$telemetry['person_count'] < 1) $reason = 'no_person';
    elseif (!in_array((string)$telemetry['state'], ['ready','active'], true)) $reason = (string)$telemetry['state'];

    $allowed = $reason === 'none';
    return [
        'scoring_allowed' => $allowed,
        'paused' => !$allowed,
        'reason' => $allowed ? 'none' : $reason,
        'message' => $allowed ? 'Câmera segura ativa.' : afSafeCameraReasonMessage($reason),
        'state' => $allowed ? ((string)$telemetry['state'] === 'ready' ? 'ready' : 'active') : 'paused',
    ];
}

function afSafeCameraRecord(PDO $pdo, int $usuarioId, string $sessaoId, string $token, array $data): array
{
    $profile = afSafeCameraProfile($pdo, $usuarioId);
    if (empty($profile['required'])) {
        return afSafeCameraEvaluate($profile, afSafeCameraNormalizeTelemetry($data)) + ['required'=>false,'profile'=>$profile];
    }

    $telemetry = afSafeCameraNormalizeTelemetry($data);
    $decision = afSafeCameraEvaluate($profile, $telemetry);

    $pdo->beginTransaction();
    try {
        $session = afSafeCameraFindSession($pdo, $usuarioId, $sessaoId, $token, true);
        if (!$session) throw new RuntimeException('Sessão da câmera segura inválida. Atualize a página.');
        if (!in_array((string)$session['partida_status'], ['aguardando','em_andamento'], true)) {
            throw new RuntimeException('A partida não aceita mais dados da câmera.');
        }

        $oldState = (string)($session['state'] ?? 'pending');
        $oldReason = (string)($session['pause_reason'] ?? 'preflight');
        $newState = (string)$decision['state'];
        $newReason = (string)$decision['reason'];
        $wasPaused = $oldState === 'paused' || $oldReason !== 'none';
        $isPaused = !empty($decision['paused']);
        $pauseStarted = $session['pause_started_at'] ?? null;
        $pauseTotal = (int)($session['pause_total_seconds'] ?? 0);
        if ($wasPaused && !$isPaused && $pauseStarted) {
            $pauseTotal += max(0, time() - strtotime((string)$pauseStarted));
            $pauseStarted = null;
        } elseif (!$wasPaused && $isPaused) {
            $pauseStarted = date('Y-m-d H:i:s');
        } elseif ($isPaused && !$pauseStarted) {
            $pauseStarted = date('Y-m-d H:i:s');
        }

        $multipleInc = (int)($session['multiple_people_incidents'] ?? 0);
        $noPersonInc = (int)($session['no_person_incidents'] ?? 0);
        $visibilityInc = (int)($session['visibility_incidents'] ?? 0);
        $cameraInc = (int)($session['camera_incidents'] ?? 0);
        $lightingInc = (int)($session['lighting_incidents'] ?? 0);
        $transition = $oldState !== $newState || $oldReason !== $newReason;
        if ($transition) {
            if ($newReason === 'multiple_people') $multipleInc++;
            if ($newReason === 'no_person') $noPersonInc++;
            if ($newReason === 'tab_hidden') $visibilityInc++;
            if ($newReason === 'poor_lighting') $lightingInc++;
            if (in_array($newReason, ['camera_lost','detector_unavailable','privacy_unavailable'], true)) $cameraInc++;
        }

        $detectorStatus = $telemetry['detector_ready'] ? 'ready' : ((string)$telemetry['state'] === 'detector_unavailable' ? 'error' : 'pending');
        $readyStarted = !$isPaused
            ? (($wasPaused || empty($session['ready_started_at'])) ? date('Y-m-d H:i:s') : (string)$session['ready_started_at'])
            : null;
        $pdo->prepare("UPDATE af_safe_camera_sessions SET
            detector_status=?,state=?,pause_reason=?,person_count=?,privacy_rendering=?,tab_visible=?,camera_active=?,lighting_score=?,lighting_ok=?,additional_faces_blurred=?,ready_started_at=?,
            pause_started_at=?,pause_total_seconds=?,multiple_people_incidents=?,no_person_incidents=?,visibility_incidents=?,camera_incidents=?,lighting_incidents=?,
            last_frame_at=NOW(),last_event_at=IF(?,NOW(),last_event_at),atualizado_em=NOW()
            WHERE id=?")->execute([
                $detectorStatus,
                $newState,
                $newReason,
                (int)$telemetry['person_count'],
                $telemetry['privacy_rendering'] ? 1 : 0,
                $telemetry['tab_visible'] ? 1 : 0,
                $telemetry['camera_active'] ? 1 : 0,
                (int)$telemetry['lighting_score'],
                $telemetry['lighting_ok'] ? 1 : 0,
                (int)$telemetry['additional_faces_blurred'],
                $readyStarted,
                $pauseStarted,
                $pauseTotal,
                $multipleInc,
                $noPersonInc,
                $visibilityInc,
                $cameraInc,
                $lightingInc,
                $transition ? 1 : 0,
                (int)$session['id'],
            ]);

        $session['state']=$newState;
        $session['pause_reason']=$newReason;
        $session['person_count']=$telemetry['person_count'];
        if ($transition) afSafeCameraInsertEvent($pdo, $session, $newReason === 'none' ? 'resumed' : $newReason, (int)$telemetry['person_count'], [
            'previous_state'=>$oldState,
            'previous_reason'=>$oldReason,
            'privacy_rendering'=>$telemetry['privacy_rendering'],
            'detector_ready'=>$telemetry['detector_ready'],
            'tab_visible'=>$telemetry['tab_visible'],
            'camera_active'=>$telemetry['camera_active'],
            'lighting_score'=>$telemetry['lighting_score'],
            'lighting_ok'=>$telemetry['lighting_ok'],
            'additional_faces_blurred'=>$telemetry['additional_faces_blurred'],
        ]);
        $pdo->commit();

        return $decision + [
            'required'=>true,
            'profile'=>$profile,
            'person_count'=>(int)$telemetry['person_count'],
            'lighting_score'=>(int)$telemetry['lighting_score'],
            'lighting_ok'=>(bool)$telemetry['lighting_ok'],
            'additional_faces_blurred'=>(int)$telemetry['additional_faces_blurred'],
            'pause_total_seconds'=>$pauseTotal,
        ];
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        throw $e;
    }
}

function afSafeCameraAssertReady(PDO $pdo, int $usuarioId, string $sessaoId, string $token): array
{
    $profile = afSafeCameraProfile($pdo, $usuarioId);
    if (empty($profile['required'])) return ['required'=>false,'scoring_allowed'=>true,'paused'=>false,'reason'=>'none'];

    $session = afSafeCameraFindSession($pdo, $usuarioId, $sessaoId, $token, false);
    if (!$session) throw new RuntimeException('Câmera segura não validada. Atualize a página e tente novamente.');
    $fresh = !empty($session['last_frame_at']) && strtotime((string)$session['last_frame_at']) >= time() - 10;
    $stable = !empty($session['ready_started_at']) && strtotime((string)$session['ready_started_at']) <= time() - 1;
    $ready = $fresh && $stable
        && (string)$session['detector_status'] === 'ready'
        && in_array((string)$session['state'], ['ready','active'], true)
        && (string)$session['pause_reason'] === 'none'
        && (int)$session['person_count'] === 1
        && (int)$session['camera_active'] === 1
        && (int)$session['tab_visible'] === 1
        && (empty($profile['lighting_check_enabled']) || (int)$session['lighting_ok'] === 1)
        && (empty($profile['blur_enabled']) || (int)$session['privacy_rendering'] === 1);
    if (!$ready) throw new RuntimeException(afSafeCameraReasonMessage((string)($session['pause_reason'] ?: 'preflight')));
    return ['required'=>true,'scoring_allowed'=>true,'paused'=>false,'reason'=>'none','person_count'=>1,'profile'=>$profile];
}

function afSafeCameraClose(PDO $pdo, int $usuarioId, string $sessaoId, string $token, string $finalState): void
{
    $profile = afSafeCameraProfile($pdo, $usuarioId);
    if (empty($profile['required'])) return;
    $finalState = $finalState === 'cancelled' ? 'cancelled' : 'finished';
    try {
        $session = afSafeCameraFindSession($pdo, $usuarioId, $sessaoId, $token, false);
        if (!$session) return;
        $pdo->prepare("UPDATE af_safe_camera_sessions SET
            pause_total_seconds=pause_total_seconds+IF(pause_started_at IS NULL,0,TIMESTAMPDIFF(SECOND,pause_started_at,NOW())),
            pause_started_at=NULL,state=?,pause_reason='none',camera_active=0,atualizado_em=NOW()
            WHERE id=?")->execute([$finalState,(int)$session['id']]);
        afSafeCameraInsertEvent($pdo,$session,$finalState,(int)($session['person_count']??0));
    } catch (Throwable $e) {
        if (defined('APP_DEBUG') && APP_DEBUG) error_log('[AURA-SAFE-CAMERA-CLOSE] '.$e->getMessage());
    }
}

function afSafeCameraApiState(array $state): array
{
    return [
        'required'=>(bool)($state['required']??false),
        'scoring_allowed'=>(bool)($state['scoring_allowed']??true),
        'paused'=>(bool)($state['paused']??false),
        'reason'=>(string)($state['reason']??'none'),
        'message'=>(string)($state['message']??''),
        'person_count'=>(int)($state['person_count']??0),
        'pause_total_seconds'=>(int)($state['pause_total_seconds']??0),
        'lighting_score'=>(int)($state['lighting_score']??0),
        'lighting_ok'=>(bool)($state['lighting_ok']??false),
        'additional_faces_blurred'=>(int)($state['additional_faces_blurred']??0),
        'policy_version'=>(string)($state['profile']['policy_version']??'1.0'),
        'privacy_mode'=>!empty($state['profile']['blur_enabled'])?'protected_blur':'standard',
    ];
}
