<?php

namespace objects;

use classes\db;
use classes\object_property;
use classes\slack;
use classes\stripe;
use Exception;
use traits\db_object_t;

class departments_o extends db
{
    use db_object_t;

    public object_property $name;
    public object_property $description;
    public object_property $economic_department_id; // The id of the department in the economic system (Can be null)
    public object_property $slack_webhook; // The slack webhook for the department
    public department_variables_o $variables; // The department variables object
    public object_property $dimension; // The dimension of the department
    public object_property $visible; // The visibility of the department
    public object_property $branding; // The branding of the department
    public object_property $longitude; // The longitude of the department (Can be null)
    public object_property $latitude; // The latitude of the department (Can be null)
    public object_property $order_priority; // The order priority of the department (Lower number = higher priority)
    public object_property $created_at;
    public object_property $updated_at;

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

    public function objectChanged(): void
    {
        // Clear the cache
        redis->clear_departments();
    }

    /**
     * Get the lanes associated with the department
     * @retuns department_lanes_o[] The lanes associated with the department
     * @throws Exception If the department is not selected
     */
    public function getLanes(): array
    {
        self::requireSelected();
        return (new department_lanes_o())->getDepartmentLanes($this->id);
    }

    /**
     * Add a department
     * @param string $name
     * @param string $description
     * @throws Exception If the object was not created successfully
     */
    public function create(string $name, string $description): void
    {
        global $db;
        // Avoid SQL injection
        $name = $db->escape_string($name);
        $description = $db->escape_string($description);
        // Create a new record in the database
        $sql = "INSERT INTO $this->table (name, description) VALUES ('$name', '$description')";
        $db->query($sql);

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

        // Set the values of the object properties
        $this->getObjectProperties();

        // Clear the cache
        redis->clear_departments();
    }

    /**
     * Get the object properties of the department
     * @throws Exception If the object is not selected
     */
    public function getObjectProperties(): void
    {
        self::requireSelected();
        $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->economic_department_id = new object_property($this->table, $this->id, 'economic_department_id', 'int', false);
        $this->slack_webhook = new object_property($this->table, $this->id, 'slack_webhook', 'string', false);
        $this->variables = (new department_variables_o())->selectDepartment($this->id);
        $this->dimension = new object_property($this->table, $this->id, 'dimension', 'int', false);
        $this->branding = new object_property($this->table, $this->id, 'branding', 'int', false);
        $this->visible = new object_property($this->table, $this->id, 'visible', 'int', false);
        $this->longitude = new object_property($this->table, $this->id, 'longitude', 'float', false);
        $this->latitude = new object_property($this->table, $this->id, 'latitude', 'float', 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 edit(int $id, string $name, string $description, int $economic_department_id): void
    {
        global $db;
        $this->id = $id;
        // Avoid SQL injection
        $name = $db->escape_string($name);
        $description = $db->escape_string($description);
        // If the economic_department_id is 0, set it to null
        if ($economic_department_id === 0) {
            $economic_department_id = null;
        }
        // Update the record in the database
        $sql = "UPDATE $this->table SET name = '$name', description = '$description', economic_department_id = " . ($economic_department_id ?? 'NULL') . " WHERE id = $this->id";
        $db->query($sql);

        // Set the values of the object properties
        $this->getObjectProperties();

        // Clear the cache
        redis->clear_department($id);
    }

    public function list($superUser = false): array
    {
        global $db;
        // Check if the departments are cached
        $departments = redis->get_departments();
        // If the departments are not cached, get them from the database
        if ($departments === null) {
            $sql = "SELECT * FROM $this->table";
            $result = $db->query($sql);
            $departments = $db->fetch_all($result);
            // Cache the departments (If it is not empty or null)
            if (!empty($departments)) {
                redis->cache_departments($departments);
            }
        }
        // Only show the name, description and id if the user isn't a super user
        if (!$superUser) {
            // Map the departments to only show the name, description and id
            $departments = array_map(function ($department) {
                return [
                    'name' => $department['name'],
                    'description' => $department['description'],
                    'id' => $department['id'],
                    'visible' => $department['visible'],
                ];
            }, $departments);
        }
        return $departments;
    }

    public function getDepartmentById(int $id, bool $force_all = false): array
    {
        global $db;
        // Check if the department is cached
        $department = redis->get_department($id);
        // If the department is not cached, get it from the database
        if ($department === null || $force_all) {
            $sql = "SELECT * FROM $this->table WHERE id = $id";
            $result = $db->query($sql);
            $department = $db->fetch_assoc($result);
            // Cache the department (If it is not empty or null)
            if (!empty($department)) {
                redis->cache_department($id, $department);
            }
        }
        return $department;
    }

    /**
     * Get the price of a product in a department
     * @param int $department_id
     * @param int $product_id
     * @param int $price
     * @return void
     */
    public function setDepartmentProductPrice(int $department_id, int $product_id, int $price): void
    {
        global $db;
        // Check if the record already exists
        $this->removeDepartmentProductPriceIfExist($department_id, $product_id);
        // If the price is 0, return
        if ($price === 0) {
            return;
        }
        // Create a new record in the database
        $sql = "INSERT INTO product_department_prices (department_id, product_id, price) VALUES ($department_id, $product_id, $price)";
        $db->query($sql);
    }

    /**
     * Remove the price of a product in a department
     * @param int $department_id
     * @param int $product_id
     * @return void
     */
    private function removeDepartmentProductPriceIfExist(int $department_id, int $product_id): void
    {
        global $db;
        // Get the price from the database
        $sql = "SELECT * FROM product_department_prices WHERE department_id = $department_id AND product_id = $product_id";
        $result = $db->query($sql);

        if ($result->num_rows > 0) {
            // Remove the record from the database
            $sql = "DELETE FROM product_department_prices WHERE department_id = $department_id AND product_id = $product_id";
            $db->query($sql);
        }
    }

    public function getDepartmentProductPrices(int $department_id): array
    {
        global $db;
        $sql = "SELECT * FROM product_department_prices WHERE department_id = $department_id";
        $result = $db->query($sql);
        return $db->fetch_all($result);
    }

    public function getDepartmentName(int $department): string
    {
        global $db;
        // Check if the department name is cached
        $name = redis->get_department_name($department);
        // If the department name is not cached, get it from the database
        if ($name === null) {
            try {
                $this->id = $department;
                $this->getObjectProperties();
                $name = $this->name->value();
                // Cache the department name (If it is not empty or null)
                if (!empty($name)) {
                    redis->cache_department_name($department, $name);
                }
            } catch (Exception $e) {
                $name = 'Unable to get department name: ' . $department;
            }
            return $name;
        }
        return redis->get_department_name($department) ?? 'Unable to get department name: ' . $department;
    }

    /**
     * Get the Stripe terminal location department variable
     * @note This is a link to the department_variables_o::getVariable(stripe_terminal_location) method
     * @return string The Stripe terminal location id
     * @throws Exception If the department variable is not set
     * @throws Exception If the department is not selected
     */
    public function getStripeTerminalLocation(): string
    {
        self::requireSelected();
        if (!$this->variables->isSet('stripe_terminal_location')) {
            throw new Exception('The Stripe terminal location is not set for the department.');
        }
        return $this->variables->getVariable('stripe_terminal_location');
    }

    /**
     * Check if the Stripe terminal location variable is set
     * @note This is a link to the department_variables_o::isSet(stripe_terminal_location) method
     * @return bool true If the Stripe terminal location is set
     * @throws Exception If the department is not selected
     */
    public function isStripeTerminalLocationSet(): bool
    {
        self::requireSelected();
        return $this->variables->isSet('stripe_terminal_location');
    }

    /**
     * Set the Stripe terminal location department variable
     * @note This is a link to the department_variables_o::set(stripe_terminal_location) method
     * @param string $location The Stripe terminal location id
     * @throws Exception If the department is not selected
     * @throws Exception If the department variable is not updated successfully
     */
    public function setStripeTerminalLocation(string $location): void
    {
        self::requireSelected();
        $this->variables->set('stripe_terminal_location', $location);
    }

    /**
     * Get the Stripe terminal readers
     * @note This is a link to the stripe_endpoint_readers::listLocation method
     * @throws Exception If the department is not selected
     * @throws Exception If the Stripe terminal location is not set for the department
     */
    public function getStripeTerminalReaders(): \Stripe\Collection
    {
        self::requireSelected();
        if (!$this->variables->isSet('stripe_terminal_location')) {
            throw new Exception('The Stripe terminal location is not set for the department.');
        }
        return (new stripe())->readers->listLocation($this->variables->getVariable('stripe_terminal_location'));
    }

    /**
     * Convert the object to an array
     * @param array|null $options The options for the conversion (e.g. ['slack_webhook' => true])
     * @return array The object as an array
     * @throws Exception If the object is not selected
     */
    public function asArray(array $options = null): array
    {
        self::requireSelected();
        $tmp = [
            'id' => (int)$this->id,
            'name' => (string)$this->name->value(),
            'description' => (string)$this->description->value(),
            'economic_department_id' => (int)$this->economic_department_id->value(),
            'dimension' => (int)$this->dimension->value(),
            'branding' => (int)$this->branding->value(),
            'longitude' => (float)$this->longitude->value(),
            'latitude' => (float)$this->latitude->value(),
            'order_priority' => (int)$this->order_priority->value(),
            'created_at' => (string)$this->created_at->value(),
            'updated_at' => (string)$this->updated_at->value(),
        ];
        // Check if the webhook should be included
        if ($options && $options['slack_webhook']) {
            $tmp['slack_webhook'] = (string)$this->slack_webhook->value();
        }
        return $tmp;
    }

    /**
     * Check if the Stripe is configured for the department
     * @note This is a link to the department_variables_o::isSet(stripe_terminal_location) method
     * @return bool true If the Stripe is configured
     * @throws Exception If the department is not selected
     */
    public function isStripeConfigured(): bool
    {
        self::requireSelected();
        if ($this->variables->isSet('stripe_terminal_location')) {
            return true;
        }
        return false;
    }

    /**
     * @throws Exception If the department is not selected
     * @throws Exception If the branding is not set
     * @throws Exception If the branding is not found
     */
    public function getBranding(): branding_o
    {
        self::requireSelected();
        return (new branding_o())->select($this->branding->value());
    }

    /**
     * Get the phone numbers for the SMS notification
     * @note This is a link to the department_notification_sms_o::getDepartmentActivePhoneNumbers method
     * @throws Exception If the department is not selected
     */
    public function notificationSmsPhoneNumbers(): array
    {
        self::requireSelected();
        return (new department_notification_sms_o())->getDepartmentActivePhoneNumbers($this->id);
    }

    /**
     * @throws Exception
     */
    public function isModuleTimeBookingsEnabled(): bool
    {
        self::requireSelected();
        return (bool)$this->variables->getVariable('bookingsystem_time_based_enabled');
    }

    /**
     * Select a department by its name (case-sensitive, exact match)
     * @throws Exception If the department selection fails or the department is not found
     * @example "Hvidovre"
     */
    public function selectByName(string $departmentName): self
    {
        $result = self::getFieldsWhere([
            'name' => $departmentName,
        ], [
            'id',
        ]);
        if (empty($result)) {
            throw new Exception('Department not found: ' . $departmentName);
        }
        $this->selectId($result[0]['id']);
        return $this;
    }

    public function selectId(int $department_id): self
    {
        $this->id = $department_id;
        $this->getObjectProperties();
        return $this;
    }

    /**
     * @return products_o[] An array of all products in the department's categories
     * @throws Exception
     */
    public function getAllProductInDepartmentCategories(): array
    {
        self::requireSelected();

        // Get all the categories associated
        $department_categories = (new department_categories_o())->getDepartmentCategoryObjects($this->id);

        // Use array_reduce to combine both loops into a single operation
        $products = array_reduce($department_categories, function ($products, $department_category) {
            return array_merge($products, $department_category->getCategory()->getProducts());
        }, []);

        // Remove duplicate products
        return array_unique($products, SORT_REGULAR);
    }

    /**
     * If the department is excluded from invoicing
     * @return bool
     * @throws Exception If the department is not selected
     */
    public function isExcludedFromInvoicing(): bool
    {
        self::requireSelected();
        return $this->variables->getVariable('exclude_from_invoicing') === true;
    }



    /**
     * Send department period statistics to Slack
     * @param string $start_date (YYYY-MM-DD)
     * @param string $end_date (YYYY-MM-DD)
     * @return void
     * @throws Exception If the department is not selected
     */
    public function sendPeriodStatisticsToSlack(string $start_date, string $end_date): void
    {
        self::requireSelected();
        // Configuration
        $department_id = $this->id;
        $product_ids = [
            25,
            [23, 24], // Used to merge two products into one percentage (Spot Free)
            22,
            27,
            21,
            26
        ];
        $date_end = date('Y-m-d 23:59:59', strtotime($end_date)); // End date at 23:59:59
        $date_start = date('Y-m-d 00:00:00', strtotime($start_date)); // Start date at 00:00:00
        /**
         * Weekly results for Roskilde
         *
         * Period: 29.01.2026 - 04.02.2026
         * Washes: 67
         * Fælg Flex: 55%
         * Spot Free: 22%
         * Special Sæbe: 11%
         * 10 min ekstra: 15%
         * Undervognsskyl: 44%
         * Voks: 66%
         */
        $max_addons = [];
        $sold_addons = [];
        $percentages = [];
        $department = (new departments_o())->select((int)$this->id);
        $tmp = "*Weekly results for {$department->name->value()}*\n";
        $tmp .= "Period: " . date('d.m.Y', strtotime($date_start)) . " - " . date('d.m.Y', strtotime($date_end)) . "\n";
        // Washes
        $wash_count = (new orders_o())->countWashesInDateRange($date_start, $date_end, $department_id);
        $analytics = $this->analyzeAddonSalesData($product_ids, $date_start, $date_end, $department_id, $max_addons, $sold_addons, $percentages);
        $tmp .= "Washes: $wash_count\n" . implode('', $analytics);
        
        // Check if there's a custom webhook for the department
        $slack = new slack();
        if (empty($this->slack_webhook->value())) {
            // If no webhook is set for the department, use the default Slack notification
            $slack->send_message($tmp);
        } else {
            // Set the custom webhook for the department
            $slack->send_webhook_message($tmp, $this->slack_webhook->value());
        }
    }

    /**
     * @param array $product_ids
     * @param string $date_start
     * @param string $date_end
     * @param int|array $department_id
     * @param array $max_addons
     * @param array $sold_addons
     * @param array $percentages
     * @return array
     * @throws Exception
     */
    public function analyzeAddonSalesData(array $product_ids, string $date_start, string $date_end, int|array $department_id, array $max_addons, array $sold_addons, array $percentages): array
    {
        if (is_array($department_id)) {
            // Combine percentages from multiple departments
            $combined_percentages = [];
            foreach ( $department_id as $dept_id ) {
                $dept_percentages = $this->getThePercentageOfAddonsSoldOutOfMax($product_ids, $date_start, $date_end, $dept_id, $max_addons, $sold_addons, $percentages);
                foreach ( $dept_percentages as $key => $value ) {
                    if (!isset($combined_percentages[$key])) {
                        $combined_percentages[$key] = 0;
                    }
                    $combined_percentages[$key] += $value;
                }
            }
            $percentages = $combined_percentages;
        } else {
            // Single department
            $percentages = $this->getThePercentageOfAddonsSoldOutOfMax($product_ids, $date_start, $date_end, $department_id, $max_addons, $sold_addons, $percentages);
        }
        $tmp = [];
        foreach ( $product_ids as $product_id ) {
            if (is_array($product_id)) {
                // Merged product names
                $product_names = [];
                foreach ( $product_id as $pid ) {
                    $product_names[] = (new products_o())->select($pid)->name->value();
                }
                // Switch to joined names
                $product_name = match (true) {
                    in_array(23, $product_id) && in_array(24, $product_id) => 'Spot Free',
                    default => implode(' + ', $product_names),
                };
            } else {
                // Check single product name
                $product_name = match ($product_id) {
                    25 => 'Fælg Flex',
                    22 => 'Special Sæbe',
                    27 => '10 min ekstra',
                    21 => 'Undervognsskyl',
                    26 => 'Voks',
                    default => (new products_o())->select($product_id)->name->value(),
                };
            }
            $tmp[] = "{$product_name}: {$percentages[is_array($product_id) ? implode('_', $product_id) : $product_id]}%\n";
        }
        // Return the data
        return $tmp;
    }

    /**
     * @param array $product_ids
     * @param string $date_start
     * @param string $date_end
     * @param int $department_id
     * @param array $max_addons
     * @param array $sold_addons
     * @param array $percentages
     * @return array
     */
    public function getThePercentageOfAddonsSoldOutOfMax(array $product_ids, string $date_start, string $date_end, int $department_id, array &$max_addons, array &$sold_addons, array &$percentages): array
    {
        $product_options_o = new product_options_o();

        // Get the percentage of addons sold out of max
        foreach ( $product_ids as $product_id ) {
            // Handle merged products
            if (is_array($product_id)) {
                $addon_sold_count = 0;
                $addon_max_count = 0;
                foreach ( $product_id as $pid ) {
                    $pid = (int)$pid;
                    $addon_sold_count += $product_options_o->countSoldAddonsInDateRange($pid, $date_start, $date_end, $department_id);
                    $addon_max_count += $product_options_o->getMaxAddonsInDateRange($pid, $date_start, $date_end, $department_id);
                }
            } else {
                $pid = $product_id;
                $addon_sold_count = $product_options_o->countSoldAddonsInDateRange($pid, $date_start, $date_end, $department_id);
                $addon_max_count = $product_options_o->getMaxAddonsInDateRange($pid, $date_start, $date_end, $department_id);
            }
            // Prevent division by zero
            if ($addon_max_count === 0) {
                $addon_percentage_sold = 0;
            } else {
                $addon_percentage_sold = ($addon_sold_count / $addon_max_count) * 100;
            }
            $max_addons[is_array($product_id) ? implode('_', $product_id) : $product_id] = $addon_max_count;
            $sold_addons[is_array($product_id) ? implode('_', $product_id) : $product_id] = $addon_sold_count;
            $percentages[is_array($product_id) ? implode('_', $product_id) : $product_id] = number_format($addon_percentage_sold, 2);
        }
        return $percentages;
    }

    /**
     * Get the total amount of addons sold in a department in a date range
     * @param array $product_ids
     * @param string $date_start
     * @param string $date_end
     * @param int $department_id
     * @return float
     */
    public function getTotalPercentageAddonsSoldInDepartment(array $product_ids, string $date_start, string $date_end, int $department_id): float
    {
        $max_addons = [];
        $sold_addons = [];
        $percentages = [];

        $this->getThePercentageOfAddonsSoldOutOfMax($product_ids, $date_start, $date_end, $department_id, $max_addons, $sold_addons, $percentages);

        $total_sold = array_sum($sold_addons);
        $total_max = array_sum($max_addons);

        if ($total_max === 0) {
            return 0;
        }

        return ($total_sold / $total_max) * 100;
    }

    /**
     * Get the total maximum addons available in a department in a date range
     * @param array $product_ids
     * @param string $date_start
     * @param string $date_end
     * @param int $department_id
     * @return float
     */
    public function getTotalPercentageMaxAddonsInDepartment(array $product_ids, string $date_start, string $date_end, int $department_id): float
    {
        return $this->getTotalPercentageAddonsSoldInDepartment($product_ids, $date_start, $date_end, $department_id);
    }

    /**
     * Get the total amount of addons sold in a department in a date range
     * @param array $product_ids
     * @param string $date_start
     * @param string $date_end
     * @param int $department_id
     * @return int
     * @throws Exception
     */
    public function getTotalAddonsSoldInDepartment(array $product_ids, string $date_start, string $date_end, int $department_id): int
    {
        $total_addons_sold = 0;
        foreach ($product_ids as $product_id) {
            $addon_sold_count = (new product_options_o())->countSoldAddonsInDateRange($product_id, $date_start, $date_end, $department_id);
            $total_addons_sold += $addon_sold_count;
        }
        return $total_addons_sold;
    }
    /**
     * Get the total maximum addons available in a department in a date range
     * @param array $product_ids
     * @param string $date_start
     * @param string $date_end
     * @param int $department_id
     * @return int
     * @throws Exception
     */
    public function getTotalMaxAddonsInDepartment(array $product_ids, string $date_start, string $date_end, int $department_id): int
    {
        return self::methodCacheWithParameters(__METHOD__, func_get_args(), 120, function() use ($product_ids, $date_start, $date_end, $department_id) {
            $total_max_addons = 0;
            foreach ($product_ids as $product_id) {
                $addon_max_count = (new product_options_o())->getMaxAddonsInDateRange($product_id, $date_start, $date_end, $department_id);
                $total_max_addons += $addon_max_count;
            }
            return $total_max_addons;
        });
    }

    /**
     * Send a (truck wash internal) department statistic Slack notification
     * @param string $date_start (YYYY-MM-DD)
     * @param string $date_end (YYYY-MM-DD)
     * @param int[] $department_ids
     * @param int[] $product_ids
     * @return void
     * @throws Exception
     */
    public function sendSlackInternalStatisticNotification(string $date_start, string $date_end, array $department_ids, array $product_ids = [
        25,
        [23, 24], // Used to merge two products into one percentage (Spot Free)
        22,
        27,
        21,
        26
    ]): void
    {
        if (empty($department_ids)) {
            throw new Exception('No department ids provided for the Slack internal statistic notification.');
        }
        if (empty($product_ids)) {
            throw new Exception('No product ids provided for the Slack internal statistic notification.');
        }

        // Sort the departments by order_priority (lowest => first)
        $departments_data = $this->getFieldsWhereIn(['id' => $department_ids], ['id', 'order_priority']);
        usort($departments_data, function ($a, $b) {
            return (int)$a['order_priority'] <=> (int)$b['order_priority'];
        });
        $department_ids = array_column($departments_data, 'id');

        // Configuration
        $date_end = date('Y-m-d 23:59:59', strtotime($date_end)); // End date at 23:59:59
        $date_start = date('Y-m-d 00:00:00', strtotime($date_start)); // Start date at 00:00:00

        /**
         * Weekly results for Truck Wash
         *
         * Period: 29.01.2026 - 04.02.2026
         * Washes:
         *
         * Taastrup: 300
         * Hvidovre: 52
         * Glostrup: 121
         * Køge: 54
         * Roskilde: 65
         * Aarhus C: 32
         * Taulov: 122
         * - Total: 650
         * Undervognsskyl
         *
         * Taastrup: 55%
         * Hvidovre: 22%
         * Glostrup: 22%
         * Køge: 24%
         * Roskilde: 54%
         * Aarhus C: 66%
         * Taulov: 55%
         * - Total: 55%
         * ...
         */
        $tmp = "*Weekly results for Truck Wash*\n";
        $tmp .= "_Period: " . date('d.m.Y', strtotime($date_start)) . " - " . date('d.m.Y', strtotime($date_end)) . "_\n";
        // Washes
        $total_wash_count = 0;
        $tmp .= "*Washes:*\n";
        $orders_o = new orders_o();
        foreach ( $department_ids as $department_id ) {
            $wash_count = $orders_o->countWashesInDateRange($date_start, $date_end, $department_id);
            $total_wash_count += $wash_count;
            $department = (new departments_o())->select($department_id);
            $tmp .= "> {$department->name->value()}: $wash_count\n";
        }
        $tmp .= "> - Total: {$total_wash_count}\n";

        // Pre-instantiate products_o to reuse
        $products_o = new products_o();
        $max_addons = [];
        $sold_addons = [];
        $percentages = [];

        // Loop through each product and get the percentages for each department
        foreach ( $product_ids as $product_id ) {
            // Reset totals for each product
            $total_percentage = 0;
            $total_count = 0;
            $total_sold = 0;

            // Header for the product
            if (is_array($product_id)) {
                $product_names = [];
                foreach ( $product_id as $pid ) {
                    $products_o->select($pid);
                    $product_names[] = $products_o->name->value();
                }
                $product_name = match (true) {
                    in_array(23, $product_id) && in_array(24, $product_id) => 'Spot Free',
                    default => implode(' + ', $product_names),
                };
            } else {
                $product_name = match ($product_id) {
                    25 => 'Fælg Flex',
                    22 => 'Special Sæbe',
                    27 => '10 min ekstra',
                    21 => 'Undervognsskyl',
                    26 => 'Voks',
                    default => $products_o->select($product_id)->name->value(),
                };
            }
            $tmp .= "\n*{$product_name}:*\n";
            $product_key = is_array($product_id) ? implode('_', $product_id) : $product_id;

            // Loop through each department to get the percentage
            foreach ( $department_ids as $department_id ) {
                $department = (new departments_o())->select($department_id);
                $this->getThePercentageOfAddonsSoldOutOfMax([$product_id], $date_start, $date_end, $department_id, $max_addons, $sold_addons, $percentages);
                $percentage = $percentages[$product_key];
                $total_count += $max_addons[$product_key];
                $total_sold += $sold_addons[$product_key];
                $tmp .= "> {$department->name->value()}: {$percentage}%\n";
            }
            // Get the total percentage for the product
            if ($total_count !== 0) {
                // Prevent division by zero
                $total_percentage = ($total_sold / $total_count) * 100;
            }
            $tmp .= "> - Total: " . number_format($total_percentage, 2) . "%\n";
        }
        // Send the message to the internal Slack webhook
        $slack = new slack();
        $slack->send_webhook_message($tmp, (new departments_o())->select(10)->slack_webhook->value());
    }
}