<?php

namespace routes;

use classes\authentication;
use classes\response;
use classes\router;
use economic_invoice_draft_mo;
use objects\departments_o;
use objects\economic_module_orders;
use objects\logs_o;
use objects\orders_o;
use objects\users_o;
use traits\route_t;

class economicInvoiceRoute
{
    use route_t;

    public function run(): void
    {
        global /** @var response $response */
        /** @var router $router */
        $router, $response;


        $this->post('/economic/invoice/draft/export', function () {
            global $response;
            $this->requirePermission('economic_invoice_draft_export');
            $user = (new authentication())->get_user();
            if ($user) {
                $order_id = $response->getRequestParameter('order_id');
                if (!isset($order_id)) {
                    $response->error('Order ID is required', 400);
                }
                // Validate the order ID is a number
                if (!is_numeric($order_id)) {
                    $response->error('Order ID must be a number', 400);
                }
                $order = (new orders_o())->getOrderById($order_id);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 404);
                }
                // Get the order items
                $order_items = (new orders_o())->getOrderItems($order_id);
                // Apply the department pricing
                $order_items = (new orders_o())->applyDepartmentPrices($order_items, $order->department_id->value());
                // Get the customer
                $customer = (new orders_o())->getCustomerByOrderId($order_id);
                // Check if the customer exists
                if (!$customer->exists()) {
                    $response->error('Customer not found', 404);
                }
                // Get the customer economic number
                $customer_economic = $customer->getCustomerEcocomicData()->economic_customer;
                $economic_invoice_draft = (new economic_invoice_draft_mo());
                // Set the customer number
                $economic_invoice_draft->setCustomerNumber((int)$customer_economic->customer_number);
                // Set the recipient
                $economic_invoice_draft->setRecipient(
                    $customer_economic->name ?? 'Ukendt',
                    $customer_economic->address ?? 'Ukendt',
                    $customer_economic->zip ?? 'Ukendt',
                    $customer_economic->city ?? 'Ukendt'
                );
                // Make sure there are order items
                if (count($order_items) === 0) {
                    $response->error('No order items found', 404);
                }
                // Get the department
                $department = (new departments_o())->getDepartmentById($order->department_id->value());
                // Add the department, date, reference
                $this->addTheDepartmentDateReference($economic_invoice_draft, $department['name'], $order);

                // Add the lines to the invoice
                foreach ( $order_items as $order_item ) {
                    // Add the order item to the invoice draft
                    $this->addOrderItemToInvoice(
                        $customer,
                        $order,
                        $order_item,
                        $economic_invoice_draft,
                        $order_item['quantity'] ?? 1);
                }
                // Check if the user has an open invoice draft
                $hasOpenInvoiceDraft = $customer->hasOpenInvoiceDraft();
                if ($hasOpenInvoiceDraft && !$customer->invoicePerOrder()) {
                    // Get the open invoice draft
                    $openInvoiceDraft = $customer->getOpenInvoiceDraft();
                    // Add the order to the invoice draft
                    $result = $this->addOrderToInvoiceDraft($openInvoiceDraft, $order, $customer, $order_items);
                }
                // If the customer doesn't want to be billed per order, or if there's no open invoice draft, we'll create a new one
                // Create the invoice draft
                if (!isset($result)) {
                    $result = $economic_invoice_draft->createInvoiceDraftExample();
                }
                // Check if the invoice draft was created, or if the lines were added (lines is an array, and should not be empty)
                if (!isset($result->draftInvoiceNumber) && !isset($result->lines[0])) {
                    // Log the error
                    (new logs_o())->add('economic_invoice_draft', 'global', 3, $user->id, 'ECONOMIC_INVOICE_DRAFT_EXPORT', 'Failed to create economic invoice draft');
                    // Check if we can get the errors from the response
                    if (isset($result->errors)) {
                        $response->add_meta('economic_errors', $result->errors);
                    }
                    $response->add_meta('economic_result', $result);
                    // Try to parse the error message
                    $response->error($result->message ?? 'Failed to create economic invoice draft', 500);
                }
                // Add the economic invoice draft to the order
                $economic_module_orders = (new economic_module_orders())->getByOrderId($order_id);
                // Remove the existing invoice draft (if any)
                if ($economic_module_orders->economic_invoice_draft_id->value() > 0) {
                    $economic_invoice_draft->deleteInvoiceDraft($economic_module_orders->economic_invoice_draft_id->value());
                }
                $economic_module_orders->economic_invoice_draft_id->set($result->draftInvoiceNumber ?? $customer->getOpenInvoiceDraft());
                if (!$customer->invoicePerOrder()) {
                    // If the customer wants to be billed per order, we'll add the order to the invoice draft
                    $customer->setOpenInvoiceDraft($result->draftInvoiceNumber ?? $customer->getOpenInvoiceDraft());
                } else {
                    // If the customer doesn't want to be billed per order, we'll remove the open invoice draft
                    $customer->unsetOpenInvoiceDraft();
                }
                // Return the response
                (new logs_o())->add('economic_invoice_draft', 'global', 1, $user->id, 'ECONOMIC_INVOICE_DRAFT_EXPORT', 'Successfully exported an economic invoice draft');
                $response->success($economic_module_orders->getArray());
            } else {
                (new logs_o())->add('economic_invoice_draft', 'global', 1, 0, 'ECONOMIC_INVOICE_DRAFT_EXPORT', 'No user found, or invalid session');
                $response->error('Invalid session', 400);
            }
        }, [
            'economic_invoice_draft_export' => 'Export an economic invoice draft'
        ]);

        $this->delete('/economic/invoice/draft/delete', function () {

            global /** @var response $response */
            $response;
            $this->requirePermission('economic_invoice_draft_delete');
            $user = (new authentication())->get_user();
            if ($user) {
                $order_id = $response->getRequestParameter('order_id');
                if (!isset($order_id)) {
                    $response->error('Order ID is required', 400);
                }
                $order = (new orders_o())->getOrderById($order_id);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 404);
                }
                // Make sure the order has an economic invoice draft
                $economic_module_orders = (new economic_module_orders())->getByOrderId($order_id);
                if ($economic_module_orders->economic_invoice_draft_id->value() === 0) {
                    $response->error('No economic invoice draft found', 404);
                }
                $economic_invoice_draft = (new economic_invoice_draft_mo());
                // Get the invoice draft number
                $invoiceDraftId = $economic_module_orders->economic_invoice_draft_id->value();
                $customer = (new orders_o())->getCustomerByOrderId($order_id);
                if (!$customer->invoicePerOrder()) {
                    // Check if the customer has an open invoice draft
                    if ($customer->hasOpenInvoiceDraft()) {
                        // Check if it's the same as the order's invoice draft
                        if ((int)$customer->getOpenInvoiceDraft() === (int)$invoiceDraftId) {
                            // Remove the open invoice draft from the customer
                            $customer->deleteOpenInvoiceDraft();
                        }
                    }
                }
                // Delete the invoice draft
                $economic_invoice_draft->deleteInvoiceDraft($invoiceDraftId);
                // Remove the economic invoice draft from the order
                $economic_module_orders->economic_invoice_draft_id->set(null);
                // Return the response
                (new logs_o())->add('economic_invoice_draft', 'global', 1, $user->id, 'ECONOMIC_INVOICE_DRAFT_DELETE', 'Successfully deleted an economic invoice draft');
                $response->success($economic_module_orders->getArray());
            } else {
                (new logs_o())->add('economic_invoice_draft', 'global', 1, 0, 'ECONOMIC_INVOICE_DRAFT_DELETE', 'No user found, or invalid session');
                $response->error('Invalid session', 400);
            }
        }, [
            'economic_invoice_draft_delete' => 'Delete an economic invoice draft'
        ]);

        $this->post('/economic/invoice/export', function () {
            global $response;
            $this->requirePermission('economic_invoice_export');
            $user = (new authentication())->get_user();
            if ($user) {
                $order_id = $response->getRequestParameter('order_id');
                if (!isset($order_id)) {
                    $response->error('Order ID is required', 400);
                }
                $order = (new orders_o())->getOrderById($order_id);
                // Check if the order exists
                if (!$order->exists()) {
                    $response->error('Order not found', 404);
                }
                // Get the customer
                $customer = (new orders_o())->getCustomerByOrderId($order_id);
                // Check if the customer exists
                if (!$customer->exists()) {
                    $response->error('Customer not found', 404);
                }
                // Make sure the order has an economic invoice draft
                $economic_module_orders = (new economic_module_orders())->getByOrderId($order_id);
                if ($economic_module_orders->economic_invoice_draft_id->value() === 0) {
                    $response->error('No economic invoice draft found', 404);
                }
                $economic_invoice_draft = (new economic_invoice_draft_mo());
                // Get the invoice draft number
                $invoiceDraftId = $economic_module_orders->economic_invoice_draft_id->value();
                // Make sure there's not already an invoice created
                $invoiceId = $economic_module_orders->economic_invoice_id->value();
                if ($invoiceId > 0) {
                    $response->error('An invoice has already been created, invoice ID: ' . $invoiceId, 400);
                }
                // Publish the invoice draft
                $result = $economic_invoice_draft->publishInvoiceDraft((int)$invoiceDraftId);
                // Check if the invoice was created
                if (!isset($result->bookedInvoiceNumber)) {
                    // Log the error
                    (new logs_o())->add('economic_invoice', 'global', 3, $user->id, 'ECONOMIC_INVOICE_EXPORT', 'Failed to create economic invoice from draft: ' . $invoiceDraftId);
                    // Check if we can get the errors from the response
                    if (isset($result->errors)) {
                        $response->add_meta('economic_errors', $result->errors);
                    }
                    // Try to parse the error message
                    $response->error($result->message ?? 'Failed to create economic invoice', 500);
                }
                // Add the economic invoice to the order
                $economic_module_orders = (new economic_module_orders())->getByOrderId($order_id);
                $economic_module_orders->economic_invoice_id->set($result->bookedInvoiceNumber);
                // Remove the economic invoice draft from the customer
                $customer->unsetOpenInvoiceDraft();
                // Return the response
                (new logs_o())->add('economic_invoice', 'global', 1, $user->id, 'ECONOMIC_INVOICE_EXPORT', 'Successfully exported an economic invoice');
                $response->success($economic_module_orders->getArray());
            } else {
                (new logs_o())->add('economic_invoice', 'global', 1, 0, 'ECONOMIC_INVOICE_EXPORT', 'No user found, or invalid session');
                $response->error('Invalid session', 400);
            }
        }, [
            'economic_invoice_export' => 'Export an economic invoice'
        ]);
    }

    /**
     * @param economic_invoice_draft_mo $economic_invoice_draft
     * @param $department_name
     * @param orders_o $order
     * @return void
     */
    function addTheDepartmentDateReference(economic_invoice_draft_mo $economic_invoice_draft, $department_name, orders_o $order): void
    {
        // The format is:
        // Truck Wash - [department name], [date], (?)Ref(erence): [reference], Reg 1: [reg_1], (?)Reg 2: [reg_2], (?)Reg 3: [reg_3]
        // (?)[note]
        // Example:
        // 12-02-2025 13:27, Truck Wash - Administration, Ref: 123456, Reg 1: ABC123, Reg 2: DEF456, Reg 3: GHI789
        // This is a note for the invoice
        //
        // (?) = Optional
        $parsed_date = date('d/m/Y H:i', strtotime($order->created_at->value()));
        $economic_invoice_draft->addLineTEXT("[ " . $parsed_date . ' ' . $department_name . ' #' . $order->id . " ]");
        // If there's a reference, add it to the invoice
        if ($order->reference->value() !== '') {
            $economic_invoice_draft->addLineTEXT('Reference:');
            // Add "# " in the beginning of each line (If there's more than one line, otherwise we just add the prefix once)
            if (str_contains($order->reference->value(), "\n")) {
                foreach ( explode("\n", $order->reference->value()) as $line ) {
                    $economic_invoice_draft->addLineTEXT('# ' . $line);
                }
            } else {
                $economic_invoice_draft->addLineTEXT('# ' . $order->reference->value());
            }
        }
        // Add the registration numbers (if any)
        $line_reg = '';
        if ($order->reg_1->value() !== '') {
            $line_reg .= 'Reg 1: ' . strtoupper($order->reg_1->value());
        }
        if ($order->reg_2->value() !== '')
            $line_reg .= ', Reg 2: ' . strtoupper($order->reg_2->value());
        if ($order->reg_3->value() !== '')
            $line_reg .= ', Reg 3: ' . strtoupper($order->reg_3->value());
        // Add the line to the invoice
        $economic_invoice_draft->addLineTEXT($line_reg);
        // If there's a note, add it to the invoice
        if ($order->notes->value() !== '') {
            $economic_invoice_draft->addLineTEXT('Notat:');
            // Add "# " in the beginning of each line (If there's more than one line, otherwise we just add the prefix once)
            if (str_contains($order->notes->value(), "\n")) {
                foreach ( explode("\n", $order->notes->value()) as $line ) {
                    $economic_invoice_draft->addLineTEXT('# ' . $line);
                }
            } else {
                $economic_invoice_draft->addLineTEXT('# ' . $order->notes->value());
            }
        }
    }

    /**
     * @param users_o $customer
     * @param orders_o $order
     * @param mixed $order_item
     * @param economic_invoice_draft_mo $economic_invoice_draft
     * @param int $quantity
     * @return void
     * @throws \Exception
     */
    function addOrderItemToInvoice(users_o $customer, orders_o $order, mixed $order_item, economic_invoice_draft_mo $economic_invoice_draft, int $quantity = 1): void
    {
        // The format is:
        // [product name], (?)Ref(erence): [reference], (!?)Reg 1: [reg 1], (!?)Reg 2: [reg 2], (!?)Reg 3: [reg 3], (?)Note: [note], (?)Discount: ([order item price] - [product price]) DKK ([discount percentage] %)
        // (?) = Optional
        // (!) = Required if the customer requires it
        // (!?) = Should be added if the customer requires it
        // Example:
        // Tankcleaning 4 spulehoveder, Ref: 123456, Reg 1: ABC123, Reg 2: DEF456, Reg 3: GHI789, Note: This is a note, Discount: -100 DKK (20%)
        // Get the department
        $department = $order->getDepartmentByOrderId($order->id);
        $economic_department_id = $department['economic_department_id'];
        $economic_dimension_id = $department['economic_dimension_id'] ?? 0;

        // Add the line to the invoice
        $economic_invoice_draft->addLine(
            (string)$order_item['product']['economic_product_id'],
            (string)$order_item['product']['name'],
            (int)$quantity,
            (int)$order_item['price'],
            0, // Since we can't be specific about the discount, we set it to 0. (The API has a limit of 2 decimals, and that's not enough for our needs)
            (int)$economic_department_id ?? 0,
            (int)$economic_dimension_id ?? 0 // If the department is not set, we'll set it to 0
        );
        // Calculate the discount percentage
        $discountPercentage = round((($order_item['product']['price'] - $order_item['price']) / $order_item['product']['price']) * 100, 0);

        // If the price is different from the product price, add it to the line
        if ($order_item['price'] !== $order_item['product']['price'])
            $economic_invoice_draft->addLineTEXT('Rabat: ' . ($order_item['price'] - $order_item['product']['price']) . ' DKK (' . $discountPercentage . '%)');

        // If there's a reference, add it to the line
        if ($order_item['reference'] !== '') {
            $economic_invoice_draft->addLineTEXT('Reference:');
            // Add "# " in the beginning of each line (If there's more than one line, otherwise we just add the prefix once)
            if (str_contains($order_item['reference'], "\n")) {
                foreach ( explode("\n", $order_item['reference']) as $line ) {
                    $economic_invoice_draft->addLineTEXT('# ' . $line);
                }
            } else {
                $economic_invoice_draft->addLineTEXT('# ' . $order_item['reference']);
            }
        }

        // Add the registration numbers (if they exist, and the customer requires it)
        if ($customer->doesUserHaveAttribute('requiresRegistrationNumbersInvoice')) {
            $line_reg = '';
            if ($order->reg_1->value() !== '') {
                $line_reg .= 'Reg 1: ' . strtoupper($order->reg_1->value());
            }
            if ($order->reg_2->value() !== '') {
                $line_reg .= ', Reg 2: ' . strtoupper($order->reg_2->value());
            }
            if ($order->reg_3->value() !== '') {
                $line_reg .= ', Reg 3: ' . strtoupper($order->reg_3->value());
            }
            $economic_invoice_draft->addLineTEXT($line_reg);
        }

        // If there's a note, add it to the line
        if ($order_item['notes'] !== '') {
            $economic_invoice_draft->addLineTEXT('Notat:');
            // Add "# " in the beginning of each line (If there's more than one line, otherwise we just add the prefix once)
            if (str_contains($order_item['notes'], "\n")) {
                foreach ( explode("\n", $order_item['notes']) as $line ) {
                    $economic_invoice_draft->addLineTEXT('# ' . $line);
                }
            } else {
                $economic_invoice_draft->addLineTEXT('# ' . $order_item['notes']);
            }
        }

    }

    function addOrderToInvoiceDraft(int $economic_invoice_draft_id, orders_o $order, users_o $customer, array $order_items): object
    {
        $economic_invoice_draft = (new economic_invoice_draft_mo());
        // Add two empty lines to the invoice, to separate the orders
        $economic_invoice_draft->addLineTEXT('');
        $economic_invoice_draft->addLineTEXT('');
        // Add the department, date, reference
        $this->addTheDepartmentDateReference($economic_invoice_draft, $order->getDepartmentByOrderId($order->id)['name'], $order);
        // Add the lines to the invoice
        foreach ( $order_items as $order_item ) {
            // Add the order item to the invoice draft
            $this->addOrderItemToInvoice($customer, $order, $order_item, $economic_invoice_draft, $order_item['quantity'] ?? 1);
        }
        return $economic_invoice_draft->addLinesToInvoiceDraft($economic_invoice_draft_id);
    }
}