<?php
/**
 * AURA FARMING — Etapa 8
 * Progressão, sequência, missões, conquistas e recompensas cosméticas.
 */

function afDetalhesNivel(int $xpTotal): array
{
    $nivel = calcularNivel(max(0,$xpTotal));
    $xpAtual = xpNoNivelAtual(max(0,$xpTotal),$nivel);
    $xpNecessario = xpParaProximoNivel($nivel);
    return [
        'nivel'=>$nivel,
        'titulo'=>getTitulo($nivel),
        'xp_total'=>max(0,$xpTotal),
        'xp_atual'=>$xpAtual,
        'xp_necessario'=>$xpNecessario,
        'percentual'=>$xpNecessario>0 ? min(100,round(($xpAtual/$xpNecessario)*100,1)) : 100,
    ];
}

function afChavePeriodoMissao(string $tipo, ?DateTimeImmutable $agora=null): string
{
    $agora ??= new DateTimeImmutable('now');
    return $tipo==='semanal' ? $agora->format('o-\WW') : $agora->format('Y-m-d');
}

function afGarantirProgressao(PDO $pdo,int $usuarioId): void
{
    $pdo->prepare('INSERT IGNORE INTO af_progressao_usuario(usuario_id) VALUES (?)')->execute([$usuarioId]);
    $pdo->prepare('INSERT IGNORE INTO af_usuario_equipamentos(usuario_id) VALUES (?)')->execute([$usuarioId]);
}

function afAtualizarSequencia(PDO $pdo,int $usuarioId): array
{
    afGarantirProgressao($pdo,$usuarioId);
    $st=$pdo->prepare('SELECT * FROM af_progressao_usuario WHERE usuario_id=? FOR UPDATE');
    $st->execute([$usuarioId]);
    $p=$st->fetch() ?: ['sequencia_atual'=>0,'melhor_sequencia'=>0,'ultima_atividade'=>null];
    $hoje=(new DateTimeImmutable('today'))->format('Y-m-d');
    $ontem=(new DateTimeImmutable('yesterday'))->format('Y-m-d');
    $ultima=(string)($p['ultima_atividade']??'');
    $novoDia=$ultima!==$hoje;
    $sequencia=(int)$p['sequencia_atual'];
    if($novoDia){
        $sequencia=$ultima===$ontem ? $sequencia+1 : 1;
        $melhor=max((int)$p['melhor_sequencia'],$sequencia);
        $pdo->prepare('UPDATE af_progressao_usuario SET sequencia_atual=?,melhor_sequencia=?,ultima_atividade=?,atualizado_em=NOW() WHERE usuario_id=?')
            ->execute([$sequencia,$melhor,$hoje,$usuarioId]);
    } else {
        $melhor=(int)$p['melhor_sequencia'];
    }
    // Parte 07: sequência diária agora dá bônus pequeno para não facilitar demais a subida de nível.
    $bonus=$novoDia ? min(10,max(0,($sequencia-1))) : 0;
    return ['sequencia'=>$sequencia,'melhor_sequencia'=>$melhor,'novo_dia'=>$novoDia,'bonus_xp'=>$bonus];
}

function afCalcularXpPartida(array $partida,float $pontuacao,bool $novoRecorde,float $auraFinal,int $bonusSequencia): array
{
    /**
     * Diamantes Parte 07
     *
     * Nova regra de economia:
     * - farm/partida concluída dá XP, mas nunca dá Diamantes;
     * - XP base de partida ficou baixo para dificultar subida de nível;
     * - bônus existem, mas são pequenos e limitados.
     */
    $base=15;
    $bonusRecorde=$novoRecorde?5:0;
    $bonusMaxima=$auraFinal>=1000?5:0;
    $combo=max(1,(int)($partida['combo_maximo']??1));
    $hits=max(0,(int)($partida['combo_hits_maximo']??0));
    $sequenciasCombo=max(0,(int)($partida['combo_sequencias_concluidas']??0));
    $perfectChain=max(0,(int)($partida['combo_perfect_chain_max']??0));
    $bonusCombo=$combo>=5?5:($combo>=3?3:($combo>=2?1:0));
    $bonusComboAvancado=min(5,(int)floor(($hits/10)+($sequenciasCombo*1.5)+($perfectChain/3)));
    $bonusSequencia=min(10,max(0,$bonusSequencia));
    $bonus=max(0,$bonusRecorde+$bonusMaxima+$bonusCombo+$bonusComboAvancado+$bonusSequencia);
    $total=min(45,$base+$bonus);
    return [
        'base'=>$base,
        'bonus'=>$total-$base,
        'total'=>$total,
        'detalhes'=>[
            'partida'=>$base,
            'novo_recorde'=>$bonusRecorde,
            'aura_maxima'=>$bonusMaxima,
            'combo'=>$bonusCombo,
            'combo_avancado'=>$bonusComboAvancado,
            'sequencias_combo'=>$sequenciasCombo,
            'sequencia'=>$bonusSequencia,
            'diamantes_ganhos'=>0,
            'regra'=>'diamantes_parte07_xp_dificil_sem_diamantes_no_farm',
        ],
    ];
}


function afRegistrarXp(PDO $pdo,int $usuarioId,int $xp,string $tipo,string $descricao,?int $partidaId=null,?int $usuarioMissaoId=null,array $dados=[]): void
{
    if($xp<=0)return;
    $pdo->prepare('INSERT INTO af_xp_eventos(usuario_id,partida_id,usuario_missao_id,tipo,xp,descricao,dados,criado_em) VALUES (?,?,?,?,?,?,?,NOW())')
        ->execute([$usuarioId,$partidaId,$usuarioMissaoId,$tipo,$xp,$descricao,$dados?json_encode($dados,JSON_UNESCAPED_UNICODE):null]);
}

function afGarantirMissoesAtuais(PDO $pdo,int $usuarioId): void
{
    foreach(['diaria','semanal'] as $tipo){
        $chave=afChavePeriodoMissao($tipo);
        $st=$pdo->prepare("INSERT IGNORE INTO af_usuario_missoes(usuario_id,missao_id,periodo_chave)
                           SELECT ?,id,? FROM af_missoes WHERE tipo=? AND ativo=1");
        $st->execute([$usuarioId,$chave,$tipo]);
    }
}

function afAtualizarMissoesPartida(PDO $pdo,int $usuarioId,array $partida,float $pontuacao,float $auraFinal): array
{
    afGarantirMissoesAtuais($pdo,$usuarioId);
    $concluidas=[];
    foreach(['diaria','semanal'] as $tipo){
        $chave=afChavePeriodoMissao($tipo);
        $st=$pdo->prepare("SELECT um.id,um.progresso,um.concluida_em,m.nome,m.criterio,m.meta,m.xp_recompensa
                           FROM af_usuario_missoes um JOIN af_missoes m ON m.id=um.missao_id
                           WHERE um.usuario_id=? AND um.periodo_chave=? AND m.tipo=? FOR UPDATE");
        $st->execute([$usuarioId,$chave,$tipo]);
        foreach($st->fetchAll() as $m){
            $atual=(int)$m['progresso'];
            $novo=match($m['criterio']){
                'partidas'=>$atual+1,
                'pontuacao_unica'=>max($atual,(int)floor($pontuacao)),
                'likes_partida'=>max($atual,(int)($partida['likes_recebidos']??0)),
                'aura_maxima'=>$atual+($auraFinal>=1000?1:0),
                'combo'=>max($atual,(int)($partida['combo_maximo']??1)),
                default=>$atual,
            };
            $completou=empty($m['concluida_em']) && $novo>=(int)$m['meta'];
            $pdo->prepare('UPDATE af_usuario_missoes SET progresso=?,concluida_em=CASE WHEN ?=1 THEN COALESCE(concluida_em,NOW()) ELSE concluida_em END,atualizada_em=NOW() WHERE id=?')
                ->execute([$novo,$completou?1:0,(int)$m['id']]);
            if($completou){
                $concluidas[]=['id'=>(int)$m['id'],'nome'=>$m['nome'],'xp'=>(int)$m['xp_recompensa']];
                criarNotificacao($pdo,$usuarioId,'mission','Missão concluída: '.$m['nome'],'Seu prêmio de '.(int)$m['xp_recompensa'].' XP está pronto para resgate.',['usuario_missao_id'=>(int)$m['id']]);
                if(!empty($partida['id'])){
                    $pdo->prepare("INSERT INTO af_partida_eventos(partida_id,usuario_id,tipo,valor,dados,criado_em) VALUES (?,?,'missao_concluida',0,?,NOW())")
                        ->execute([(int)$partida['id'],$usuarioId,json_encode(['missao'=>$m['nome']],JSON_UNESCAPED_UNICODE)]);
                }
            }
        }
    }
    return $concluidas;
}

function afSincronizarRecompensas(PDO $pdo,int $usuarioId,int $nivel,?int $partidaId=null): array
{
    afGarantirProgressao($pdo,$usuarioId);
    $st=$pdo->prepare("SELECT r.* FROM af_recompensas r
                       LEFT JOIN af_usuario_recompensas ur ON ur.recompensa_id=r.id AND ur.usuario_id=?
                       WHERE r.ativo=1 AND r.nivel_min<=? AND ur.id IS NULL ORDER BY r.nivel_min,r.ordem");
    $st->execute([$usuarioId,$nivel]);
    $novas=$st->fetchAll() ?: [];
    foreach($novas as $r){
        $pdo->prepare("INSERT IGNORE INTO af_usuario_recompensas(usuario_id,recompensa_id,origem,desbloqueada_em) VALUES (?,?,'nivel',NOW())")
            ->execute([$usuarioId,(int)$r['id']]);
        criarNotificacao($pdo,$usuarioId,'reward','Recompensa desbloqueada!',(string)$r['nome'].' já pode ser equipada.',['recompensa_id'=>(int)$r['id']]);
        if($partidaId){
            $pdo->prepare("INSERT INTO af_partida_eventos(partida_id,usuario_id,tipo,valor,dados,criado_em) VALUES (?,?,'recompensa_desbloqueada',0,?,NOW())")
                ->execute([$partidaId,$usuarioId,json_encode(['codigo'=>$r['codigo'],'nome'=>$r['nome']],JSON_UNESCAPED_UNICODE)]);
        }
    }
    $pdo->prepare('UPDATE af_progressao_usuario SET recompensas_desbloqueadas=(SELECT COUNT(*) FROM af_usuario_recompensas WHERE usuario_id=?),atualizado_em=NOW() WHERE usuario_id=?')
        ->execute([$usuarioId,$usuarioId]);
    $eq=$pdo->prepare('SELECT * FROM af_usuario_equipamentos WHERE usuario_id=?');$eq->execute([$usuarioId]);$equip=$eq->fetch()?:[];
    foreach(['titulo'=>'titulo_id','cor_aura'=>'cor_aura_id'] as $tipo=>$campo){
        if(empty($equip[$campo])){
            $st=$pdo->prepare("SELECT r.id FROM af_usuario_recompensas ur JOIN af_recompensas r ON r.id=ur.recompensa_id WHERE ur.usuario_id=? AND r.tipo=? ORDER BY r.nivel_min DESC,r.ordem DESC LIMIT 1");
            $st->execute([$usuarioId,$tipo]);$rid=(int)($st->fetchColumn()?:0);
            if($rid)$pdo->prepare("UPDATE af_usuario_equipamentos SET {$campo}=? WHERE usuario_id=?")->execute([$rid,$usuarioId]);
        }
    }
    $st=$pdo->prepare('SELECT r.valor_visual FROM af_usuario_equipamentos e JOIN af_recompensas r ON r.id=e.titulo_id WHERE e.usuario_id=? LIMIT 1');
    $st->execute([$usuarioId]);$tituloEquipado=(string)($st->fetchColumn()?:'');
    if($tituloEquipado!=='')$pdo->prepare('UPDATE af_usuarios SET titulo=?,atualizado_em=NOW() WHERE id=?')->execute([$tituloEquipado,$usuarioId]);
    return $novas;
}

function afEquipamentoUsuario(PDO $pdo,int $usuarioId): array
{
    afGarantirProgressao($pdo,$usuarioId);
    $sql="SELECT e.usuario_id,
      t.id titulo_id,t.nome titulo_nome,t.valor_visual titulo_valor,
      m.id moldura_id,m.nome moldura_nome,m.valor_visual moldura_valor,
      c.id cor_aura_id,c.nome cor_aura_nome,c.valor_visual cor_aura_valor,
      f.id efeito_aura_id,f.nome efeito_aura_nome,f.valor_visual efeito_aura_valor
      FROM af_usuario_equipamentos e
      LEFT JOIN af_recompensas t ON t.id=e.titulo_id
      LEFT JOIN af_recompensas m ON m.id=e.moldura_id
      LEFT JOIN af_recompensas c ON c.id=e.cor_aura_id
      LEFT JOIN af_recompensas f ON f.id=e.efeito_aura_id
      WHERE e.usuario_id=? LIMIT 1";
    $st=$pdo->prepare($sql);$st->execute([$usuarioId]);
    return $st->fetch() ?: [];
}

function afEquiparRecompensa(PDO $pdo,int $usuarioId,int $recompensaId): array
{
    $st=$pdo->prepare("SELECT r.* FROM af_usuario_recompensas ur JOIN af_recompensas r ON r.id=ur.recompensa_id WHERE ur.usuario_id=? AND r.id=? AND r.ativo=1 LIMIT 1");
    $st->execute([$usuarioId,$recompensaId]);$r=$st->fetch();
    if(!$r)throw new RuntimeException('Recompensa não desbloqueada.');
    $campo=match($r['tipo']){'titulo'=>'titulo_id','moldura'=>'moldura_id','cor_aura'=>'cor_aura_id','efeito_aura'=>'efeito_aura_id',default=>null};
    if(!$campo)throw new RuntimeException('Tipo de recompensa inválido.');
    afGarantirProgressao($pdo,$usuarioId);
    $pdo->prepare("UPDATE af_usuario_equipamentos SET {$campo}=?,atualizado_em=NOW() WHERE usuario_id=?")->execute([(int)$r['id'],$usuarioId]);
    if($r['tipo']==='titulo')$pdo->prepare('UPDATE af_usuarios SET titulo=?,atualizado_em=NOW() WHERE id=?')->execute([(string)$r['valor_visual'],$usuarioId]);
    return $r;
}

function afResgatarMissao(PDO $pdo,int $usuarioId,int $usuarioMissaoId): array
{
    $pdo->beginTransaction();
    try{
        $st=$pdo->prepare("SELECT um.*,m.nome,m.meta,m.xp_recompensa FROM af_usuario_missoes um JOIN af_missoes m ON m.id=um.missao_id WHERE um.id=? AND um.usuario_id=? FOR UPDATE");
        $st->execute([$usuarioMissaoId,$usuarioId]);$m=$st->fetch();
        if(!$m)throw new RuntimeException('Missão não encontrada.');
        if(empty($m['concluida_em']) || (int)$m['progresso']<(int)$m['meta'])throw new RuntimeException('A missão ainda não foi concluída.');
        if(!empty($m['resgatada_em']))throw new RuntimeException('Esta recompensa já foi resgatada.');
        $st=$pdo->prepare('SELECT xp_total,nivel FROM af_usuarios WHERE id=? FOR UPDATE');$st->execute([$usuarioId]);$u=$st->fetch();
        $nivelAntes=calcularNivel((int)$u['xp_total']);
        $xp=(int)$m['xp_recompensa'];$novoXp=(int)$u['xp_total']+$xp;$nivelNovo=calcularNivel($novoXp);
        $pdo->prepare('UPDATE af_usuario_missoes SET resgatada_em=NOW(),atualizada_em=NOW() WHERE id=?')->execute([$usuarioMissaoId]);
        $pdo->prepare('UPDATE af_usuarios SET xp_total=?,nivel=?,titulo=?,atualizado_em=NOW() WHERE id=?')->execute([$novoXp,$nivelNovo,getTitulo($nivelNovo),$usuarioId]);
        $pdo->prepare('UPDATE af_progressao_usuario SET missoes_concluidas=missoes_concluidas+1,atualizado_em=NOW() WHERE usuario_id=?')->execute([$usuarioId]);
        afRegistrarXp($pdo,$usuarioId,$xp,'missao','Missão resgatada: '.$m['nome'],null,$usuarioMissaoId);
        $recompensas=afSincronizarRecompensas($pdo,$usuarioId,$nivelNovo);
        if($nivelNovo>$nivelAntes)criarNotificacao($pdo,$usuarioId,'level_up','Você alcançou o nível '.$nivelNovo.'!','Novas recompensas podem ter sido desbloqueadas.',['nivel'=>$nivelNovo]);
        $pdo->commit();
        recarregarSessaoUsuario($pdo,$usuarioId);
        return ['xp'=>$xp,'nivel_antes'=>$nivelAntes,'nivel_novo'=>$nivelNovo,'recompensas'=>$recompensas];
    }catch(Throwable $e){if($pdo->inTransaction())$pdo->rollBack();throw $e;}
}

function afListaMissoesAtuais(PDO $pdo,int $usuarioId): array
{
    afGarantirMissoesAtuais($pdo,$usuarioId);
    $out=[];
    foreach(['diaria','semanal'] as $tipo){
        $chave=afChavePeriodoMissao($tipo);
        $st=$pdo->prepare("SELECT um.id usuario_missao_id,um.progresso,um.concluida_em,um.resgatada_em,m.*
                           FROM af_usuario_missoes um JOIN af_missoes m ON m.id=um.missao_id
                           WHERE um.usuario_id=? AND um.periodo_chave=? AND m.tipo=? ORDER BY m.ordem,m.id");
        $st->execute([$usuarioId,$chave,$tipo]);
        $out[$tipo]=$st->fetchAll() ?: [];
    }
    return $out;
}

function afResumoProgressao(PDO $pdo,int $usuarioId): array
{
    afGarantirProgressao($pdo,$usuarioId);
    afGarantirMissoesAtuais($pdo,$usuarioId);
    $st=$pdo->prepare('SELECT u.xp_total,u.nivel,u.titulo,p.* FROM af_usuarios u LEFT JOIN af_progressao_usuario p ON p.usuario_id=u.id WHERE u.id=? LIMIT 1');
    $st->execute([$usuarioId]);$u=$st->fetch()?:[];
    $nivel=afDetalhesNivel((int)($u['xp_total']??0));
    $missoes=afListaMissoesAtuais($pdo,$usuarioId);
    $st=$pdo->prepare('SELECT COUNT(*) total,SUM(resgatada_em IS NULL AND concluida_em IS NOT NULL) prontas FROM af_usuario_missoes WHERE usuario_id=? AND periodo_chave IN (?,?)');
    $st->execute([$usuarioId,afChavePeriodoMissao('diaria'),afChavePeriodoMissao('semanal')]);$mc=$st->fetch()?:[];
    $st=$pdo->prepare('SELECT COUNT(*) FROM af_usuario_recompensas WHERE usuario_id=?');$st->execute([$usuarioId]);
    return [
        'nivel'=>$nivel,
        'sequencia_atual'=>(int)($u['sequencia_atual']??0),
        'melhor_sequencia'=>(int)($u['melhor_sequencia']??0),
        'missoes_prontas'=>(int)($mc['prontas']??0),
        'recompensas_desbloqueadas'=>(int)$st->fetchColumn(),
        'missoes'=>$missoes,
        'equipamentos'=>afEquipamentoUsuario($pdo,$usuarioId),
    ];
}

function afListarRecompensas(PDO $pdo,int $usuarioId): array
{
    $st=$pdo->prepare("SELECT r.*,ur.desbloqueada_em,
      CASE WHEN e.titulo_id=r.id OR e.moldura_id=r.id OR e.cor_aura_id=r.id OR e.efeito_aura_id=r.id THEN 1 ELSE 0 END equipada
      FROM af_recompensas r
      LEFT JOIN af_usuario_recompensas ur ON ur.recompensa_id=r.id AND ur.usuario_id=?
      LEFT JOIN af_usuario_equipamentos e ON e.usuario_id=?
      WHERE r.ativo=1 ORDER BY FIELD(r.tipo,'titulo','moldura','cor_aura','efeito_aura'),r.nivel_min,r.ordem");
    $st->execute([$usuarioId,$usuarioId]);
    return $st->fetchAll() ?: [];
}

function afHistoricoXp(PDO $pdo,int $usuarioId,int $limite=20): array
{
    $limite=max(1,min(100,$limite));
    $st=$pdo->prepare("SELECT tipo,xp,descricao,criado_em FROM af_xp_eventos WHERE usuario_id=? ORDER BY id DESC LIMIT {$limite}");
    $st->execute([$usuarioId]);return $st->fetchAll()?:[];
}
