<?php
namespace Domain\Product;

/**
 * Repository odpowiedzialny za dostęp do danych produktów
 *
 * Zgodnie z wzorcem Repository Pattern, ta klasa enkapsuluje
 * logikę dostępu do bazy danych dla produktów.
 */
class ProductRepository
{
    private const MAX_PER_PAGE = 100;

    /**
     * @var \medoo Instancja Medoo ORM
     */
    private $db;

    /**
     * Konstruktor - przyjmuje instancję bazy danych
     *
     * @param \medoo $db Instancja Medoo ORM
     */
    public function __construct($db)
    {
        $this->db = $db;
    }

    /**
     * Pobiera stan magazynowy produktu
     *
     * @param int $productId ID produktu
     * @return int|null Ilość produktu lub null jeśli nie znaleziono
     */
    public function getQuantity(int $productId): ?int
    {
        $quantity = $this->db->get('pp_shop_products', 'quantity', ['id' => $productId]);

        // Medoo zwraca false jeśli nie znaleziono
        return $quantity !== false ? (int)$quantity : null;
    }

    /**
     * Pobiera produkt po ID
     *
     * @param int $productId ID produktu
     * @return array|null Dane produktu lub null
     */
    public function find(int $productId): ?array
    {
        $product = $this->db->get('pp_shop_products', '*', ['id' => $productId]);
        return $product ?: null;
    }

    /**
     * Zwraca liste produktow z archiwum do panelu admin.
     *
     * @return array{items: array<int, array<string, mixed>>, total: int}
     */
    public function listArchivedForAdmin(
        array $filters,
        string $sortColumn = 'id',
        string $sortDir = 'DESC',
        int $page = 1,
        int $perPage = 10
    ): array {
        $allowedSortColumns = [
            'id' => 'psp.id',
            'name' => 'name',
            'price_brutto' => 'psp.price_brutto',
            'price_brutto_promo' => 'psp.price_brutto_promo',
            'quantity' => 'psp.quantity',
            'combinations' => 'combinations',
        ];

        $sortSql = $allowedSortColumns[$sortColumn] ?? 'psp.id';
        $sortDir = strtoupper(trim($sortDir)) === 'ASC' ? 'ASC' : 'DESC';
        $page = max(1, $page);
        $perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
        $offset = ($page - 1) * $perPage;

        $where = ['psp.archive = 1', 'psp.parent_id IS NULL'];
        $params = [];

        $phrase = trim((string)($filters['phrase'] ?? ''));
        if (strlen($phrase) > 255) {
            $phrase = substr($phrase, 0, 255);
        }

        if ($phrase !== '') {
            $where[] = '(
                psp.ean LIKE :phrase
                OR psp.sku LIKE :phrase
                OR EXISTS (
                    SELECT 1
                    FROM pp_shop_products_langs AS pspl2
                    WHERE pspl2.product_id = psp.id
                      AND pspl2.name LIKE :phrase
                )
            )';
            $params[':phrase'] = '%' . $phrase . '%';
        }

        $whereSql = implode(' AND ', $where);

        $sqlCount = "
            SELECT COUNT(0)
            FROM pp_shop_products AS psp
            WHERE {$whereSql}
        ";

        $stmtCount = $this->db->query($sqlCount, $params);
        $countRows = $stmtCount ? $stmtCount->fetchAll() : [];
        $total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;

        $sql = "
            SELECT
                psp.id,
                psp.price_brutto,
                psp.price_brutto_promo,
                psp.quantity,
                psp.sku,
                psp.ean,
                (
                    SELECT pspl.name
                    FROM pp_shop_products_langs AS pspl
                    INNER JOIN pp_langs AS pl ON pl.id = pspl.lang_id
                    WHERE pspl.product_id = psp.id
                      AND pspl.name <> ''
                    ORDER BY pl.o ASC
                    LIMIT 1
                ) AS name,
                (
                    SELECT pspi.src
                    FROM pp_shop_products_images AS pspi
                    WHERE pspi.product_id = psp.id
                    ORDER BY pspi.o ASC, pspi.id ASC
                    LIMIT 1
                ) AS image_src,
                (
                    SELECT pspi.alt
                    FROM pp_shop_products_images AS pspi
                    WHERE pspi.product_id = psp.id
                    ORDER BY pspi.o ASC, pspi.id ASC
                    LIMIT 1
                ) AS image_alt,
                (
                    SELECT COUNT(0)
                    FROM pp_shop_products AS pspc
                    WHERE pspc.parent_id = psp.id
                ) AS combinations
            FROM pp_shop_products AS psp
            WHERE {$whereSql}
            ORDER BY {$sortSql} {$sortDir}, psp.id {$sortDir}
            LIMIT {$perPage} OFFSET {$offset}
        ";

        $stmt = $this->db->query($sql, $params);
        $items = $stmt ? $stmt->fetchAll() : [];

        return [
            'items' => is_array($items) ? $items : [],
            'total' => $total,
        ];
    }

    /**
     * Pobiera cenę produktu (promocyjną jeśli jest niższa, w przeciwnym razie regularną)
     *
     * @param int $productId ID produktu
     * @return float|null Cena brutto lub null jeśli nie znaleziono
     */
    public function getPrice(int $productId): ?float
    {
        $prices = $this->db->get('pp_shop_products', ['price_brutto', 'price_brutto_promo'], ['id' => $productId]);

        if (!$prices) {
            return null;
        }

        if ($prices['price_brutto_promo'] != '' && $prices['price_brutto_promo'] < $prices['price_brutto']) {
            return (float)$prices['price_brutto_promo'];
        }

        return (float)$prices['price_brutto'];
    }

    /**
     * Pobiera nazwę produktu w danym języku
     *
     * @param int $productId ID produktu
     * @param string $langId ID języka
     * @return string|null Nazwa produktu lub null jeśli nie znaleziono
     */
    public function getName(int $productId, string $langId): ?string
    {
        $name = $this->db->get('pp_shop_products_langs', 'name', ['AND' => ['product_id' => $productId, 'lang_id' => $langId]]);

        return $name ?: null;
    }

    /**
     * Aktualizuje ilość produktu
     *
     * @param int $productId ID produktu
     * @param int $quantity Nowa ilość
     * @return bool Czy aktualizacja się powiodła
     */
    public function updateQuantity(int $productId, int $quantity): bool
    {
        $result = $this->db->update(
            'pp_shop_products',
            ['quantity' => $quantity],
            ['id' => $productId]
        );

        return $result !== false;
    }

    /**
     * Przywraca produkt z archiwum (wraz z kombinacjami)
     *
     * @param int $productId ID produktu
     * @return bool Czy operacja się powiodła
     */
    public function unarchive(int $productId): bool
    {
        $this->db->update( 'pp_shop_products', [ 'status' => 1, 'archive' => 0 ], [ 'id' => $productId ] );
        $this->db->update( 'pp_shop_products', [ 'status' => 1, 'archive' => 0 ], [ 'parent_id' => $productId ] );

        return true;
    }

    /**
     * Przenosi produkt do archiwum (wraz z kombinacjami)
     *
     * @param int $productId ID produktu
     * @return bool Czy operacja się powiodła
     */
    public function archive(int $productId): bool
    {
        $this->db->update( 'pp_shop_products', [ 'status' => 0, 'archive' => 1 ], [ 'id' => $productId ] );
        $this->db->update( 'pp_shop_products', [ 'status' => 0, 'archive' => 1 ], [ 'parent_id' => $productId ] );

        return true;
    }
}
