<?php
// Shared CRUD for the `posts` and `tutorials` tables — they're identical in
// shape (title/slug/lang/category/excerpt/content_html/cover_image/status), so
// one set of functions backs both api/posts/*.php and api/tutorials/*.php
// instead of duplicating the SQL twice.

const ENTITY_TABLES = ['posts', 'tutorials'];
const ENTITY_LANGS = ['es', 'en'];
const ENTITY_DEFAULT_LANG = 'es';

function entity_assert_table(string $table): void
{
    if (!in_array($table, ENTITY_TABLES, true)) {
        json_error('Recurso desconocido.', 404);
    }
}

/** Normalizes a language from a request; unknown values fall back to Spanish. */
function entity_lang($value): string
{
    $lang = is_string($value) ? strtolower(trim($value)) : '';
    return in_array($lang, ENTITY_LANGS, true) ? $lang : ENTITY_DEFAULT_LANG;
}

function entity_list(PDO $pdo, string $table, bool $all, ?string $lang = null): array
{
    entity_assert_table($table);

    $sql = "SELECT id, title, slug, lang, category, excerpt, cover_image, status, created_at, updated_at, published_at FROM `$table`";
    $where = [];
    $params = [];

    if (!$all) $where[] = "status = 'published'";
    // A null lang means "every language" — used by the admin list so one
    // screen shows all content; the public site always passes a language.
    if ($lang !== null) {
        $where[] = 'lang = ?';
        $params[] = $lang;
    }

    if ($where) $sql .= ' WHERE ' . implode(' AND ', $where);
    $sql .= ' ORDER BY COALESCE(published_at, created_at) DESC';

    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return $stmt->fetchAll();
}

function entity_get_by_id(PDO $pdo, string $table, int $id): ?array
{
    entity_assert_table($table);
    $stmt = $pdo->prepare("SELECT * FROM `$table` WHERE id = ? LIMIT 1");
    $stmt->execute([$id]);
    $row = $stmt->fetch();
    return $row ?: null;
}

function entity_get_by_slug(PDO $pdo, string $table, string $slug, string $lang, bool $includeDrafts): ?array
{
    entity_assert_table($table);
    $sql = "SELECT * FROM `$table` WHERE slug = ? AND lang = ?";
    if (!$includeDrafts) $sql .= " AND status = 'published'";
    $sql .= ' LIMIT 1';
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$slug, $lang]);
    $row = $stmt->fetch();
    return $row ?: null;
}

/** Slug collisions are per-language, matching the uq_(lang, slug) index. */
function entity_slug_taken(PDO $pdo, string $table, string $slug, string $lang, ?int $excludeId): bool
{
    entity_assert_table($table);
    $sql = "SELECT id FROM `$table` WHERE slug = ? AND lang = ?";
    $params = [$slug, $lang];
    if ($excludeId !== null) {
        $sql .= ' AND id != ?';
        $params[] = $excludeId;
    }
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return (bool) $stmt->fetch();
}

// Validates and normalizes the editable fields from a request body. Ends
// the request with a 422 if anything required is missing/invalid.
function entity_validate_input(array $body): array
{
    $title = trim((string) ($body['title'] ?? ''));
    $slug = trim((string) ($body['slug'] ?? ''));
    $status = ($body['status'] ?? 'draft') === 'published' ? 'published' : 'draft';
    $contentHtml = (string) ($body['content_html'] ?? '');

    if ($title === '') json_error('El título es requerido.', 422);
    if (!preg_match('/^[a-z0-9]+(-[a-z0-9]+)*$/', $slug)) {
        json_error('El slug debe usar minúsculas, números y guiones (ej: mi-articulo).', 422);
    }
    if ($contentHtml === '') json_error('El contenido es requerido.', 422);

    return [
        'title' => $title,
        'slug' => $slug,
        'lang' => entity_lang($body['lang'] ?? null),
        'category' => trim((string) ($body['category'] ?? '')) ?: null,
        'excerpt' => trim((string) ($body['excerpt'] ?? '')) ?: null,
        'content_html' => $contentHtml,
        'cover_image' => trim((string) ($body['cover_image'] ?? '')) ?: null,
        'status' => $status,
    ];
}

function entity_create(PDO $pdo, string $table, array $fields): int
{
    entity_assert_table($table);
    if (entity_slug_taken($pdo, $table, $fields['slug'], $fields['lang'], null)) {
        json_error('Ya existe contenido con ese slug en este idioma.', 409);
    }

    $publishedAt = $fields['status'] === 'published' ? date('Y-m-d H:i:s') : null;

    $stmt = $pdo->prepare(
        "INSERT INTO `$table` (title, slug, lang, category, excerpt, content_html, cover_image, status, published_at)
         VALUES (:title, :slug, :lang, :category, :excerpt, :content_html, :cover_image, :status, :published_at)"
    );
    $stmt->execute(array_merge($fields, [
        'published_at' => $publishedAt,
    ]));

    return (int) $pdo->lastInsertId();
}

function entity_update(PDO $pdo, string $table, int $id, array $fields): void
{
    entity_assert_table($table);
    $existing = entity_get_by_id($pdo, $table, $id);
    if (!$existing) json_error('No encontrado.', 404);

    if (entity_slug_taken($pdo, $table, $fields['slug'], $fields['lang'], $id)) {
        json_error('Ya existe contenido con ese slug en este idioma.', 409);
    }

    $publishedAt = $existing['published_at'];
    if ($fields['status'] === 'published' && !$publishedAt) {
        $publishedAt = date('Y-m-d H:i:s');
    } elseif ($fields['status'] === 'draft') {
        $publishedAt = null;
    }

    $stmt = $pdo->prepare(
        "UPDATE `$table` SET title = :title, slug = :slug, lang = :lang, category = :category, excerpt = :excerpt,
         content_html = :content_html, cover_image = :cover_image, status = :status, published_at = :published_at
         WHERE id = :id"
    );
    $stmt->execute(array_merge($fields, [
        'published_at' => $publishedAt,
        'id' => $id,
    ]));
}

function entity_delete(PDO $pdo, string $table, int $id): void
{
    entity_assert_table($table);
    $stmt = $pdo->prepare("DELETE FROM `$table` WHERE id = ?");
    $stmt->execute([$id]);
}
