{"openapi":"3.0.3","info":{"title":"eTecTrilha API","version":"1.0.0","description":"API REST do eTecTrilha — app gamificado de estudos para o Vestibulinho ETEC. Autenticação via sessão NextAuth (cookie). Rotas /admin exigem papel ADMIN."},"servers":[{"url":"/"}],"tags":[{"name":"Auth","description":"Cadastro e login"},{"name":"Perfil","description":"Dados do usuário logado"},{"name":"Jornada","description":"Disciplinas, módulos e fases"},{"name":"Estudo","description":"Respostas, revisão espaçada e desafio diário"},{"name":"Simulados"},{"name":"Estatísticas"},{"name":"Admin","description":"Gestão do banco de questões"}],"paths":{"/api/auth/register":{"post":{"tags":["Auth"],"summary":"Cadastra usuário com e-mail e senha","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","email","password"],"properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":6}}}}}},"responses":{"201":{"description":"Usuário criado"},"409":{"description":"E-mail já cadastrado"}}}},"/api/auth/signin":{"get":{"tags":["Auth"],"summary":"Fluxo de login NextAuth (Google e credenciais)","responses":{"200":{"description":"Página/fluxo de login"}}}},"/api/me":{"get":{"tags":["Perfil"],"summary":"Resumo do usuário: XP, nível, vidas, streak, precisão, revisões pendentes","responses":{"200":{"description":"Resumo","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"Não autenticado"}}},"patch":{"tags":["Perfil"],"summary":"Atualiza nome e preferência de som","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"soundOn":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Atualizado"}}}},"/api/journey":{"get":{"tags":["Jornada"],"summary":"Jornada completa: disciplinas → módulos → fases, com progresso e bloqueios","responses":{"200":{"description":"Jornada"}}}},"/api/phases/{id}":{"get":{"tags":["Jornada"],"summary":"Questões de uma fase (sem gabarito)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Fase com questões"},"404":{"description":"Fase não encontrada"}}}},"/api/phases/{id}/complete":{"post":{"tags":["Jornada"],"summary":"Registra conclusão de fase (+100 XP na primeira conclusão com ≥70%)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["correctCount","total"],"properties":{"correctCount":{"type":"integer"},"total":{"type":"integer"}}}}}},"responses":{"200":{"description":"Resultado da fase"}}}},"/api/answers":{"post":{"tags":["Estudo"],"summary":"Responde uma questão — retorna gabarito, explicação, XP, vidas e conquistas","description":"Regras: +10 XP por acerto (dobro no desafio diário); +50 XP a cada 5 acertos seguidos (campo combo); errar em fase/desafio custa 1 vida; erros entram na revisão espaçada (1, 3, 7, 15, 30 dias).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["questionId","selected"],"properties":{"questionId":{"type":"string"},"selected":{"type":"string","enum":["A","B","C","D","E"]},"timeSpent":{"type":"number"},"context":{"type":"string","enum":["PHASE","REVIEW","EXAM","CHALLENGE"],"default":"PHASE"},"combo":{"type":"integer","description":"Acertos consecutivos na sessão, contando este"}}}}}},"responses":{"200":{"description":"Resultado","content":{"application/json":{"schema":{"type":"object","properties":{"isCorrect":{"type":"boolean"},"correct":{"type":"string","enum":["A","B","C","D","E"]},"explanation":{"type":"string"},"xpGained":{"type":"integer"},"comboBonus":{"type":"integer"},"lives":{"type":"integer"},"nextLifeInSeconds":{"type":"integer"},"streak":{"type":"integer"},"level":{"type":"integer"},"leveledUp":{"type":"boolean"},"xp":{"type":"integer"},"unlocked":{"type":"array","items":{"$ref":"#/components/schemas/Achievement"}}}}}}}}}},"/api/review":{"get":{"tags":["Estudo"],"summary":"Questões erradas vencidas para revisar (repetição espaçada)","responses":{"200":{"description":"Fila de revisão"}}}},"/api/challenge":{"get":{"tags":["Estudo"],"summary":"Desafio diário (10 questões, XP em dobro) e status do dia","responses":{"200":{"description":"Desafio de hoje"}}}},"/api/challenge/complete":{"post":{"tags":["Estudo"],"summary":"Registra a conclusão do desafio diário","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["correctCount","total"],"properties":{"correctCount":{"type":"integer"},"total":{"type":"integer"},"timeSpent":{"type":"number"},"xpEarned":{"type":"integer"}}}}}},"responses":{"200":{"description":"Resultado registrado"}}}},"/api/exams":{"get":{"tags":["Simulados"],"summary":"Lista os simulados do usuário","responses":{"200":{"description":"Lista"}}},"post":{"tags":["Simulados"],"summary":"Cria um simulado com filtros (ano, disciplina, aleatório)","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"year":{"type":"integer"},"subjectSlug":{"type":"string"},"count":{"type":"integer","default":20,"minimum":5,"maximum":50}}}}}},"responses":{"201":{"description":"Simulado criado (id)"}}}},"/api/exams/{id}":{"get":{"tags":["Simulados"],"summary":"Detalhe do simulado (gabarito incluído apenas após finalizar)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Simulado"}}}},"/api/exams/{id}/submit":{"post":{"tags":["Simulados"],"summary":"Finaliza o simulado: nota, acertos, erros, tempo, percentil e análise por disciplina","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["answers"],"properties":{"answers":{"type":"object","additionalProperties":{"type":"string","enum":["A","B","C","D","E"]}},"timeSpent":{"type":"number"}}}}}},"responses":{"200":{"description":"Resultado completo"}}}},"/api/stats":{"get":{"tags":["Estatísticas"],"summary":"Dashboard: acertos por disciplina, evolução semanal, mapa de calor, tempo estudado","responses":{"200":{"description":"Estatísticas"}}}},"/api/ranking":{"get":{"tags":["Estatísticas"],"summary":"Ranking semanal, mensal ou geral","parameters":[{"name":"period","in":"query","schema":{"type":"string","enum":["week","month","all"],"default":"week"}}],"responses":{"200":{"description":"Top 20 + posição do usuário"}}}},"/api/achievements":{"get":{"tags":["Estatísticas"],"summary":"Conquistas (todas + desbloqueadas)","responses":{"200":{"description":"Conquistas"}}}},"/api/admin/questions":{"get":{"tags":["Admin"],"summary":"Lista questões com filtros e paginação","parameters":[{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"perPage","in":"query","schema":{"type":"integer","default":20}},{"name":"subject","in":"query","schema":{"type":"string"},"description":"slug da disciplina"},{"name":"year","in":"query","schema":{"type":"integer"}},{"name":"q","in":"query","schema":{"type":"string"},"description":"busca no enunciado"}],"responses":{"200":{"description":"Página de questões"},"403":{"description":"Somente admin"}}},"post":{"tags":["Admin"],"summary":"Cadastra uma questão","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuestionInput"}}}},"responses":{"201":{"description":"Criada"}}}},"/api/admin/questions/{id}":{"put":{"tags":["Admin"],"summary":"Edita uma questão","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuestionInput"}}}},"responses":{"200":{"description":"Atualizada"}}},"delete":{"tags":["Admin"],"summary":"Exclui uma questão","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Excluída"}}}},"/api/admin/import":{"post":{"tags":["Admin"],"summary":"Importa questões em massa (JSON ou CSV, até 10.000 por chamada)","description":"Cabeçalhos aceitos (flexíveis): ano, semestre, disciplina, tema, enunciado, a, b, c, d, e, correta/gabarito, explicacao, dificuldade, tempo_medio.","requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","required":["format","data"],"properties":{"format":{"type":"string","enum":["json"]},"data":{"type":"array","items":{"type":"object"}}}},{"type":"object","required":["format","text"],"properties":{"format":{"type":"string","enum":["csv"]},"text":{"type":"string"}}}]}}}},"responses":{"200":{"description":"{ imported, errorCount, errors[] }"}}}}},"components":{"schemas":{"Question":{"type":"object","properties":{"id":{"type":"string"},"year":{"type":"integer"},"semester":{"type":"integer","enum":[1,2]},"theme":{"type":"string"},"statement":{"type":"string"},"options":{"type":"array","items":{"type":"string"},"minItems":5,"maxItems":5},"difficulty":{"type":"integer","minimum":1,"maximum":3},"avgTimeSeconds":{"type":"integer"}}},"QuestionInput":{"type":"object","required":["year","subjectSlug","theme","statement","options","correct","explanation"],"properties":{"year":{"type":"integer"},"semester":{"type":"integer","enum":[1,2]},"subjectSlug":{"type":"string","example":"matematica"},"theme":{"type":"string","example":"Frações"},"statement":{"type":"string"},"options":{"type":"array","items":{"type":"string"},"minItems":5,"maxItems":5},"correct":{"type":"string","enum":["A","B","C","D","E"]},"explanation":{"type":"string"},"difficulty":{"type":"integer","minimum":1,"maximum":3},"avgTimeSeconds":{"type":"integer"}}},"Achievement":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"icon":{"type":"string"},"xpReward":{"type":"integer"}}},"Me":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"xp":{"type":"integer"},"levelProgress":{"type":"object","properties":{"level":{"type":"integer"},"pct":{"type":"integer"},"nextLevelXp":{"type":"integer"}}},"lives":{"type":"integer"},"nextLifeInSeconds":{"type":"integer"},"streak":{"type":"integer"},"accuracy":{"type":"integer"},"dueReviews":{"type":"integer"},"challengeDoneToday":{"type":"boolean"}}}}}}