<?php

/**
 * CategoryService - CRUD operations for categories
 */

declare(strict_types=1);

namespace Notifier\Services;

use PDO;

class CategoryService
{
    private PDO $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    private function normalizeIcon(?string $icon): ?string
    {
        if ($icon === null) {
            return null;
        }

        $icon = \trim($icon);
        if ($icon === '') {
            return null;
        }

        if (!\preg_match('/^[a-z0-9-]+$/i', $icon)) {
            return null;
        }

        if (\strlen($icon) > 50) {
            $icon = \substr($icon, 0, 50);
        }

        return $icon;
    }

    /**
     * Get all categories.
     *
     * @return array List of categories
     */
    public function getAll(): array
    {
        $stmt = $this->pdo->query('
            SELECT id, name, color, icon, created_at
            FROM categories
            ORDER BY name ASC
        ');
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    /**
     * Get a category by ID.
     *
     * @param int $id Category ID
     * @return array|null Category data or null if not found
     */
    public function getById(int $id): ?array
    {
        $stmt = $this->pdo->prepare('
            SELECT id, name, color, icon, created_at
            FROM categories
            WHERE id = ?
        ');
        $stmt->execute([$id]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        return $result ?: null;
    }

    /**
     * Create a new category.
     *
     * @param string $name Category name
     * @param string $color Hex color code (e.g., #6366f1)
     * @param string|null $icon Emoji or icon identifier
     * @return array ['success' => bool, 'id' => int|null, 'error' => string|null]
     */
    public function create(string $name, string $color = '#6366f1', ?string $icon = null): array
    {
        // Validate
        $name = \trim($name);
        if (empty($name)) {
            return ['success' => false, 'id' => null, 'error' => 'Name is required'];
        }

        if (!\preg_match('/^#[0-9a-fA-F]{6}$/', $color)) {
            return ['success' => false, 'id' => null, 'error' => 'Invalid color format. Use #RRGGBB'];
        }

        // Sanitize icon (allow safe class names only)
        $icon = $this->normalizeIcon($icon);

        // Check for duplicate name
        $stmt = $this->pdo->prepare('SELECT id FROM categories WHERE name = ?');
        $stmt->execute([$name]);
        if ($stmt->fetch()) {
            return ['success' => false, 'id' => null, 'error' => 'Category name already exists'];
        }

        try {
            $stmt = $this->pdo->prepare('
                INSERT INTO categories (name, color, icon)
                VALUES (?, ?, ?)
            ');
            $stmt->execute([$name, $color, $icon]);
            $id = (int) $this->pdo->lastInsertId();

            return ['success' => true, 'id' => $id, 'error' => null];
        } catch (\PDOException $e) {
            return ['success' => false, 'id' => null, 'error' => 'Database error: ' . $e->getMessage()];
        }
    }

    /**
     * Update a category.
     *
     * @param int $id Category ID
     * @param string $name New name
     * @param string $color New color
     * @param string|null $icon New icon (emoji or identifier)
     * @return array ['success' => bool, 'error' => string|null]
     */
    public function update(int $id, string $name, string $color, ?string $icon = null): array
    {
        // Validate
        $name = \trim($name);
        if (empty($name)) {
            return ['success' => false, 'error' => 'Name is required'];
        }

        if (!\preg_match('/^#[0-9a-fA-F]{6}$/', $color)) {
            return ['success' => false, 'error' => 'Invalid color format. Use #RRGGBB'];
        }

        // Sanitize icon (allow safe class names only)
        $icon = $this->normalizeIcon($icon);

        // Check if category exists
        $existing = $this->getById($id);
        if (!$existing) {
            return ['success' => false, 'error' => 'Category not found'];
        }

        // Check for duplicate name (excluding self)
        $stmt = $this->pdo->prepare('SELECT id FROM categories WHERE name = ? AND id != ?');
        $stmt->execute([$name, $id]);
        if ($stmt->fetch()) {
            return ['success' => false, 'error' => 'Category name already exists'];
        }

        try {
            $stmt = $this->pdo->prepare('
                UPDATE categories
                SET name = ?, color = ?, icon = ?
                WHERE id = ?
            ');
            $stmt->execute([$name, $color, $icon, $id]);

            return ['success' => true, 'error' => null];
        } catch (\PDOException $e) {
            return ['success' => false, 'error' => 'Database error: ' . $e->getMessage()];
        }
    }

    /**
     * Delete a category.
     * Notifications in this category become uncategorized (category_id = NULL).
     *
     * @param int $id Category ID
     * @return array ['success' => bool, 'error' => string|null]
     */
    public function delete(int $id): array
    {
        // Check if category exists
        $existing = $this->getById($id);
        if (!$existing) {
            return ['success' => false, 'error' => 'Category not found'];
        }

        try {
            // FK constraint with ON DELETE SET NULL handles notifications
            $stmt = $this->pdo->prepare('DELETE FROM categories WHERE id = ?');
            $stmt->execute([$id]);

            return ['success' => true, 'error' => null];
        } catch (\PDOException $e) {
            return ['success' => false, 'error' => 'Database error: ' . $e->getMessage()];
        }
    }

    /**
     * Get notification count per category.
     *
     * @return array Category ID => count mapping
     */
    public function getNotificationCounts(): array
    {
        $stmt = $this->pdo->query('
            SELECT category_id, COUNT(*) as count
            FROM notifications
            WHERE category_id IS NOT NULL
            GROUP BY category_id
        ');

        $counts = [];
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
            $counts[$row['category_id']] = (int) $row['count'];
        }
        return $counts;
    }
}
