<?php
/**
 * Aura Farming — fechamento técnico das Etapas 16, 18, 19, 20 e 21.
 *
 * Este módulo concentra integrações adicionais sem armazenar documento, selfie,
 * vídeo bruto ou landmarks corporais. Todas as decisões sensíveis continuam
 * validadas no servidor.
 */

function afSafetyTableExists(PDO $pdo, string $table): bool
{
    static $cache=[];
    if(isset($cache[$table])) return $cache[$table];
    try{
        $st=$pdo->prepare('SELECT 1 FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name=? LIMIT 1');
        $st->execute([$table]);
        return $cache[$table]=(bool)$st->fetchColumn();
    }catch(Throwable $e){return $cache[$table]=false;}
}

function afSafetyColumnExists(PDO $pdo, string $table, string $column): bool
{
    static $cache=[];$key=$table.'.'.$column;
    if(isset($cache[$key]))return $cache[$key];
    try{
        $st=$pdo->prepare('SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name=? AND column_name=? LIMIT 1');
        $st->execute([$table,$column]);
        return $cache[$key]=(bool)$st->fetchColumn();
    }catch(Throwable $e){return $cache[$key]=false;}
}

// -----------------------------------------------------------------------------
// Etapa 16 — modo interno sem serviços terceiros.
// As funções abaixo permanecem apenas por compatibilidade com versões antigas.
// Nenhuma chamada externa, documento, selfie, biometria ou SMS é utilizada.
// -----------------------------------------------------------------------------
function afGuardianProfessionalVerificationRequired(PDO $pdo): bool
{
    return false;
}

function afGuardianProfessionalProvider(PDO $pdo): ?array
{
    return null;
}

function afGuardianProfessionalVerificationStatus(PDO $pdo, int $usuarioId): array
{
    return [
        'required'=>false,
        'available'=>false,
        'verified'=>false,
        'provider'=>null,
        'provider_reference'=>null,
        'status'=>'disabled',
    ];
}

function afGuardianStartProfessionalVerification(PDO $pdo, int $usuarioId): array
{
    throw new RuntimeException('A aferição por empresa externa foi removida. O Aura usa verificação interna em camadas e revisão administrativa.');
}

function afGuardianApplyProfessionalResult(PDO $pdo, string $reference, bool $adultVerified, string $providerKey='', array $metadata=[]): bool
{
    return false;
}

// -----------------------------------------------------------------------------
// Etapa 18 — configurações centrais e contatos aprovados.
// -----------------------------------------------------------------------------
function afMinorSafetySettingsEnsure(PDO $pdo, int $minorId): array
{
    if(!afSafetyTableExists($pdo,'af_minor_safety_settings'))return [];
    $pdo->prepare("INSERT IGNORE INTO af_minor_safety_settings (minor_usuario_id,profile_private,alias_only,hide_last_name,hide_city,hide_location,block_stranger_messages,block_public_search,block_public_share,block_real_replay,block_image_download,comments_mode,created_at,updated_at) VALUES (?,1,1,1,1,1,1,1,1,1,1,'disabled',NOW(),NOW())")
        ->execute([$minorId]);
    try{$pdo->prepare('UPDATE af_perfis SET perfil_privado=1,cidade=NULL,estado=NULL WHERE usuario_id=?')->execute([$minorId]);}catch(Throwable $e){}
    if(function_exists('afCameraPrivacySettingsEnsure'))afCameraPrivacySettingsEnsure($pdo,$minorId);
    $st=$pdo->prepare('SELECT * FROM af_minor_safety_settings WHERE minor_usuario_id=? LIMIT 1');$st->execute([$minorId]);
    return $st->fetch()?:[];
}

function afCameraPrivacySettingsEnsure(PDO $pdo,int $minorId): array
{
    if(!afSafetyTableExists($pdo,'af_camera_privacy_settings'))return [];
    $pdo->prepare("INSERT IGNORE INTO af_camera_privacy_settings (minor_usuario_id,blur_background,blur_additional_faces,recording_enabled,processing_mode,transmission_allowed,created_at,updated_at) VALUES (?,1,1,0,'device',0,NOW(),NOW())")->execute([$minorId]);
    $st=$pdo->prepare('SELECT * FROM af_camera_privacy_settings WHERE minor_usuario_id=? LIMIT 1');$st->execute([$minorId]);
    return $st->fetch()?:[];
}

function afMinorFeaturePermissionSet(PDO $pdo,int $minorId,string $feature,bool $allowed,int $guardianId,?int $linkId=null,string $source='guardian_dashboard'): void
{
    if(!afSafetyTableExists($pdo,'af_feature_permissions'))return;
    $feature=afMinorPolicyFeatureAlias($feature);
    $pdo->prepare("INSERT INTO af_feature_permissions (minor_usuario_id,feature_key,allowed,source,guardian_usuario_id,guardian_link_id,changed_at,created_at,updated_at) VALUES (?,?,?,?,?,?,NOW(),NOW(),NOW()) ON DUPLICATE KEY UPDATE allowed=VALUES(allowed),source=VALUES(source),guardian_usuario_id=VALUES(guardian_usuario_id),guardian_link_id=VALUES(guardian_link_id),changed_at=NOW(),updated_at=NOW()")
        ->execute([$minorId,$feature,$allowed?1:0,$source,$guardianId,$linkId]);
}

function afMinorFeaturePermission(PDO $pdo,int $minorId,string $feature): ?bool
{
    if(!afSafetyTableExists($pdo,'af_feature_permissions'))return null;
    $feature=afMinorPolicyFeatureAlias($feature);
    $st=$pdo->prepare('SELECT allowed FROM af_feature_permissions WHERE minor_usuario_id=? AND feature_key=? LIMIT 1');$st->execute([$minorId,$feature]);$v=$st->fetchColumn();
    return $v===false?null:(bool)$v;
}

function afApprovedContactAdd(PDO $pdo,int $guardianId,int $linkId,string $identifier,string $notes=''): array
{
    if(!afGuardianAdultVerified($pdo,$guardianId))throw new RuntimeException('Somente um responsável verificado pode aprovar contatos.');
    if(!function_exists('afGuardianFamilyLinkConfirmed') || !afGuardianFamilyLinkConfirmed($pdo,$linkId))throw new RuntimeException('Confirme o código familiar deste dependente antes de aprovar contatos.');
    $link=afGuardianDashboardLink($pdo,$guardianId,$linkId);$minorId=(int)$link['minor_usuario_id'];
    $identifier=trim($identifier);if($identifier==='')throw new InvalidArgumentException('Informe o ID AURA ou nome de usuário do contato.');
    $st=$pdo->prepare("SELECT id,nome,username,aura_id FROM af_usuarios WHERE status=1 AND (UPPER(aura_id)=UPPER(?) OR LOWER(username)=LOWER(?)) LIMIT 1");$st->execute([$identifier,ltrim($identifier,'@')]);$target=$st->fetch();
    if(!$target)throw new RuntimeException('Contato não encontrado.');
    if((int)$target['id']===$minorId)throw new RuntimeException('Não é possível aprovar a própria conta.');
    $notes=mb_substr(trim($notes),0,500);
    $pdo->prepare("INSERT INTO af_approved_contacts (minor_usuario_id,contact_usuario_id,guardian_usuario_id,guardian_link_id,status,notes,approved_at,created_at,updated_at) VALUES (?,?,?,?,'approved',?,NOW(),NOW(),NOW()) ON DUPLICATE KEY UPDATE guardian_usuario_id=VALUES(guardian_usuario_id),guardian_link_id=VALUES(guardian_link_id),status='approved',notes=VALUES(notes),approved_at=NOW(),revoked_at=NULL,updated_at=NOW()")
        ->execute([$minorId,(int)$target['id'],$guardianId,$linkId,$notes?:null]);
    afGuardianDashboardCreateAlert($pdo,$minorId,'approved_contact','info','Contato aprovado',(string)$target['nome'].' foi autorizado para partidas protegidas.',['contact_user_id'=>(int)$target['id']],'approved_contact',(int)$target['id']);
    afLogSeguranca($pdo,'minor_contact_approved',10,['minor_id'=>$minorId,'contact_id'=>(int)$target['id']],$guardianId);
    if(function_exists('afMinorPolicyClearCache'))afMinorPolicyClearCache($minorId);
    return $target;
}

function afApprovedContactRemove(PDO $pdo,int $guardianId,int $linkId,int $contactId): void
{
    if(!afGuardianAdultVerified($pdo,$guardianId))throw new RuntimeException('Somente um responsável verificado pode remover contatos.');
    if(!function_exists('afGuardianFamilyLinkConfirmed') || !afGuardianFamilyLinkConfirmed($pdo,$linkId))throw new RuntimeException('Confirme o código familiar deste dependente antes de remover contatos.');
    $link=afGuardianDashboardLink($pdo,$guardianId,$linkId);$minorId=(int)$link['minor_usuario_id'];
    $pdo->prepare("UPDATE af_approved_contacts SET status='revoked',revoked_at=NOW(),updated_at=NOW() WHERE minor_usuario_id=? AND contact_usuario_id=? AND guardian_link_id=?")
        ->execute([$minorId,$contactId,$linkId]);
    afLogSeguranca($pdo,'minor_contact_revoked',15,['minor_id'=>$minorId,'contact_id'=>$contactId],$guardianId);
    if(function_exists('afMinorPolicyClearCache'))afMinorPolicyClearCache($minorId);
}

function afApprovedContacts(PDO $pdo,int $guardianId,int $linkId,bool $activeOnly=true): array
{
    $link=afGuardianDashboardLink($pdo,$guardianId,$linkId);$minorId=(int)$link['minor_usuario_id'];
    if(!afSafetyTableExists($pdo,'af_approved_contacts'))return [];
    $sql="SELECT c.*,u.nome,u.username,u.aura_id,u.foto_perfil,u.foto_externa FROM af_approved_contacts c JOIN af_usuarios u ON u.id=c.contact_usuario_id WHERE c.minor_usuario_id=? AND c.guardian_link_id=?".($activeOnly?" AND c.status='approved'":'')." ORDER BY c.updated_at DESC";
    $st=$pdo->prepare($sql);$st->execute([$minorId,$linkId]);return $st->fetchAll()?:[];
}

function afApprovedContactCheck(PDO $pdo,int $minorId,int $contactId): bool
{
    if($minorId<=0||$contactId<=0||!afSafetyTableExists($pdo,'af_approved_contacts'))return false;
    $st=$pdo->prepare("SELECT 1 FROM af_approved_contacts c
        JOIN af_guardian_minor_links l ON l.id=c.guardian_link_id AND l.minor_usuario_id=c.minor_usuario_id AND l.family_confirmed_at IS NOT NULL
        JOIN af_guardian_accounts ga ON ga.usuario_id=l.guardian_usuario_id AND ga.status_verificacao='verified'
        JOIN af_guardian_manual_reviews gr ON gr.guardian_account_id=ga.id AND gr.status='approved' AND (gr.valid_until IS NULL OR gr.valid_until>NOW())
        WHERE c.minor_usuario_id=? AND c.contact_usuario_id=? AND c.status='approved' AND l.status='active' LIMIT 1");
    $st->execute([$minorId,$contactId]);return (bool)$st->fetchColumn();
}

function afMinorBattleContext(PDO $pdo,int $userId,int $opponentId): array
{
    $ctx=afMinorPolicyContext($pdo,$userId);
    return ['opponent_user_id'=>$opponentId,'approved_contact'=>empty($ctx['is_minor'])?true:afApprovedContactCheck($pdo,$userId,$opponentId)];
}

function afCanMinorUseFeature(PDO $pdo,int $userId,string $feature,array $context=[]): bool
{
    return !empty(afMinorPolicyDecision($pdo,$userId,$feature,$context,true)['allowed']);
}

function canMinorUseFeature(int $userId,string $feature,array $context=[]): bool
{
    global $pdo;
    return $pdo instanceof PDO ? afCanMinorUseFeature($pdo,$userId,$feature,$context) : false;
}

function afMinorAccessLog(PDO $pdo,array $decision,array $context=[]): void
{
    if(!afSafetyTableExists($pdo,'af_minor_access_logs'))return;
    try{
        $pdo->prepare('INSERT INTO af_minor_access_logs (minor_usuario_id,feature_key,allowed,reason_code,context_json,ip_address,device_uuid,created_at) VALUES (?,?,?,?,?,?,?,NOW())')
            ->execute([(int)$decision['user_id'],substr((string)$decision['feature'],0,80),!empty($decision['allowed'])?1:0,substr((string)$decision['code'],0,80),$context?json_encode($context,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES):null,clientIp(),deviceUuid()]);
    }catch(Throwable $e){}
}

// -----------------------------------------------------------------------------
// Etapa 20 — decisão por recurso e supervisão real.
// -----------------------------------------------------------------------------
function afGuardianDashboardFeatureColumn(string $feature): ?string
{
    $feature=afMinorPolicyFeatureAlias($feature);
    return match($feature){
        'camera','body_processing'=>'camera_enabled',
        'farm'=>'farm_enabled',
        'battle'=>'battle_enabled',
        'ranking_alias'=>'ranking_enabled',
        'live','public_live'=>'transmission_enabled',
        default=>null,
    };
}

function afGuardianDashboardFeatureDecision(PDO $pdo,int $minorId,string $feature): array
{
    $base=afGuardianDashboardAccessDecision($pdo,$minorId);
    if(empty($base['allowed']))return $base;
    $controls=$base['controls']??[];$column=afGuardianDashboardFeatureColumn($feature);
    if($column&&array_key_exists($column,$controls)&&empty($controls[$column])){
        return array_merge($base,['allowed'=>false,'code'=>'FEATURE_DISABLED_BY_GUARDIAN','message'=>'O responsável desativou este recurso no painel familiar.','feature_column'=>$column]);
    }
    return array_merge($base,['feature_column'=>$column]);
}

function afGuardianDashboardRequestDeletion(PDO $pdo,int $guardianId,int $linkId,string $scope='account'): array
{
    if(!afGuardianAdultVerified($pdo,$guardianId))throw new RuntimeException('Conclua a verificação interna antes de solicitar exclusão em nome do dependente.');
    if(!function_exists('afGuardianFamilyLinkConfirmed') || !afGuardianFamilyLinkConfirmed($pdo,$linkId))throw new RuntimeException('Confirme o código familiar deste dependente antes de solicitar exclusão.');
    $link=afGuardianDashboardLink($pdo,$guardianId,$linkId);$minorId=(int)$link['minor_usuario_id'];
    $type=$scope==='history'?'history_deletion':'account_deletion';
    $description=$scope==='history'?'Solicito a exclusão do histórico de partidas e atividades do dependente.':'Solicito a exclusão integral da conta e dos dados do dependente, conforme avaliação legal e de segurança.';
    if($scope==='account'){
        $pdo->prepare("UPDATE af_guardian_controls SET access_status='paused',alterado_por_usuario_id=?,atualizado_em=NOW() WHERE link_id=? AND guardian_usuario_id=?")->execute([$guardianId,$linkId,$guardianId]);
        if(function_exists('afMinorPolicyClearCache'))afMinorPolicyClearCache($minorId);
        criarNotificacao($pdo,$minorId,'guardian_account_suspended','Conta suspensa pelo responsável','O acesso aos recursos foi suspenso enquanto a solicitação de exclusão é analisada.',['link_id'=>$linkId]);
    }
    return afCreatePrivacyRequest($pdo,$guardianId,$minorId,$type,$description);
}

// -----------------------------------------------------------------------------
// Etapa 21 — relatório de riscos e gate de liberação de menores.
// -----------------------------------------------------------------------------
function afPrivacyLatestRiskAssessment(PDO $pdo): ?array
{
    if(!afSafetyTableExists($pdo,'af_data_protection_risk_assessments'))return null;
    try{$st=$pdo->query('SELECT r.*,a.nome reviewer_name FROM af_data_protection_risk_assessments r LEFT JOIN af_admin_users a ON a.id=r.reviewed_by_admin_id ORDER BY r.id DESC LIMIT 1');return $st?($st->fetch()?:null):null;}catch(Throwable $e){return null;}
}

function afPrivacyMinorReleaseReady(PDO $pdo): array
{
    $required=filter_var(afPrivacyConfig($pdo,'minor_release_requires_approved_risk_assessment','1'),FILTER_VALIDATE_BOOLEAN);
    if(!$required)return ['ready'=>true,'code'=>'RISK_GATE_DISABLED','assessment'=>null];

    $controller=function_exists('afPrivacyController')?afPrivacyController($pdo):[];
    foreach(['name','document','address','contact_email'] as $field){
        if(trim((string)($controller[$field]??''))===''){
            return ['ready'=>false,'code'=>'CONTROLLER_DATA_REQUIRED','assessment'=>afPrivacyLatestRiskAssessment($pdo)];
        }
    }

    $row=afPrivacyLatestRiskAssessment($pdo);
    if(!$row)return ['ready'=>false,'code'=>'RISK_ASSESSMENT_REQUIRED','assessment'=>null];
    if((string)$row['status']!=='approved'||empty($row['legal_reviewed_at'])||empty($row['approved_at'])){
        return ['ready'=>false,'code'=>'RISK_ASSESSMENT_REQUIRED','assessment'=>$row];
    }
    if(empty($row['legal_reviewer_name'])||empty($row['legal_reviewer_registration'])||empty($row['legal_review_reference'])){
        return ['ready'=>false,'code'=>'LEGAL_REVIEW_EVIDENCE_REQUIRED','assessment'=>$row];
    }
    if(empty($row['next_review_at'])||strtotime((string)$row['next_review_at'])<=time()){
        return ['ready'=>false,'code'=>'LEGAL_REVIEW_EXPIRED','assessment'=>$row];
    }
    return ['ready'=>true,'code'=>'RISK_ASSESSMENT_APPROVED','assessment'=>$row];
}

function afPrivacySaveRiskAssessment(PDO $pdo,int $adminId,array $input): array
{
    if(!afSafetyTableExists($pdo,'af_data_protection_risk_assessments'))throw new RuntimeException('Importe a migração de finalização primeiro.');
    $status=(string)($input['status']??'draft');if(!in_array($status,['draft','in_review','approved','rejected'],true))$status='draft';
    $title=trim((string)($input['title']??'Relatório de riscos para contas de menores'));
    $scope=trim((string)($input['scope']??''));$risks=trim((string)($input['risks']??''));$mitigations=trim((string)($input['mitigations']??''));$residual=trim((string)($input['residual_risks']??''));
    if(mb_strlen($scope)<20||mb_strlen($risks)<20||mb_strlen($mitigations)<20)throw new RuntimeException('Descreva escopo, riscos e medidas com pelo menos 20 caracteres em cada campo.');
    $legalReviewed=!empty($input['legal_reviewed']);
    $legalReviewerName=mb_substr(trim((string)($input['legal_reviewer_name']??'')),0,180);
    $legalReviewerRegistration=mb_substr(trim((string)($input['legal_reviewer_registration']??'')),0,80);
    $legalReviewReference=mb_substr(trim((string)($input['legal_review_reference']??'')),0,255);
    $nextReviewRaw=trim((string)($input['next_review_at']??''));
    $nextReviewAt=null;
    if($nextReviewRaw!==''){
        $ts=strtotime($nextReviewRaw);
        if($ts!==false)$nextReviewAt=date('Y-m-d H:i:s',$ts);
    }
    if($status==='approved'){
        if(!$legalReviewed)throw new RuntimeException('A aprovação exige confirmação de revisão jurídica.');
        if($legalReviewerName===''||$legalReviewerRegistration===''||$legalReviewReference==='')throw new RuntimeException('Informe profissional, registro e referência da revisão jurídica.');
        if(!$nextReviewAt||strtotime($nextReviewAt)<=time())throw new RuntimeException('Defina uma data futura para a próxima revisão jurídica.');
        $controller=function_exists('afPrivacyController')?afPrivacyController($pdo):[];
        foreach(['name','document','address','contact_email'] as $field){
            if(trim((string)($controller[$field]??''))==='')throw new RuntimeException('Preencha os dados do controlador antes de aprovar o relatório.');
        }
    }
    $pdo->prepare('INSERT INTO af_data_protection_risk_assessments (version_key,title,scope_text,risks_text,mitigations_text,residual_risks_text,status,legal_reviewed_at,legal_reviewer_name,legal_reviewer_registration,legal_review_reference,next_review_at,reviewed_by_admin_id,approved_at,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,NOW(),NOW())')
        ->execute([(string)($input['version_key']??date('Y.m.d')),$title,$scope,$risks,$mitigations,$residual?:null,$status,$legalReviewed?date('Y-m-d H:i:s'):null,$legalReviewerName?:null,$legalReviewerRegistration?:null,$legalReviewReference?:null,$nextReviewAt,$adminId,$status==='approved'?date('Y-m-d H:i:s'):null]);
    return afPrivacyLatestRiskAssessment($pdo)?:[];
}

function afPrivacyCreateDeletionRecord(PDO $pdo,int $requestId,int $requesterId,int $subjectId,string $scope): void
{
    if(!afSafetyTableExists($pdo,'af_deletion_requests'))return;
    $pdo->prepare("INSERT INTO af_deletion_requests (privacy_request_id,requester_user_id,subject_user_id,scope_key,status,requested_at,created_at,updated_at) VALUES (?,?,?,?,'requested',NOW(),NOW(),NOW()) ON DUPLICATE KEY UPDATE scope_key=VALUES(scope_key),updated_at=NOW()")
        ->execute([$requestId,$requesterId,$subjectId,$scope]);
}
