<?php

namespace classes;
require_once WD . '/modules/economic/economic_c.php';
require_once WD . '/modules/economic/endpoints/economic_orders_endpoint.php';
require_once WD . '/modules/economic/endpoints/economic_invoices_endpoint.php';
require_once WD . '/modules/economic/endpoints/economic_departments_endpoint.php';
require_once WD . '/modules/economic/endpoints/economic_layouts_endpoint.php';
require_once WD . '/modules/economic/endpoints/economic_payment_terms_endpoint.php';
require_once WD . '/modules/economic/endpoints/economic_customers_endpoint.php';
require_once WD . '/modules/economic/endpoints/economic_products_endpoint.php';
require_once WD . '/modules/economic/economic_helpers.php';


use economic_c;
use economic_helpers;
use endpoints\economic_customers_endpoint;
use endpoints\economic_departments_endpoint;
use endpoints\economic_invoices_endpoint;
use endpoints\economic_layouts_endpoint;
use endpoints\economic_orders_endpoint;
use endpoints\economic_payment_terms_endpoint;
use endpoints\economic_products_endpoint;
use helpers\economic_customer;
use helpers\economic_invoice_draft;
use helpers\economic_invoice_booked;
use helpers\economic_tasks;
use interfaces\economic_i;

class economic implements economic_i
{
    public const DRAFT_CUSTOMER_EXPORT_BLOCKED_MESSAGE = 'Transactions for the configured draft customer cannot be exported to e-conomic.';
    public const DEFAULT_DISTRIBUTION_DEPARTMENT_ID = 1;

    /**
     * Configuration of the economic module
     * @var economic_c
     */
    public economic_c $config;
    /**
     * Any endpoints reached by the /orders endpoint
     * @var economic_orders_endpoint
     */
    public economic_orders_endpoint $orders;
    /**
     * Any endpoints reached by the /invoices endpoint
     * @var economic_invoices_endpoint
     */
    public economic_invoices_endpoint $invoices;
    /**
     * Any endpoints reached by the /departments endpoint
     * @var economic_departments_endpoint
     */
    public economic_departments_endpoint $departments;
    /**
     * Any endpoints reached by the /layouts endpoint
     * @var economic_layouts_endpoint
     */
    public economic_layouts_endpoint $layouts;
    /**
     * Any endpoints reached by the /customers endpoint
     * @var economic_customers_endpoint
     */
    public economic_customers_endpoint $customers;
    /**
     * Any endpoints reached by the /products endpoint
     * @var economic_products_endpoint
     */
    public economic_products_endpoint $products;
    /**
     * Any endpoints reached by the /payment-terms endpoint
     * @var economic_payment_terms_endpoint
     */
    public economic_payment_terms_endpoint $payment_terms;
    /**
     * Helper classes
     * @var economic_helpers
     */
    protected economic_helpers $helpers;


    public function __construct()
    {
        $this->config = new economic_c();
        $this->orders = new economic_orders_endpoint();
        $this->invoices = new economic_invoices_endpoint();
        $this->departments = new economic_departments_endpoint();
        $this->layouts = new economic_layouts_endpoint();
        $this->payment_terms = new economic_payment_terms_endpoint();
        $this->customers = new economic_customers_endpoint();
        $this->products = new economic_products_endpoint();
        $this->helpers = new economic_helpers();
    }

    /**
     * Get a customer by their customer number
     * @param int $customer_number The Economic customer number
     * @return economic_customer
     */
    public function getCustomer(int $customer_number): economic_customer
    {
        return new $this->helpers->economic_customer($customer_number);
    }

    /**
     * Get a invoiceDraft (Helper) from the Economic system
     * @param int $draft_invoice_number The Economic draft invoice number
     * @param string $currency The conversion rate to use (default: DKK)
     * @param bool $skip_fetch Skip fetching the invoice draft from the Economic system (default: false)
     * @return economic_invoice_draft
     */
    public function getInvoiceDraft(int $draft_invoice_number, string $currency = 'DKK', bool $skip_fetch = false): economic_invoice_draft
    {
        return new $this->helpers->economic_invoice_draft($draft_invoice_number, $currency, $skip_fetch);
    }

    /**
     * Get the tasks helper
     * @return economic_tasks
     */
    public function getTasks(): economic_tasks
    {
        return new $this->helpers->economic_tasks();
    }

    public function getTransactionDraftCustomerNumber(): ?int
    {
        $value = $this->config->transaction_draft_customer_number->getVariableValue();
        if ($value === null || $value === '') {
            return null;
        }

        $customer_number = (int)$value;
        return $customer_number > 0 ? $customer_number : null;
    }

    public function getDefaultDistributionDepartmentId(): int
    {
        $value = $this->config->default_department_id->getVariableValue();
        $department_id = (int)$value;

        return $department_id > 0 ? $department_id : self::DEFAULT_DISTRIBUTION_DEPARTMENT_ID;
    }

    public function isDraftCustomerNumber(?int $customer_number): bool
    {
        $configured_customer_number = $this->getTransactionDraftCustomerNumber();
        if ($configured_customer_number === null || $customer_number === null) {
            return false;
        }

        return $configured_customer_number === (int)$customer_number;
    }

    /**
     * @throws \Exception
     */
    public function assertCustomerNumberIsNotDraft(?int $customer_number): void
    {
        if ($this->isDraftCustomerNumber($customer_number)) {
            throw new \Exception(self::DRAFT_CUSTOMER_EXPORT_BLOCKED_MESSAGE);
        }
    }

    /**
     * Create a customer in e-conomic and return the raw upstream payload.
     */
    public function createCustomer(
        int $customer_number,
        string $name,
        int $cvr_number,
        string $email,
        int $phone,
        ?int $mobile_phone = null,
        object|array|null $company_information = null,
        ?string $ean = null
    ): object
    {
        $payload = [
            'customerNumber' => $customer_number,
            'corporateIdentificationNumber' => (string)$cvr_number,
            'customerGroup' => [
                'customerGroupNumber' => 1,
            ],
            'paymentTerms' => [
                'paymentTermsNumber' => 12,
            ],
            'name' => $name,
            'email' => $email,
            'phone' => $phone,
            'telephoneAndFaxNumber' => (string)$phone,
            'mobilePhone' => (string)($mobile_phone ?? $phone),
            'currency' => 'DKK',
            'vatZone' => [
                'vatZoneNumber' => 1,
            ]
        ];

        $payload = array_replace($payload, $this->buildCustomerPayloadFromCompanyInformation($company_information));
        $normalized_ean = self::normalizeCustomerEan($ean);
        if ($normalized_ean !== null) {
            $payload['ean'] = $normalized_ean;
        }

        return $this->customers->customers->create($payload);
    }

    public static function normalizeCustomerEan(mixed $value): ?string
    {
        if ($value === null) {
            return null;
        }

        $digits = preg_replace('/\D+/', '', (string)$value);
        if (!is_string($digits)) {
            return null;
        }

        $digits = trim($digits);
        if ($digits === '') {
            return null;
        }

        if (strlen($digits) > 13) {
            throw new \InvalidArgumentException('EAN must be at most 13 digits.');
        }

        return $digits;
    }

    private function buildCustomerPayloadFromCompanyInformation(object|array|null $company_information): array
    {
        if ($company_information === null) {
            return [];
        }

        $payload = [];
        $field_map = [
            'address' => 'address',
            'zipcode' => 'zip',
            'city' => 'city',
            'website' => 'website',
        ];

        foreach ($field_map as $source_field => $economic_field) {
            $value = $this->companyInformationValue($company_information, $source_field);
            if ($value === null) {
                continue;
            }

            $payload[$economic_field] = $value;
        }

        return $payload;
    }

    private function companyInformationValue(object|array $company_information, string $field): ?string
    {
        if (is_array($company_information)) {
            $value = $company_information[$field] ?? null;
        } else {
            $value = $company_information->{$field} ?? null;
        }

        if ($value === null) {
            return null;
        }

        $normalized = trim((string)$value);

        return $normalized !== '' ? $normalized : null;
    }

    /**
     * @throws \Exception
     */
    public function getInvoiceBookedFromExternalId(string $external_id): economic_invoice_booked
    {
        $bookedId = $this->invoices->booked->get_from_external_id($external_id);
        $raw = $this->invoices->booked->getFromId($bookedId);
        // Return the booked invoice helper
        return (new economic_invoice_booked($raw));
    }
}
