# BovManager — Backend PHP / MySQL (MVC)

Reestructuración del **Sistema de Hotelería Ganadera** para que persista sus
datos en **MySQL** a través de un backend **PHP con arquitectura MVC**,
**sin modificar el frontend** y con **importación de los backups antiguos**
(la versión que guardaba todo en `localStorage`).

---

## 1. La idea en una frase

El frontend (un único HTML de ~11.600 líneas) sigue usando `localStorage`
exactamente como antes. Un **puente** (`hg-storage-bridge.js`) reemplaza
`window.localStorage` por un adaptador que lee/escribe contra la API PHP, la
cual guarda todo en MySQL. El HTML **no se toca**: el backend le inyecta el
puente al momento de servirlo.

```
Navegador                         Servidor PHP (MVC)              MySQL
┌───────────────────────┐         ┌─────────────────────┐        ┌───────────┐
│ index.html (INTACTO)  │  HTTP   │ AppController        │        │ empresas  │
│  · usa localStorage ──┼────────▶│ DataController (API) │───────▶│ cuentas   │
│  · hg-storage-bridge  │  JSON   │ BackupController     │        │ empresa_  │
│    intercepta y sync  │◀────────│ Models / PDO         │◀───────│   datos   │
└───────────────────────┘         └─────────────────────┘        └───────────┘
```

> **Único punto de contacto con el frontend:** una etiqueta `<script>` que el
> backend agrega al vuelo. El archivo `frontend/index.html` queda byte por byte
> igual al original. Un HTML estático no puede hablar con MySQL por sí solo, así
> que ese *seam* mínimo es inevitable — pero las 11.600 líneas de la app no
> cambian.

---

## 2. Arquitectura MVC

```
bovmanager/
├── public/                     ← raíz web (DocumentRoot)
│   ├── index.php               ← Front Controller (todas las rutas)
│   ├── .htaccess               ← rewrite → index.php
│   ├── importar.html           ← página de admin para importar backups
│   └── assets/
│       └── hg-storage-bridge.js← el puente localStorage → API
├── app/
│   ├── Core/                   ← núcleo (sin dependencias externas)
│   │   ├── App.php             ← bootstrap + autoloader PSR-4
│   │   ├── Database.php        ← PDO singleton
│   │   ├── Router.php  Request.php  Response.php  Controller.php
│   │   └── KeyResolver.php     ← resuelve claves hg_<slug>_<suf>
│   ├── Controllers/
│   │   ├── AppController.php    ← sirve el HTML + inyecta el puente
│   │   ├── DataController.php   ← API del puente (global/bundle/get/push)
│   │   ├── BackupController.php ← importación de backups antiguos
│   │   └── HealthController.php
│   └── Models/
│       ├── Empresa.php  Cuenta.php  EmpresaDato.php
├── config/
│   ├── config.php              ← credenciales (copiá desde config.example.php)
│   └── config.example.php
├── database/
│   ├── schema.sql              ← esquema MySQL
│   ├── install.sh              ← aplica el esquema
│   └── import_backup.php       ← importador por consola (CLI)
└── frontend/
    └── index.html              ← tu app original, SIN modificar
```

**Modelo de datos (diseño híbrido):**

- `empresas` y `cuentas` → tablas relacionales (multi-tenant, unicidad, FKs).
- `empresa_datos` → almacén clave/valor: una fila por cada clave de
  `localStorage` del tipo `hg_<slug>_<clave>` (tropas, checks, muertes,
  pesadas, tratamientos, `config_empresa`, `logo_empresa`, …). Guarda el
  **string crudo** tal cual, así el frontend recibe exactamente la forma que
  espera y no hace falta tocarlo.

---

## 3. Puesta en marcha

**Requisitos:** PHP 8.0+ con PDO/MySQL, y MySQL 5.7+ / 8.x.

```bash
# 1) Base de datos
./database/install.sh root 127.0.0.1        # aplica database/schema.sql

# 2) Configuración
cp config/config.example.php config/config.php
#   → editá host/usuario/clave de MySQL en config/config.php

# 3) Servidor (desarrollo)
php -S localhost:8080 -t public public/index.php
#   Producción: apuntá el DocumentRoot de Apache/Nginx a la carpeta public/
```

Abrí <http://localhost:8080/> → verás tu app tal cual, ahora respaldada por MySQL.
`GET /health` confirma la conexión a la base.

> Con el server embebido de PHP (`php -S`), agregá `public/index.php` como
> router (como arriba) para que las rutas “bonitas” funcionen sin `.htaccess`.

---

## 4. Importar los backups antiguos

Los backups son los `.json` que generaba el botón **Exportar backup** de la
versión vieja (formato `version: 2`, con un objeto `datos` que mapea las claves
de `localStorage` a sus valores). Hay tres formas de migrarlos:

**A. Por consola (recomendado para migrar en lote):**
```bash
php database/import_backup.php  backups/backup_estancia_2025-06-30.json
php database/import_backup.php  carpeta_con_todos_los_backups/
```

**B. Por navegador (para un admin):** abrí `/importar.html`, elegí uno o varios
archivos y presioná *Importar*.

**C. Desde la propia app:** el botón *Restaurar backup* del sistema sigue
funcionando; como el puente intercepta las escrituras a `localStorage`, los
datos restaurados se sincronizan solos a MySQL.

Qué se importa de cada backup: la empresa (`empresas`), sus datos por clave
(`empresa_datos`) y sus cuentas de usuario (`cuentas`). Las claves de snapshots
locales (`hg_backup_*`) se ignoran a propósito por ser propias del dispositivo.
Cada importación queda registrada en la tabla `import_log`.

---

## 5. Qué se sincroniza y qué queda local

| Clave                         | Dónde vive        | Motivo |
|-------------------------------|-------------------|--------|
| `hg_cuentas`                  | MySQL (`cuentas`) | login |
| `hg_estado_empresas`          | MySQL (`empresas.habilitada`) | habilitación |
| `hg_<slug>_<suf>` (20 sufijos)| MySQL (`empresa_datos`) | datos de la empresa |
| `hg_sesion`                   | localStorage real | sesión por dispositivo |
| `hg_backup_*`                 | localStorage real | snapshots locales de respaldo |

---

## 6. Nota de seguridad (importante)

El frontend original hace el login **del lado del cliente** comparando la clave
con `btoa(pass)` (Base64, **no** es un hash). Para no modificar el frontend, el
backend conserva ese valor tal cual (`cuentas.pass_b64`) y expone `hg_cuentas`
al cliente, igual que hacía la versión de `localStorage`.

Esto es funcionalmente equivalente a la app anterior, pero **no es seguro** para
Internet abierto. Para endurecerlo (autenticación real en el servidor con
contraseñas hasheadas y sesiones PHP) hace falta cambiar el flujo de login del
frontend — algo que queda **fuera** del mandato “no modificar el frontend”. El
código está estructurado para agregar un `AuthController` server-side cuando se
decida dar ese paso. Mientras tanto, se recomienda servir la app sólo en red
interna o detrás de VPN / autenticación del servidor web.

---

## 7. Endpoints

| Método | Ruta                  | Descripción |
|--------|-----------------------|-------------|
| GET    | `/`                   | Sirve la app (HTML + puente inyectado) |
| GET    | `/api/data/global`    | Claves globales (login) |
| GET    | `/api/data/bundle?slug=` | Todas las claves de una empresa |
| GET    | `/api/data/get?key=`  | Una clave puntual (respaldo síncrono) |
| POST   | `/api/data/push`      | Guarda un lote de cambios |
| POST   | `/api/backup/import`  | Importa un backup antiguo |
| GET    | `/health`             | Estado de la conexión a MySQL |
