# Documentação de Funções - Fábrica dos Ovos

## Visão Geral do Projeto
Sistema de votação de ovos de Páscoa com autenticação por email, funcionalidades de administração e estatísticas em tempo real.

### ✨ Funcionalidades Principais
- **Autenticação por email** com controlo de acesso
- **Sistema de votação** com classificação de 1-5 estrelas
- **Restrições inteligentes:** Pontuações 4★ e 5★ limitadas a 1 uso cada
- **Proteção anti-auto-votação:** Autores não podem votar nos próprios ovos
- **Painel de administração** completo
- **Gestão de autores** para cada item
- **Estatísticas em tempo real** com médias e distribuição
- **Interface responsiva** com design moderno

---

## Ficheiros do Projeto

### Ficheiros PHP Principais
- **index.php** - Página de login
- **vote.php** - Interface de votação para utilizadores
- **admin.php** - Painel de administração
- **logout.php** - Termina a sessão do utilizador
- **src/bootstrap.php** - Inicialização da aplicação
- **src/functions.php** - Todas as funções auxiliares

### Ficheiros Python
- **fix_votes.py** - Script de correção de formulários

### Ficheiros de Dados (JSON)
- **data/allowed_emails.json** - Lista de emails autorizados
- **data/admin_emails.json** - Lista de emails de administradores
- **data/items.json** - Itens (ovos) disponíveis para votação
- **data/votes.json** - Votos registados por utilizador

---

## Funções PHP

### 1. Gestão de Ficheiros e Dados

#### `ensure_data_files(): void`
**Ficheiro:** src/functions.php  
**Descrição:** Garante que o diretório de dados existe e cria ficheiros JSON com valores padrão se não existirem.

**Funcionamento:**
- Cria o diretório `data/` se não existir
- Cria ficheiros JSON com dados iniciais:
  - `allowed_emails.json` - emails autorizados
  - `admin_emails.json` - emails de administradores
  - `items.json` - itens exemplo (Ovo 1 e Ovo 2)
  - `votes.json` - objeto vazio para votos

**Exemplo de uso:**
```php
ensure_data_files();
```

---

#### `load_json(string $file, $fallback)`
**Ficheiro:** src/functions.php  
**Descrição:** Carrega e descodifica um ficheiro JSON.

**Parâmetros:**
- `$file` - Caminho completo do ficheiro JSON
- `$fallback` - Valor a retornar se o ficheiro não existir ou estiver inválido

**Retorno:** Dados descodificados do JSON ou o valor de fallback

**Exemplo de uso:**
```php
$items = load_json(DATA_DIR . '/items.json', []);
$votes = load_json(DATA_DIR . '/votes.json', []);
```

---

#### `save_json(string $file, $data): void`
**Ficheiro:** src/functions.php  
**Descrição:** Guarda dados num ficheiro JSON com formatação legível.

**Parâmetros:**
- `$file` - Caminho do ficheiro onde guardar
- `$data` - Dados a serializar em JSON

**Características:**
- Usa `JSON_PRETTY_PRINT` para formatação
- Usa `JSON_UNESCAPED_SLASHES` para manter barras
- Adiciona `LOCK_EX` para evitar escrita concorrente

**Exemplo de uso:**
```php
save_json(DATA_DIR . '/votes.json', $votes);
```

---

### 2. Gestão de Utilizadores e Autenticação

#### `normalize_email(string $email): string`
**Ficheiro:** src/functions.php  
**Descrição:** Normaliza e valida um endereço de email.

**Parâmetros:**
- `$email` - Email a normalizar

**Retorno:** Email normalizado (minúsculas, sem espaços) ou string vazia se inválido

**Exemplo de uso:**
```php
$email = normalize_email('Usuario@Example.COM  ');
// Retorna: 'usuario@example.com'
```

---

#### `email_is_allowed(string $email): bool`
**Ficheiro:** src/functions.php  
**Descrição:** Verifica se um email está na lista de utilizadores autorizados.

**Parâmetros:**
- `$email` - Email a verificar

**Retorno:** `true` se autorizado, `false` caso contrário

**Exemplo de uso:**
```php
if (email_is_allowed($email)) {
    // Permitir acesso
}
```

---

#### `email_is_admin(string $email): bool`
**Ficheiro:** src/functions.php  
**Descrição:** Verifica se um email pertence a um administrador.

**Parâmetros:**
- `$email` - Email a verificar

**Retorno:** `true` se for administrador, `false` caso contrário

**Exemplo de uso:**
```php
$isAdmin = email_is_admin('admin@pascoa.local');
```

---

#### `current_user_email(): ?string`
**Ficheiro:** src/functions.php  
**Descrição:** Obtém o email do utilizador atualmente autenticado.

**Retorno:** Email do utilizador ou `null` se não autenticado

**Exemplo de uso:**
```php
$email = current_user_email();
if ($email !== null) {
    echo "Utilizador: $email";
}
```

---

#### `is_logged_in(): bool`
**Ficheiro:** src/functions.php  
**Descrição:** Verifica se existe um utilizador autenticado.

**Retorno:** `true` se autenticado, `false` caso contrário

**Exemplo de uso:**
```php
if (is_logged_in()) {
    redirect('vote.php');
}
```

---

#### `is_admin(): bool`
**Ficheiro:** src/functions.php  
**Descrição:** Verifica se o utilizador atual tem privilégios de administrador.

**Retorno:** `true` se for admin, `false` caso contrário

**Exemplo de uso:**
```php
if (is_admin()) {
    // Mostrar opções de administração
}
```

---

### 3. Controlo de Acesso

#### `require_login(): void`
**Ficheiro:** src/functions.php  
**Descrição:** Força autenticação, redireciona para index.php se não autenticado.

**Exemplo de uso:**
```php
require_login(); // No início de vote.php
```

---

#### `require_admin(): void`
**Ficheiro:** src/functions.php  
**Descrição:** Força privilégios de administrador, redireciona se não for admin.

**Funcionamento:**
- Chama `require_login()` primeiro
- Depois verifica se é admin
- Redireciona para vote.php se não for admin

**Exemplo de uso:**
```php
require_admin(); // No início de admin.php
```

---

### 4. Navegação e Utilidades

#### `redirect(string $path): void`
**Ficheiro:** src/functions.php  
**Descrição:** Redireciona para um caminho específico e termina a execução.

**Parâmetros:**
- `$path` - Caminho relativo para redirecionamento

**Exemplo de uso:**
```php
redirect('vote.php');
redirect('index.php');
```

---

#### `escape(string $value): string`
**Ficheiro:** src/functions.php  
**Descrição:** Escapa caracteres especiais HTML para prevenir XSS.

**Parâmetros:**
- `$value` - String a escapar

**Retorno:** String com caracteres HTML especiais convertidos

**Exemplo de uso:**
```php
echo escape($userInput);
// <script> torna-se &lt;script&gt;
```

---

### 5. Segurança

#### `disable_inspect(): void`
**Ficheiro:** src/functions.php  
**Descrição:** Injeta JavaScript para desativar ferramentas de desenvolvedor no navegador.

**Funcionalidades bloqueadas:**
- Menu de contexto (botão direito)
- F12 (DevTools)
- Ctrl+Shift+I, Ctrl+Shift+J, Ctrl+Shift+C
- Ctrl+U (ver código-fonte)

**Nota:** Esta é uma medida básica e pode ser contornada.

**Exemplo de uso:**
```php
<?php disable_inspect(); ?>
```

---

#### `csrf_token(): string`
**Ficheiro:** src/functions.php  
**Descrição:** Gera ou retorna o token CSRF da sessão atual.

**Retorno:** Token CSRF de 32 caracteres hexadecimais

**Exemplo de uso:**
```php
<input type="hidden" name="csrf_token" value="<?= csrf_token() ?>">
```

---

#### `verify_csrf(): void`
**Ficheiro:** src/functions.php  
**Descrição:** Verifica se o token CSRF submetido é válido.

**Funcionamento:**
- Compara o token do POST com o da sessão
- Usa `hash_equals()` para comparação segura
- Retorna erro 403 se inválido

**Exemplo de uso:**
```php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    verify_csrf();
    // Processar formulário
}
```

---

### 6. Gestão de Conteúdo

#### `generate_id(): string`
**Ficheiro:** src/functions.php  
**Descrição:** Gera um identificador único aleatório.

**Retorno:** String hexadecimal de 16 caracteres

**Exemplo de uso:**
```php
$newItem = [
    'id' => generate_id(),
    'title' => 'Novo Ovo',
    // ...
];
```

---

#### `get_available_images(): array`
**Ficheiro:** src/functions.php  
**Descrição:** Obtém lista de todas as imagens disponíveis na pasta `imagens/`.

**Retorno:** Array associativo com:
- Chave: caminho relativo da imagem
- Valor: array com 'path' e 'label' (nome do ficheiro sem extensão)

**Formatos suportados:** PNG, JPG, JPEG, GIF, WEBP

**Exemplo de uso:**
```php
$images = get_available_images();
foreach ($images as $path => $info) {
    echo $info['label'];
}
```

---

### 7. Estatísticas e Análise

#### `build_item_stats(array $items, array $votes): array`
**Ficheiro:** src/functions.php  
**Descrição:** Calcula estatísticas de votação para todos os itens.

**Parâmetros:**
- `$items` - Array de itens (ovos)
- `$votes` - Array de votos por utilizador

**Retorno:** Array ordenado por média (descendente) com:
- `id` - ID do item
- `title` - Título do item
- `image` - Caminho da imagem
- `count` - Número total de votos
- `average` - Média de classificação
- `distribution` - Distribuição de votos (1-5 estrelas)

**Exemplo de uso:**
```php
$stats = build_item_stats($items, $allVotes);
foreach ($stats as $stat) {
    echo "{$stat['title']}: {$stat['average']} estrelas";
}
```

---

#### `format_distribution(array $distribution): string`
**Ficheiro:** src/functions.php  
**Descrição:** Formata a distribuição de votos numa string legível.

**Parâmetros:**
- `$distribution` - Array com contagem por classificação (1-5)

**Retorno:** String no formato "1: X | 2: Y | 3: Z | 4: W | 5: V"

**Exemplo de uso:**
```php
$dist = [1 => 2, 2 => 3, 3 => 5, 4 => 8, 5 => 12];
echo format_distribution($dist);
// Resultado: "1: 2 | 2: 3 | 3: 5 | 4: 8 | 5: 12"
```

---

#### `find_item_title(array $items, string $itemId): ?string`
**Ficheiro:** src/functions.php  
**Descrição:** Procura o título de um item pelo seu ID.

**Parâmetros:**
- `$items` - Array de itens
- `$itemId` - ID do item a procurar

**Retorno:** Título do item ou `null` se não encontrado

**Exemplo de uso:**
```php
$title = find_item_title($items, $itemId);
if ($title !== null) {
    echo "Item: $title";
}
```

---

### 8. Formatação

#### `format_datetime(?string $value): string`
**Ficheiro:** src/functions.php  
**Descrição:** Formata uma data/hora ISO 8601 para formato português.

**Parâmetros:**
- `$value` - String de data em formato ISO 8601

**Retorno:** Data no formato "dd/mm/YYYY HH:MM" ou "-" se inválida

**Exemplo de uso:**
```php
echo format_datetime('2026-03-30T15:30:00+00:00');
// Resultado: "30/03/2026 15:30"
```

---

### 9. Validação e Restrições de Votação

#### `can_user_vote_on_item(string $email, array $item): bool`
**Ficheiro:** src/functions.php  
**Descrição:** Verifica se um utilizador pode votar num item (não é autor do ovo).

**Parâmetros:**
- `$email` - Email do utilizador
- `$item` - Array com dados do item, incluindo campo 'authors'

**Retorno:** `true` se pode votar, `false` se é autor

**Funcionamento:**
- Se o item não tem autores definidos, qualquer um pode votar
- Normaliza o email do utilizador e dos autores
- Verifica se o email está na lista de autores

**Exemplo de uso:**
```php
if (can_user_vote_on_item($email, $item)) {
    // Permitir votação
} else {
    // Mostrar mensagem: "Não pode votar no seu próprio ovo"
}
```

---

#### `get_user_vote_counts(string $email, array $votes): array`
**Ficheiro:** src/functions.php  
**Descrição:** Obtém a contagem de votos por pontuação de um utilizador.

**Parâmetros:**
- `$email` - Email do utilizador
- `$votes` - Array completo de votos

**Retorno:** Array associativo com contagem por pontuação (1-5)

**Formato de retorno:**
```php
[
    1 => 2,  // 2 votos com 1 estrela
    2 => 1,  // 1 voto com 2 estrelas
    3 => 3,  // 3 votos com 3 estrelas
    4 => 1,  // 1 voto com 4 estrelas
    5 => 1,  // 1 voto com 5 estrelas
]
```

**Exemplo de uso:**
```php
$counts = get_user_vote_counts($email, $votes);
if ($counts[5] >= 1) {
    echo "Já usou a pontuação de 5 estrelas";
}
```

---

#### `validate_vote_restrictions(string $email, array $votes, int $newScore, ?string $currentItemId = null): array`
**Ficheiro:** src/functions.php  
**Descrição:** Valida se o utilizador pode dar uma pontuação específica segundo as regras de restrição.

**Regras de Votação:**
- ⭐⭐⭐⭐⭐ (5 estrelas): Pode usar apenas **1 vez**
- ⭐⭐⭐⭐ (4 estrelas): Pode usar apenas **1 vez**
- ⭐⭐⭐ / ⭐⭐ / ⭐ (1-3 estrelas): **Ilimitadas**

**Parâmetros:**
- `$email` - Email do utilizador
- `$votes` - Array completo de votos
- `$newScore` - Nova pontuação a atribuir (1-5)
- `$currentItemId` - (Opcional) ID do item sendo editado

**Retorno:** Array com:
- `valid` (bool) - Se a votação é válida
- `message` (string) - Mensagem de erro se inválida

**Funcionamento:**
- Pontuações 1-3 sempre retornam válido
- Para 4 e 5, verifica se já foram usadas
- Se `$currentItemId` fornecido, desconta voto atual (permite edição)

**Exemplo de uso:**
```php
$validation = validate_vote_restrictions($email, $votes, 5, $itemId);
if (!$validation['valid']) {
    echo $validation['message'];
    // "Já atribuiu a pontuação 5 a outro ovo."
}
```

---

## Funções Python

### `replacer(match)`
**Ficheiro:** fix_votes.py  
**Descrição:** Função de substituição para expressões regulares que substitui formulários de voto por elementos desativados.

**Parâmetros:**
- `match` - Objeto Match da regex

**Retorno:** String HTML com grupo de classificação desativado

**Contexto:** Script usado para desativar formulários de voto em vote.php, convertendo-os em visualização apenas de leitura.

---

## Estrutura da Base de Dados (JSON)

### allowed_emails.json
```json
{
  "utilizadores": [
    "admin@pascoa.local",
    "colaborador@pascoa.local"
  ]
}
```

### admin_emails.json
```json
{
  "admins": [
    "admin@pascoa.local"
  ]
}
```

### items.json
```json
[
  {
    "id": "a1b2c3d4e5f6g7h8",
    "title": "Ovo 1",
    "description": "Descrição do ovo",
    "image": "imagens/Ovo_1-01.png",
    "authors": [
      "joao@exemplo.com",
      "maria@exemplo.com"
    ],
    "created_at": "2026-03-30T12:00:00+00:00"
  }
]
```

**Campos:**
- `id` - Identificador único gerado
- `title` - Título do ovo
- `description` - Descrição (opcional)
- `image` - Caminho relativo da imagem
- `authors` - Array de emails dos autores (não podem votar no próprio ovo)
- `created_at` - Data de criação em formato ISO 8601

### votes.json
```json
{
  "user@example.com": {
    "item_id_1": {
      "score": 5
    },
    "item_id_2": {
      "score": 4
    }
  }
}
```

---

## Fluxo de Autenticação

1. Utilizador acede a **index.php**
2. Introduz email
3. Sistema verifica com `normalize_email()` e `email_is_allowed()`
4. Se válido, cria sessão com `$_SESSION['user_email']`
5. Define `$_SESSION['is_admin']` usando `email_is_admin()`
6. Redireciona para **vote.php**

---

## Fluxo de Votação

1. `require_login()` verifica autenticação
2. Carrega itens e votos existentes
3. Calcula contagem de votos do utilizador com `get_user_vote_counts()`
4. Mostra painel informativo com pontuações disponíveis/usadas
5. Utilizador submete voto (1-5 estrelas)
6. `verify_csrf()` valida o pedido
7. Verifica se item existe
8. **Validação de autorização:** `can_user_vote_on_item()` - bloqueia se for autor
9. **Validação de restrições:** `validate_vote_restrictions()` - verifica limite de 4★ e 5★
10. Se válido: Voto é guardado em `votes.json` indexado por email
11. Confirmação ou erro exibido ao utilizador

---

## Fluxo de Administração

1. `require_admin()` verifica privilégios
2. **Upload de Imagem:**
   - Valida extensão do ficheiro
   - Gera nome único com `generate_id()`
   - Move para pasta `imagens/`
   - **Processa campo 'authors':** emails separados por vírgula
   - Cria novo item em `items.json` com autores associados
3. **Editar Autores:**
   - Modal permite editar lista de autores de qualquer item
   - Normaliza emails introduzidos
   - Atualiza campo 'authors' em `items.json`
4. **Remover Item:**
   - Remove item de `items.json`
   - Limpa votos associados em `votes.json`
5. **Reset de Votos:**
   - Remove todos os votos de um utilizador específico
6. **Estatísticas:**
   - Mostra coluna de autores para cada item
   - Usa `build_item_stats()` para calcular médias e distribuições

---

## Constantes Globais

Definidas em **src/bootstrap.php**:

- `BASE_DIR` - Diretório raiz do projeto
- `DATA_DIR` - Diretório dos ficheiros JSON (`BASE_DIR/data`)

---

## Segurança Implementada

1. **CSRF Protection:** Tokens em todos os formulários
2. **Validação de Email:** Normalização e validação
3. **Lista de Controlo de Acesso:** Apenas emails autorizados
4. **Escape de Output:** Função `escape()` para prevenir XSS
5. **Strict Types:** `declare(strict_types=1)` em todos os ficheiros PHP
6. **File Locking:** `LOCK_EX` ao guardar JSON para evitar corrupção

---

## Sessões PHP

Variáveis de sessão utilizadas:
- `$_SESSION['user_email']` - Email do utilizador autenticado
- `$_SESSION['is_admin']` - Boolean indicando privilégios de admin
- `$_SESSION['csrf_token']` - Token CSRF para proteção de formulários

---

## Regras de Votação

### Restrições de Pontuação

O sistema implementa regras específicas para garantir uma votação justa:

#### Pontuações Limitadas
- **5 Estrelas (⭐⭐⭐⭐⭐)**: Cada utilizador pode atribuir apenas **1 vez**
- **4 Estrelas (⭐⭐⭐⭐)**: Cada utilizador pode atribuir apenas **1 vez**

#### Pontuações Ilimitadas
- **3 Estrelas (⭐⭐⭐)**: Sem limite de utilização
- **2 Estrelas (⭐⭐)**: Sem limite de utilização
- **1 Estrela (⭐)**: Sem limite de utilização

### Proteção contra Auto-Votação

Os autores de um ovo **não podem votar** no seu próprio trabalho:

1. Cada item tem um campo `authors` com lista de emails
2. Sistema verifica se o utilizador está na lista de autores
3. Se for autor, a votação é bloqueada com mensagem:
   - _"Não pode votar no seu próprio ovo! 🚫"_

### Edição de Votos

- Utilizadores podem **alterar** seus votos já atribuídos
- Ao editar, a restrição considera o voto anterior:
  - Exemplo: Se tinha 5★ no Ovo A, pode mudar para 4★ no Ovo A
  - Mas não pode dar 5★ a outro ovo se já o usou em outro lugar

### Validação em Tempo Real

Na interface de votação (vote.php):
- **Painel informativo** mostra quais pontuações já foram usadas
- Indicadores visuais:
  - ✓ **Verde**: Pontuação disponível
  - ✓ **Vermelho**: Pontuação já utilizada
- **Mensagens de erro** explicativas quando tenta usar pontuação indisponível

### Fluxo de Validação

```
Utilizador submete voto
     ↓
Verifica se item existe
     ↓
Verifica se é autor do ovo → NÃO pode votar
     ↓
Valida restrições de pontuação 4/5
     ↓
Se válido: Guarda voto
Se inválido: Mostra mensagem de erro
```

---

## Notas Técnicas

### Tratamento de Erros
- Funções retornam valores de fallback em caso de erro
- JSON inválido retorna o valor de fallback especificado
- Validações antes de operações críticas

### Performance
- Ficheiros JSON lidos/escritos apenas quando necessário
- Arrays reindexados após remoções
- Ordenação de estatísticas feita em memória

### Compatibilidade
- PHP 7.4+ (strict types, typed properties)
- Modern browsers para CSS Grid e Flexbox
- Python 3.x para scripts auxiliares

---

## Melhorias Futuras Sugeridas

1. **Base de Dados:** Migrar de JSON para MySQL/PostgreSQL
2. **Cache:** Implementar cache de estatísticas
3. **API REST:** Criar endpoints para integração
4. **Testes Unitários:** Adicionar PHPUnit
5. **Logs:** Sistema de logging de ações
6. **Paginação:** Para grandes volumes de itens
7. **Upload Assíncrono:** AJAX para uploads de imagens
8. **Validação de Imagens:** Verificar dimensões e tipo MIME

---

**Última Atualização:** 30 de Março de 2026  
**Versão do Documento:** 1.0
