<?php
/**
 * AURA FARMING — Etapa 11
 * Compartilhamento, desafios por link, convites e métricas virais.
 */

function afViralToken(PDO $pdo, string $table, string $column, int $bytes = 10): string
{
    for ($i = 0; $i < 12; $i++) {
        $token = bin2hex(random_bytes($bytes));
        $st = $pdo->prepare("SELECT 1 FROM {$table} WHERE {$column}=? LIMIT 1");
        $st->execute([$token]);
        if (!$st->fetchColumn()) return $token;
    }
    throw new RuntimeException('Não foi possível gerar um código exclusivo.');
}

function afCodigoConviteViral(PDO $pdo, int $usuarioId): array
{
    try {
        $st = $pdo->prepare('SELECT * FROM af_convites_virais WHERE usuario_id=? LIMIT 1');
        $st->execute([$usuarioId]);
        if ($row = $st->fetch()) return $row;

        for ($i = 0; $i < 12; $i++) {
            $code = strtoupper(substr(strtr(base64_encode(random_bytes(9)), '+/', 'AZ'), 0, 10));
            $code = preg_replace('/[^A-Z0-9]/', '', $code);
            if (strlen($code) < 8) continue;
            try {
                $pdo->prepare("INSERT INTO af_convites_virais(usuario_id,codigo,origem,cliques,cadastros,primeiras_partidas,criado_em,atualizado_em)
                               VALUES (?,?,'perfil',0,0,0,NOW(),NOW())")
                    ->execute([$usuarioId,$code]);
                $st->execute([$usuarioId]);
                return $st->fetch() ?: [];
            } catch (PDOException $e) {
                if ((string)$e->getCode() !== '23000') throw $e;
            }
        }
    } catch (Throwable $e) {
        if (defined('APP_DEBUG') && APP_DEBUG) error_log('Convite viral indisponível: '.$e->getMessage());
    }
    return [];
}

function afCapturarConviteViral(PDO $pdo, string $codigo): void
{
    $codigo = strtoupper(preg_replace('/[^A-Z0-9]/', '', $codigo));
    if (strlen($codigo) < 8 || isLoggedIn()) return;
    if (strtoupper((string)($_SESSION['af_referral_code'] ?? '')) === $codigo) return;
    try {
        $st = $pdo->prepare('SELECT id,usuario_id FROM af_convites_virais WHERE codigo=? LIMIT 1');
        $st->execute([$codigo]);
        $convite = $st->fetch();
        if (!$convite) return;
        $_SESSION['af_referral_invite_id'] = (int)$convite['id'];
        $_SESSION['af_referral_code'] = $codigo;
        $pdo->prepare('UPDATE af_convites_virais SET cliques=cliques+1,ultimo_clique_em=NOW(),atualizado_em=NOW() WHERE id=?')
            ->execute([(int)$convite['id']]);
    } catch (Throwable $e) {
        if (defined('APP_DEBUG') && APP_DEBUG) error_log('Falha ao capturar convite: '.$e->getMessage());
    }
}

function afRegistrarCadastroIndicado(PDO $pdo, int $novoUsuarioId): void
{
    $conviteId = (int)($_SESSION['af_referral_invite_id'] ?? 0);
    if ($conviteId <= 0) return;
    try {
        $st = $pdo->prepare('SELECT id,usuario_id FROM af_convites_virais WHERE id=? LIMIT 1');
        $st->execute([$conviteId]);
        $convite = $st->fetch();
        if (!$convite || (int)$convite['usuario_id'] === $novoUsuarioId) return;
        $pdo->beginTransaction();
        $ins = $pdo->prepare("INSERT IGNORE INTO af_indicacoes_virais(convite_id,indicador_id,indicado_id,status,cadastrado_em)
                              VALUES (?,?,?,'cadastrado',NOW())");
        $ins->execute([$conviteId,(int)$convite['usuario_id'],$novoUsuarioId]);
        if ($ins->rowCount() > 0) {
            $pdo->prepare('UPDATE af_convites_virais SET cadastros=cadastros+1,atualizado_em=NOW() WHERE id=?')->execute([$conviteId]);
            if (function_exists('criarNotificacao')) {
                criarNotificacao($pdo,(int)$convite['usuario_id'],'convite_aceito','Seu convite trouxe um novo jogador','Um novo Aura Farmer criou a conta usando seu link.',['indicado_id'=>$novoUsuarioId]);
            }
        }
        $pdo->commit();
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        if (defined('APP_DEBUG') && APP_DEBUG) error_log('Falha ao registrar indicação: '.$e->getMessage());
    } finally {
        unset($_SESSION['af_referral_invite_id'], $_SESSION['af_referral_code']);
    }
}

function afMarcarPrimeiraPartidaIndicacao(PDO $pdo, int $usuarioId, int $partidaId): void
{
    try {
        $pdo->beginTransaction();
        $st = $pdo->prepare("SELECT * FROM af_indicacoes_virais WHERE indicado_id=? AND status='cadastrado' LIMIT 1 FOR UPDATE");
        $st->execute([$usuarioId]);
        $ind = $st->fetch();
        if (!$ind) { $pdo->commit(); return; }
        $pdo->prepare("UPDATE af_indicacoes_virais SET status='primeira_partida',primeira_partida_id=?,concluido_em=NOW() WHERE id=?")
            ->execute([$partidaId,(int)$ind['id']]);
        $pdo->prepare('UPDATE af_convites_virais SET primeiras_partidas=primeiras_partidas+1,atualizado_em=NOW() WHERE id=?')
            ->execute([(int)$ind['convite_id']]);
        if (function_exists('criarNotificacao')) {
            criarNotificacao($pdo,(int)$ind['indicador_id'],'convite_concluido','Convite convertido em jogador ativo','O jogador convidado concluiu a primeira farmada.',['indicado_id'=>$usuarioId,'partida_id'=>$partidaId]);
        }
        $pdo->commit();
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        if (defined('APP_DEBUG') && APP_DEBUG) error_log('Falha ao concluir indicação: '.$e->getMessage());
    }
}

function afCriarDesafioViral(PDO $pdo, int $usuarioId, int $partidaId, string $template = 'neon'): array
{
    $template = in_array($template,['neon','story','square','maximum'],true) ? $template : 'neon';
    $st = $pdo->prepare("SELECT p.*,u.nome,u.aura_id FROM af_partidas p JOIN af_usuarios u ON u.id=p.usuario_id
                         WHERE p.id=? AND p.usuario_id=? AND p.status='finalizada' AND p.antifraude_status='aprovada' AND p.controlled_event_id IS NULL LIMIT 1");
    $st->execute([$partidaId,$usuarioId]);
    $partida = $st->fetch();
    if (!$partida) throw new RuntimeException('Resultado não encontrado.');

    $st = $pdo->prepare("SELECT * FROM af_desafios_virais WHERE partida_origem_id=? AND criador_id=? AND status IN ('aberto','aceito') AND expira_em>NOW() ORDER BY id DESC LIMIT 1");
    $st->execute([$partidaId,$usuarioId]);
    if ($existente = $st->fetch()) return $existente;

    $token = afViralToken($pdo,'af_desafios_virais','token',10);
    $pdo->prepare("INSERT INTO af_desafios_virais
        (token,criador_id,partida_origem_id,alvo_pontuacao,template,status,visualizacoes,compartilhamentos,criado_em,expira_em)
        VALUES (?,?,?,?,?,'aberto',0,0,NOW(),DATE_ADD(NOW(),INTERVAL 30 DAY))")
        ->execute([$token,$usuarioId,$partidaId,(float)($partida['pontuacao_validada'] ?? 0),$template]);
    $id = (int)$pdo->lastInsertId();
    $st = $pdo->prepare('SELECT * FROM af_desafios_virais WHERE id=?');$st->execute([$id]);
    return $st->fetch();
}

function afBuscarDesafioViral(PDO $pdo, string $token): ?array
{
    $token = strtolower(preg_replace('/[^a-f0-9]/','',$token));
    if (strlen($token) !== 20) return null;
    $st = $pdo->prepare("SELECT d.*,u.nome,u.username,u.aura_id,u.foto_perfil,u.foto_externa,u.titulo,
                                p.aura_gerada,p.likes_recebidos,p.superlikes_recebidos,p.combo_maximo,p.combo_nota,p.ranking_global_posicao,p.share_token,
                                ur.nome respondente_nome,ur.aura_id respondente_aura_id,pr.pontuacao_validada resposta_pontuacao,pr.share_token resposta_share_token
                         FROM af_desafios_virais d
                         JOIN af_usuarios u ON u.id=d.criador_id
                         JOIN af_partidas p ON p.id=d.partida_origem_id
                         LEFT JOIN af_usuarios ur ON ur.id=d.convidado_id
                         LEFT JOIN af_partidas pr ON pr.id=d.partida_resposta_id
                         WHERE d.token=? LIMIT 1");
    $st->execute([$token]);
    $d = $st->fetch() ?: null;
    if ($d && $d['status']==='aberto' && strtotime((string)$d['expira_em']) < time()) {
        $pdo->prepare("UPDATE af_desafios_virais SET status='expirado' WHERE id=? AND status='aberto'")->execute([(int)$d['id']]);
        $d['status']='expirado';
    }
    return $d;
}

function afAceitarDesafioViral(PDO $pdo, string $token, int $usuarioId): array
{
    $pdo->beginTransaction();
    try {
        $st = $pdo->prepare('SELECT * FROM af_desafios_virais WHERE token=? FOR UPDATE');
        $st->execute([$token]);
        $d = $st->fetch();
        if (!$d) throw new RuntimeException('Desafio não encontrado.');
        if ((int)$d['criador_id'] === $usuarioId) throw new RuntimeException('Você não pode aceitar o próprio desafio.');
        if (strtotime((string)$d['expira_em']) < time()) throw new RuntimeException('Este desafio expirou.');
        if (!in_array($d['status'],['aberto','aceito'],true)) throw new RuntimeException('Este desafio já foi encerrado.');
        if ($d['status']==='aceito' && (int)$d['convidado_id'] !== $usuarioId) throw new RuntimeException('Outro jogador já aceitou este desafio.');
        $novoAceite = $d['status'] === 'aberto';
        $pdo->prepare("UPDATE af_desafios_virais SET status='aceito',convidado_id=?,aceito_em=COALESCE(aceito_em,NOW()) WHERE id=?")
            ->execute([$usuarioId,(int)$d['id']]);
        if ($novoAceite && function_exists('criarNotificacao')) {
            $stU=$pdo->prepare('SELECT nome,aura_id FROM af_usuarios WHERE id=?');$stU->execute([$usuarioId]);$j=$stU->fetch()?:['nome'=>'Um jogador','aura_id'=>''];
            criarNotificacao($pdo,(int)$d['criador_id'],'desafio_aceito','Seu desafio foi aceito',$j['nome'].' ('.$j['aura_id'].') vai tentar superar sua aura.',['desafio_id'=>(int)$d['id']]);
        }
        $pdo->commit();
        return afBuscarDesafioViral($pdo,$token) ?: $d;
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        throw $e;
    }
}

function afVincularDesafioViralPartida(PDO $pdo, string $token, int $usuarioId, int $partidaId): void
{
    $token = strtolower(preg_replace('/[^a-f0-9]/','',$token));
    if (strlen($token)!==20) return;
    $pdo->beginTransaction();
    try {
        $st=$pdo->prepare("SELECT * FROM af_desafios_virais WHERE token=? AND convidado_id=? AND status='aceito' FOR UPDATE");
        $st->execute([$token,$usuarioId]);$d=$st->fetch();
        if(!$d) throw new RuntimeException('Desafio viral inválido ou não aceito.');
        if(!empty($d['partida_resposta_id']) && (int)$d['partida_resposta_id']!==$partidaId) throw new RuntimeException('Este desafio já está ligado a outra partida.');
        $stPartida=$pdo->prepare("SELECT status,desafio_viral_id FROM af_partidas WHERE id=? AND usuario_id=? FOR UPDATE");
        $stPartida->execute([$partidaId,$usuarioId]);$partida=$stPartida->fetch();
        if(!$partida) throw new RuntimeException('Partida de resposta não encontrada.');
        if($partida['status']!=='aguardando' && (int)($partida['desafio_viral_id']??0)!==(int)$d['id']) throw new RuntimeException('Finalize ou cancele sua partida atual antes de iniciar o desafio.');
        if(!empty($partida['desafio_viral_id']) && (int)$partida['desafio_viral_id']!==(int)$d['id']) throw new RuntimeException('Esta partida já pertence a outro desafio.');
        $pdo->prepare('UPDATE af_desafios_virais SET partida_resposta_id=? WHERE id=?')->execute([$partidaId,(int)$d['id']]);
        $pdo->prepare('UPDATE af_partidas SET desafio_viral_id=? WHERE id=? AND usuario_id=?')->execute([(int)$d['id'],$partidaId,$usuarioId]);
        $pdo->commit();
    }catch(Throwable $e){if($pdo->inTransaction())$pdo->rollBack();throw $e;}
}

function afConcluirDesafioViralPorPartida(PDO $pdo, array $partida): void
{
    $desafioId=(int)($partida['desafio_viral_id']??0);
    if($desafioId<=0)return;
    try{
        $pdo->beginTransaction();
        $st=$pdo->prepare("SELECT * FROM af_desafios_virais WHERE id=? FOR UPDATE");$st->execute([$desafioId]);$d=$st->fetch();
        if(!$d||$d['status']==='concluido'){$pdo->commit();return;}
        $score=(float)($partida['pontuacao_validada'] ?? 0);$alvo=(float)$d['alvo_pontuacao'];
        $resultado=abs($score-$alvo)<0.005?'empate':($score>$alvo?'superou':'nao_superou');
        $pdo->prepare("UPDATE af_desafios_virais SET status='concluido',resultado=?,concluido_em=NOW() WHERE id=?")
            ->execute([$resultado,$desafioId]);
        if(function_exists('criarNotificacao')){
            $msg=$resultado==='superou'?'Seu resultado foi superado!':($resultado==='empate'?'O desafio terminou empatado.':'Sua pontuação permaneceu invicta.');
            criarNotificacao($pdo,(int)$d['criador_id'],'desafio_finalizado','Desafio viral finalizado',$msg,['desafio_id'=>$desafioId,'resultado'=>$resultado]);
            $msg2=$resultado==='superou'?'Você superou o desafio!':($resultado==='empate'?'Você igualou a pontuação!':'Quase! Tente novamente em uma nova farmada.');
            criarNotificacao($pdo,(int)$d['convidado_id'],'desafio_finalizado','Resultado do desafio',$msg2,['desafio_id'=>$desafioId,'resultado'=>$resultado]);
        }
        $pdo->commit();
    }catch(Throwable $e){if($pdo->inTransaction())$pdo->rollBack();if(defined('APP_DEBUG')&&APP_DEBUG)error_log('Falha no desafio viral: '.$e->getMessage());}
}

function afRegistrarVisitaViral(PDO $pdo, ?int $partidaId, ?int $desafioId, string $origem = ''): void
{
    try{
        $fingerprint=hash('sha256',clientIp().'|'.substr((string)($_SERVER['HTTP_USER_AGENT']??''),0,250).'|'.date('Y-m-d'));
        $referer=substr((string)($_SERVER['HTTP_REFERER']??''),0,500);
        $source=substr(preg_replace('/[^a-zA-Z0-9_.-]/','',(string)($_GET['utm_source']??$origem)),0,60);
        $chaveAlvo=$desafioId ? 'd:'.$desafioId : 'p:'.(int)$partidaId;
        $st=$pdo->prepare("INSERT IGNORE INTO af_visitas_virais(partida_id,desafio_id,visitante_hash,chave_alvo,origem,referer,dia,criado_em) VALUES (?,?,?,?,?,?,CURDATE(),NOW())");
        $st->execute([$partidaId,$desafioId,$fingerprint,$chaveAlvo,$source?:null,$referer?:null]);
        if($st->rowCount()>0 && $desafioId)$pdo->prepare('UPDATE af_desafios_virais SET visualizacoes=visualizacoes+1 WHERE id=?')->execute([$desafioId]);
    }catch(Throwable $e){if(defined('APP_DEBUG')&&APP_DEBUG)error_log('Métrica viral indisponível: '.$e->getMessage());}
}

function afResumoViral(PDO $pdo, int $usuarioId): array
{
    $convite=afCodigoConviteViral($pdo,$usuarioId);
    $r=['compartilhamentos'=>0,'visualizacoes'=>0,'desafios_criados'=>0,'desafios_superados'=>0,'convite'=>$convite,'desafios'=>[],'resultados'=>[]];
    try{
        $st=$pdo->prepare('SELECT COUNT(*) FROM af_compartilhamentos WHERE usuario_id=?');$st->execute([$usuarioId]);$r['compartilhamentos']=(int)$st->fetchColumn();
        $st=$pdo->prepare('SELECT COUNT(*) FROM af_visitas_virais v JOIN af_partidas p ON p.id=v.partida_id WHERE p.usuario_id=?');$st->execute([$usuarioId]);$r['visualizacoes']=(int)$st->fetchColumn();
        $st=$pdo->prepare('SELECT COUNT(*) FROM af_desafios_virais WHERE criador_id=?');$st->execute([$usuarioId]);$r['desafios_criados']=(int)$st->fetchColumn();
        $st=$pdo->prepare("SELECT COUNT(*) FROM af_desafios_virais WHERE convidado_id=? AND resultado='superou'");$st->execute([$usuarioId]);$r['desafios_superados']=(int)$st->fetchColumn();
        $st=$pdo->prepare("SELECT d.*,u.nome convidado_nome,u.aura_id convidado_aura_id FROM af_desafios_virais d LEFT JOIN af_usuarios u ON u.id=d.convidado_id WHERE d.criador_id=? OR d.convidado_id=? ORDER BY d.id DESC LIMIT 12");$st->execute([$usuarioId,$usuarioId]);$r['desafios']=$st->fetchAll();
        $st=$pdo->prepare("SELECT p.id,p.sessao_id,p.pontuacao_validada pontuacao_total,p.aura_gerada,p.ranking_global_posicao,p.share_token,p.finalizada_em,COUNT(DISTINCT v.id) visualizacoes,COUNT(DISTINCT c.id) compartilhamentos
                           FROM af_partidas p LEFT JOIN af_visitas_virais v ON v.partida_id=p.id LEFT JOIN af_compartilhamentos c ON c.partida_id=p.id
                           WHERE p.usuario_id=? AND p.status='finalizada' AND COALESCE(p.admin_status,'normal')='normal' AND p.controlled_event_id IS NULL GROUP BY p.id ORDER BY p.finalizada_em DESC LIMIT 8");$st->execute([$usuarioId]);$r['resultados']=$st->fetchAll();
    }catch(Throwable $e){if(defined('APP_DEBUG')&&APP_DEBUG)error_log('Resumo viral: '.$e->getMessage());}
    return $r;
}
