<?php

namespace App\Models;

use PDO;
use PDOException;

class Category {
    private PDO $db;
    private ?int $id;
    private string $name;
    private ?int $parentId;

    public function __construct(PDO $db, ?int $id = null, string $name = '', ?int $parentId = null) {
        $this->db = $db;
        $this->id = $id;
        $this->name = $name;
        $this->parentId = $parentId;
    }

    public function getId(): ?int {
        return $this->id;
    }

    public function getName(): string {
        return $this->name;
    }

    public function setName(string $name): void {
        $this->name = $name;
    }

    public function getParentId(): ?int {
        return $this->parentId;
    }

    public function setParentId(?int $parentId): void {
        $this->parentId = $parentId;
    }

    public static function getAll(PDO $db): array {
        try {
            $stmt = $db->query('SELECT c.id, c.name, c.parent_id, p.name as parent_name FROM categories c LEFT JOIN categories p ON c.parent_id = p.id ORDER BY c.name');
            $categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
            // Add full path
            foreach ($categories as &$category) {
                $category['path'] = self::getFullPath($db, $category['id']);
            }
            return $categories;
        } catch (PDOException $e) {
            error_log("Model Error: Failed to get all categories - " . $e->getMessage());
            throw $e;
        }
    }

    public static function getFullPath(PDO $db, int $id, string $separator = ' / ', array &$visited = []): string {
        if (in_array($id, $visited)) {
            return '[Circular]'; // Indicate circular reference
        }
        $category = self::getById($db, $id);
        if (!$category) return '';
        if ($category['parent_id']) {
            $visited[] = $id;
            return self::getFullPath($db, $category['parent_id'], $separator, $visited) . $separator . $category['name'];
        } else {
            return $category['name'];
        }
    }

    public static function getDescendantIds(PDO $db, int $id, array &$visited = []): array {
        if (in_array($id, $visited)) {
            return []; // Prevent infinite recursion
        }
        $visited[] = $id;
        $descendants = [];
        $stmt = $db->prepare('SELECT id FROM categories WHERE parent_id = :id');
        $stmt->execute([':id' => $id]);
        $children = $stmt->fetchAll(PDO::FETCH_COLUMN);
        foreach ($children as $childId) {
            $descendants[] = $childId;
            $descendants = array_merge($descendants, self::getDescendantIds($db, $childId, $visited));
        }
        return $descendants;
    }

    public static function getById(PDO $db, int $id): ?array {
        try {
            $stmt = $db->prepare('SELECT id, name, parent_id FROM categories WHERE id = :id');
            $stmt->execute([':id' => $id]);
            $category = $stmt->fetch(PDO::FETCH_ASSOC);
            return $category ?: null;
        } catch (PDOException $e) {
            error_log("Model Error: Failed to get category by ID - " . $e->getMessage());
            throw $e;
        }
    }

    public function save(): bool {
        if ($this->id === null) {
            // Add new category
            try {
                $stmt = $this->db->prepare('INSERT INTO categories (name, parent_id) VALUES (:name, :parent_id)');
                $stmt->execute([':name' => $this->name, ':parent_id' => $this->parentId]);
                $this->id = (int)$this->db->lastInsertId();
                return true;
            } catch (PDOException $e) {
                error_log("Model Error: Failed to save category - " . $e->getMessage());
                throw $e;
            }
        } else {
            // Update category
            try {
                $stmt = $this->db->prepare('UPDATE categories SET name = :name, parent_id = :parent_id WHERE id = :id');
                $stmt->execute([':name' => $this->name, ':parent_id' => $this->parentId, ':id' => $this->id]);
                return true;
            } catch (PDOException $e) {
                error_log("Model Error: Failed to update category - " . $e->getMessage());
                throw $e;
            }
        }
    }

    public static function delete(PDO $db, int $id): bool {
        try {
            $stmt = $db->prepare('DELETE FROM categories WHERE id = :id');
            $stmt->execute([':id' => $id]);
            return $stmt->rowCount() > 0;
        } catch (PDOException $e) {
            error_log("Model Error: Failed to delete category - " . $e->getMessage());
            throw $e;
        }
    }
}
