<?php

namespace objects;

use classes\db;
use classes\object_property;
use traits\db_object_t;

class products_o extends db
{
    use db_object_t;

    /**
     * The name of the product
     * @var object_property
     */
    public object_property $name;
    /**
     * The description of the product
     * @var object_property
     */
    public object_property $description;
    /**
     * The price of the product
     * @var object_property
     */
    public object_property $price;
    /**
     * Whether the product is allowed for subscription
     * @var object_property $subscription_allowed
     */
    public object_property $subscription_allowed;
    /**
     * The category the product belongs to
     * @var object_property
     */
    public object_property $category;
    /**
     * The path to the piktogram image
     * @var object_property
     */
    public object_property $piktogram;
    /**
     * The id of the product in the economic system
     * @var object_property
     */
    public object_property $economic_product_id;
    /**
     * Whether the category discount should be applied to this product
     * @var object_property
     */
    public object_property $apply_category_discount;
    /**
     * Whether the product requires a note
     * @var object_property
     */
    public object_property $requires_note;
    /**
     * Whether the product is a wash, and should be included in wash statistics
     * @var object_property $is_wash
     */
    public object_property $is_wash;
    /**
     * Should the item be displayed in the booking form?
     * @var object_property $display_in_booking_form
     */
    public object_property $display_in_booking_form;
    /**
     * Order priority integer (lower = higher priority)
     * @var object_property $order_priority
     */
    public object_property $order_priority;
    /**
     * The timestamp of when the object was created
     * @var object_property
     */
    public object_property $created_at;
    /**
     * The timestamp of when the object was last updated
     * @var object_property
     */
    public object_property $updated_at;

    public function structure(): void
    {
        $this->setTable('products');
    }

    public function objectChanged(): void
    {
        // TODO: Add cache invalidation
    }

    public function getProductById(int $id): products_o
    {
        global $db;
        // Get the record from the database
        $sql = "SELECT * FROM $this->table WHERE id = $id";
        $result = $db->query($sql);
        if ($result->num_rows > 0) {
            $this->id = $id;
            $this->getObjectProperties();
        }
        return $this;
    }

    public function getObjectProperties(): void
    {
        $this->name = new object_property($this->table, $this->id, 'name', 'string', true);
        $this->description = new object_property($this->table, $this->id, 'description', 'string', false);
        $this->price = new object_property($this->table, $this->id, 'price', 'int', true);
        $this->subscription_allowed = new object_property($this->table, $this->id, 'subscription_allowed', 'bool', false);
        $this->category = new object_property($this->table, $this->id, 'category', 'string', true);
        $this->piktogram = new object_property($this->table, $this->id, 'piktogram', 'string', false);
        $this->economic_product_id = new object_property($this->table, $this->id, 'economic_product_id', 'int', false);
        $this->apply_category_discount = new object_property($this->table, $this->id, 'apply_category_discount', 'bool', false);
        $this->requires_note = new object_property($this->table, $this->id, 'requires_note', 'bool', false);
        $this->is_wash = new object_property($this->table, $this->id, 'is_wash', 'bool', false);
        $this->display_in_booking_form = new object_property($this->table, $this->id, 'display_in_booking_form', 'bool', false);
        $this->order_priority = new object_property($this->table, $this->id, 'order_priority', 'int', false);
        $this->created_at = new object_property($this->table, $this->id, 'created_at', 'string', false);
        $this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'string', false);
    }

    public function add(string $name, string $description, int $price, string|bool $category = false, string|bool $piktogram = false, string|bool $economicProductId = false): void
    {
        global $db, $response;
        try {
            // Avoid SQL injection
            $name = $db->escape_string($name);
            $description = $db->escape_string($description);
            $category = $category ? $db->escape_string($category) : null;
            $piktogram = $piktogram ? $db->escape_string($piktogram) : '';
            $economicProductId = $economicProductId ? $db->escape_string($economicProductId) : null;
            // Create a new record in the database
            $sql = "INSERT INTO $this->table (name, description, price, category, piktogram, economic_product_id) VALUES ('$name', '$description', $price, '$category', '$piktogram', '$economicProductId')";
            $db->query($sql);

            // Get the id of the new record
            $this->id = $db->insert_id();

            // Set the values of the object properties
            $this->getObjectProperties();
        } catch (\Exception $e) {
            $response->error($e->getMessage());
        }
    }


    /**
     * Edit a product
     * @param mixed $id
     * @param mixed $name
     * @param mixed $description
     * @param mixed $price
     * @param mixed $category
     * @param mixed $piktogram
     * @param mixed $economicProductId
     * @param bool $applyCategoryDiscount
     * @return void
     */
    public function edit(mixed $id, mixed $name, mixed $description, mixed $price, mixed $category = false, mixed $piktogram = false, mixed $economicProductId = false, bool $applyCategoryDiscount = true): void
    {
        global $db, $response;
        $this->id = $id;
        try {
            // Avoid SQL injection
            $name = $db->escape_string($name);
            $description = $db->escape_string($description);
            $category = $category ? $db->escape_string($category) : null;
            $piktogram = $piktogram ? $db->escape_string($piktogram) : '';
            $economicProductId = $economicProductId ? $db->escape_string($economicProductId) : null;
            // Update the record in the database
            $sql = "UPDATE $this->table SET name = '$name', description = '$description', price = $price, category = '$category', piktogram = '$piktogram', economic_product_id = '$economicProductId', apply_category_discount = " . (int)$applyCategoryDiscount . " WHERE id = $id";
            $db->query($sql);

            // Set the values of the object properties
            $this->getObjectProperties();
        } catch (\Exception $e) {
            $response->error($e->getMessage());
        }
    }

    public function listObjectsByCategory(string $category, $parseFunction = null): array
    {
        global $db;
        $category = $db->escape_string($category);
        $sql = "SELECT * FROM $this->table WHERE category = '$category'";
        $result = $db->query($sql);
        $objects = $db->fetch_all($result);
        if ($parseFunction) {
            $objects = array_map($parseFunction, $objects);
        }
        return $objects;
    }

    public function asArray(): array
    {
        return [
            'id' => (int)$this->id,
            'name' => (string)$this->name->value(),
            'description' => (string)$this->description->value(),
            'price' => (int)$this->price->value(),
            'subscription_allowed' => (int)$this->subscription_allowed->value(),
            'category' => $this->category->value(),
            'piktogram' => $this->piktogram->value(),
            'economic_product_id' => $this->economic_product_id->value(),
            'apply_category_discount' => (bool)$this->apply_category_discount->value(),
            'requires_note' => (bool)$this->requires_note->value(),
            'is_wash' => (bool)$this->is_wash->value(),
            'display_in_booking_form' => (bool)$this->display_in_booking_form->value(),
            'order_priority' => (int)$this->order_priority->value(),
            'created_at' => (string)$this->created_at->value(),
            'updated_at' => (string)$this->updated_at->value(),
        ];
    }

    /**
     * Apply department pricing to a list of products
     * @param array $products
     * @param int $department_id
     * @return array
     */
    public function applyDepartmentPricing(array $products, int $department_id): array
    {
        global $db;
        $department_id = $db->escape_string($department_id);
        $sql = "SELECT * FROM product_department_prices WHERE department_id = $department_id";
        $result = $db->query($sql);
        $prices = $db->fetch_all($result);
        foreach ( $products as $key => $product ) {
            foreach ( $prices as $price ) {
                if ((int)$product['id'] === (int)$price['product_id']) {
                    $products[$key]['price'] = $price['price'];
                }
            }
        }
        return $products;
    }

    public function getDepartmentPrice(int $department_id): int
    {
        global $db;
        $department_id = $db->escape_string($department_id);
        $sql = "SELECT * FROM product_department_prices WHERE department_id = $department_id AND product_id = $this->id";
        $result = $db->query($sql);
        $prices = $db->fetch_all($result);
        // Check if the product has a department price
        if (count($prices) > 0) {
            return $prices[0]['price'];
        }
        // Return the default price
        return $this->price->value();
    }

    public function applyCustomerDiscounts(array $products, users_o $customer): array
    {
        global $db;
        // Get the customer's discounts
        return array_map(fn($product) => $this->applyCustomerDiscount($product, $customer), $products);
    }
    public function applyCustomerDiscount(array $product, users_o $customer): array
    {
        global $db;
        // Validate input
        if (!isset($product['id']) || !isset($product['price'])) {
            throw new \InvalidArgumentException('Invalid product array, must contain id and price keys');
        }
        // Get the customer's discount percentage
        $discount_percentage = $customer->getProductDiscountPercentage($product['id']);
        // Apply the discount to the product price
        if ($discount_percentage > 0) {
            $product['price'] = (int)(round($product['price'] * (1 - ($discount_percentage / 100))));
        }
        return $product;
    }

    public function getSubscriptionMonthlyPrice(): int
    {
        // Subscription price (for 2 washes per month) is 1.2 times the normal price
        return (int)round($this->price->value() * 1.2);
    }

    public function isWashCertificate(): bool
    {
        self::requireSelected();
        return $this->id === 41;
    }
}