<?php

/**
 * Helpers function for retrieving data from WHMCS.
 *
 * ATTENTION: do not edit this file as the changes will be lost when the module
 * gets updated.
 */

use Lkn\HookNotification\Core\Shared\Infrastructure\Config\Settings;
use Spatie\PdfToImage\Enums\OutputFormat;
use Spatie\PdfToImage\Pdf;
use WHMCS\Database\Capsule;

/**
 * Uses localAPI GetInvoice to get the invoice balance.
 *
 * @see https://developers.whmcs.com/api-reference/getinvoice/
 *
 * @param integer $invoiceId
 * @param boolean $formatFriendly
 *
 * @return float|null
 */
function getInvoiceBalance(
    int $invoiceId,
    bool $formatFriendly = true
): null|string|float {
    $value = localAPI('GetInvoice', ['invoiceid' => $invoiceId])['balance'];

    if ($formatFriendly) {
        return formatInvoiceValue($invoiceId, $value);
    }

    return $value;
}

/**
 * @param integer $invoiceId
 * @param boolean $formatFriendly
 *
 * @return null|string|float
 */
function getInvoiceTotal(
    int $invoiceId,
    bool $formatFriendly = true
): null|string|float {
    $value = Capsule::table('tblinvoices')->where('id', $invoiceId)->first('total')->total;

    if ($formatFriendly) {
        return formatInvoiceValue($invoiceId, $value);
    }

    return $value;
}

/**
 * @param integer $invoiceId
 * @param boolean $formatFriendly
 *
 * @return null|string|float
 */
function getInvoiceSubtotal(
    int $invoiceId,
    bool $formatFriendly = true
): null|string|float {
    $value = Capsule::table('tblinvoices')->where('id', $invoiceId)->first('subtotal')->subtotal;

    if ($formatFriendly) {
        return formatInvoiceValue($invoiceId, $value);
    }

    return $value;
}

/**
 * @param integer $invoiceId
 * @param float   $value
 *
 * @return string
 */
function formatInvoiceValue(int $invoiceId, float $value): string
{
    $currencyInfo       = getInvoiceCurrency($invoiceId);
    $currencySeparators = getCurrencySeparatorsFromFormatId($currencyInfo->format);

    $currencySymbol = $currencyInfo->prefix;

    return $currencySymbol . number_format($value, 2, $currencySeparators['decimal'], $currencySeparators['thousands']);
}

/**
 * Gets the info from the tblcurrencies database table.
 *
 * @param integer $invoiceId
 */
function getInvoiceCurrency(int $invoiceId): stdClass
{
    return Capsule::table('tblinvoices')
        ->join('tblclients', 'tblclients.id', '=', 'tblinvoices.userid')
        ->join('tblcurrencies', 'tblcurrencies.id', '=', 'tblclients.currency')
        ->where('tblinvoices.id', $invoiceId)
        ->select('tblcurrencies.*')
        ->first();
}

/**
 * WHMCS format id are related to the formats as follow:
 *
 * 1 - 1234.56
 * 2 - 1,234.56
 * 3 - 1.234,56
 * 4 - 1,234
 *
 * @param integer $formatId
 *
 * @return array an array as: [decimal => '', thousands => ''].
 */
function getCurrencySeparatorsFromFormatId(int $formatId): array
{
    $separators = [
        1 => [
            'decimal' => '.',
            'thousands' => '',
        ],
        2 => [
            'decimal' => '.',
            'thousands' => ',',
        ],
        3 => [
            'decimal' => ',',
            'thousands' => '.',
        ],
        4 => [
            'decimal' => '',
            'thousands' => ',',
        ],
    ];

    return $separators[$formatId];
}

function getClientFullNameByClientId(int $id): string
{
    $name = Capsule::table('tblclients')
        ->where('id', $id)
        ->first(['firstname', 'lastname']);

    $name = ucwords(strtolower(trim($name->firstname . ' ' . $name->lastname)));

    return empty($name) ? lkn_hn_config(Settings::DEFAULT_CLIENT_NAME) : $name;
}

function getClientFirstTwoNamesByClientId(int $id): string
{
    $name = Capsule::table('tblclients')
        ->where('id', $id)
        ->first(['firstname', 'lastname']);

    $secondName = explode(' ', $name->lastname)[0] ?? '';

    $name = ucwords(strtolower(trim($name->firstname . ' ' . $secondName)));

    return empty($name) ? lkn_hn_config(Settings::DEFAULT_CLIENT_NAME) : $name;
}

function getClientFirstNameByClientId(int $id): string
{
    $name = trim(Capsule::table('tblclients')
        ->where('id', $id)
        ->value('firstname'));

    $name = ucwords($name);

    return empty($name) ? lkn_hn_config(Settings::DEFAULT_CLIENT_NAME): $name;
}

function getClientEmailByClientId(int $clientId): string
{
    $client = Capsule::table('tblclients')
        ->where('id', $clientId)
        ->first(['email']);

    return strtolower($client->email ?? '');
}

function getInvoicePdfUrlByInvocieId(
    int $id,
    bool $returnNullOtherwise = false,
    bool $returnSystemPath = false,
): ?string {
    if (!function_exists('getclientsdetails')) {
        require_once __DIR__ . '/../../../../../../includes/invoicefunctions.php';
    }

    $pdfContent = pdfInvoice($id);

    $randonPrefix   = (string) random_int(0, 100000);
    $randomFileName = uniqid($randonPrefix, true) . '.pdf';

    $pdfPath = __DIR__ . '/../../Core/temp/invoices_pdf/' . $randomFileName;

    if (!file_put_contents($pdfPath, $pdfContent)) {
        if ($returnNullOtherwise) {
            return null;
        }

        throw new Exception('Could not create invoice PDF');
    }

    $pdfPath = realpath($pdfPath);

    if (!$pdfPath) {
        throw new Exception('Unable to identify PDF path.');
    }

    $pdfUrl = moduleUrl() . '/src/Core/temp/invoices_pdf/' . $randomFileName;

    return $returnSystemPath ? $pdfPath : $pdfUrl;
}

function getInvoiceImgUrlByInvoiceId(
    int $invoiceId,
    bool $returnSystemPath = false,
): ?string {
    $pdfSystemPath = getInvoicePdfUrlByInvocieId($invoiceId, true, true);

    if (!$pdfSystemPath || !file_exists($pdfSystemPath)) {
        return null;
    }

    $imageName = uniqid('invoice_', false) . '.png';
    $imagePath = __DIR__ . '/../../Core/temp/invoices_img/' . $imageName;

    $pdf    = new Pdf($pdfSystemPath);
    $result = $pdf->selectPages(1)->format(OutputFormat::Png)->save($imagePath);

    $imgPath = realpath($imagePath);

    if (!$imgPath) {
        throw new Exception('Unable to identify PDF image path.');
    }

    $imgUrl = moduleUrl() . '/src/Core/temp/invoices_img/' . $imageName;

    return $returnSystemPath
        ? $imgPath
        : $imgUrl;
}


function getClientIdByQuoteId(int $quoteId): int
{
    return Capsule::table('tblquotes')->where('id', $quoteId)->first('userid')->userid;
} 

/**
 * Line items "type" and "domain".
 *
 * @param integer $orderId
 *
 * @return string
 */
function getOrderItemsDescripByOrderId(int $orderId): string
{
    $response = localAPI('GetOrders', ['id' => $orderId]);

    $lineItems = $response['orders']['order'][0]['lineitems']['lineitem'];

    $description = '';

    foreach ($lineItems as $item) {
        $domain = $item['domain'];

        $productName = match ($item['producttype']) {
            'Hosting Account' => strtolower(lkn_hn_lang('hosting_account')),
            'Domain' => strtolower(lkn_hn_lang('domain')),
            'Server' => strtolower(lkn_hn_lang('server')),
            default => $item['producttype']
        };

        if ($productName === 'Other Product/Service') {
            $splittedProductName = explode(' - ', $item['product']);
            $productName         = count($splittedProductName) > 0 ? $splittedProductName[0] : lkn_hn_lang('service');
        }

        $description .= "$productName ";

        if (!empty($domain)) {
            $description .= lkn_hn_lang('for') . " $domain, ";
        }
    }

    return rtrim($description, ', ');
}

function getInvoiceItemsByInvoiceId(int $invoiceId): string
{
    $invoice = localAPI('GetInvoice', ['invoiceid' => $invoiceId]);
    $items   = $invoice['items']['item'];

    $description = implode(', ', array_column($items, 'description'));

    return rtrim($description, ', ');
}

/**
 * Tries to get the items of the invoice first using getOrderItemsDescripByOrderId.
 * If an empty string is returned, the uses getInvoiceItemsByInvoiceId.
 *
 * @param integer $invoiceId
 *
 * @return string
 */
function getItemsRelatedToInvoice(int $invoiceId): string
{
    $orderId = getOrderIdByInvoiceId($invoiceId);

    if ($orderId === null) {
        $output =  getInvoiceItemsByInvoiceId($invoiceId);

        return str_replace("\n", ' - ', $output);
    }

    $itemsByOrder = getOrderItemsDescripByOrderId($orderId);

    if (!empty($itemsByOrder)) {
        return str_replace("\n", ' - ', $itemsByOrder);
    }

    $output = getInvoiceItemsByInvoiceId($invoiceId);
    $output = str_replace("\n", ' - ', $itemsByOrder);

    return $output;
}

/**
 * @param integer $invoiceId
 *
 * @return array [0 => Description 1, 1 => Description 2]
 */
function getInvoiceItemsDescriptionsByInvoiceId(int $invoiceId): array
{
    return Capsule::table('tblinvoiceitems')
        ->where('invoiceid', $invoiceId)
        ->get('description')
        ->pluck('description')
        ->toArray();
}

function getInvoiceDueDateByInvoiceId(int $invoiceId): string
{
    $isoDate = Capsule::table('tblinvoices')->where('id', $invoiceId)->value('duedate');

    return (new DateTime($isoDate))->format('d/m/Y');
}

function getServiceProductNameByProductId(int $productId): string
{
    return Capsule::table('tblproducts')->where('id', $productId)->first('name')->name;
}

function getHostDomainByHostId(int $hostId): string
{
    return Capsule::table('tblhosting')->where('id', $hostId)->first('domain')->domain;
}

function getClientIdByInvoiceId(int $invoiceId): int
{
    return Capsule::table('tblinvoices')->where('id', $invoiceId)->first('userid')->userid;
}

function getClientIdByOrderId(int $orderId): int
{
    return Capsule::table('tblorders')->where('id', $orderId)->first('userid')->userid;
}

function getClientIdByModuleId(int $moduleId): int
{
    return Capsule::table('tblhosting')->where('packageid', $moduleId)->first('userid')->userid;
}

function getOrderIdByInvoiceId(int $invoiceId): ?int
{
    return Capsule::table('tblorders')->where('invoiceid', $invoiceId)->first('id')->id;
}

function getClientIdByTicketId(int $ticketId): ?int
{
    return Capsule::table('tbltickets')->where('id', $ticketId)->first('userid')->userid;
}

function getTicket(int $ticketId, string $column): string
{
    return Capsule::table('tbltickets')->where('id', $ticketId)->first($column)->$column;
}

function getTicketMask(int $ticketId): string
{
    return getTicket($ticketId, 'tid');
}

function getTicketSubject(int $ticketId): string
{
    return trim(strtolower(getTicket($ticketId, 'title')));
}

function getTicketStatus(int $ticketId): string
{
    return strtolower(getTicket($ticketId, 'status'));
}

function getTicketEmail(int $ticketId): string
{
    return strtolower(getTicket($ticketId, 'email'));
}

function getTicketNameColumn(int $ticketId): string
{
    $name = getTicket($ticketId, 'name');

    return empty($name)
        ? lkn_hn_config(Settings::DEFAULT_CLIENT_NAME)
        : ucwords(strtolower($name));
}

function getTicketWhatsAppCfValue(int $ticketId): ?int
{
    $ticketWhatsAppCfId = lkn_hn_config(Settings::WP_USE_TICKET_WHATSAPP_CF_WHEN_SET);

    $phone = Capsule::table('tblcustomfieldsvalues')
        ->where('relid', $ticketId)
        ->where('fieldid', $ticketWhatsAppCfId)
        ->first('value')
        ->value;

    return $phone ? preg_replace('/[^0-9]/', '', $phone) : null;
}

/**
 * @return array An array of items like: (
 *               [id] =>
 *               [type] =>
 *               [relid] =>
 *               [description] =>
 *               [amount] =>
 *               [taxed] =>
 *               [product_id] =>
 *               )
 *               Some items may not have a product_id since it must be a manually-added product or a taxe.
 */
function getInvoiceItems(int $invoiceId)
{
    $invoiceItems = localAPI('GetInvoice', ['invoiceid' => $invoiceId])['items']['item'];

    $invoiceItemsWithProductsIds = [];

    foreach ($invoiceItems as $item) {
        $relid = (int) ($item['relid']);

        if (
            in_array($item['type'], ['DomainRegister'], true)
        ) {
            $invoiceItemsWithProductsIds[] = $item;

            continue;
        }

        $takenRelids[] = $relid;

        // https://whmcs.community/topic/294264-api-get-the-product-id-from-getorders-call/#comment-1315022
        $product = Capsule::table('tblhosting')
            ->join('tblproducts', 'tblhosting.packageid', '=', 'tblproducts.id')
            ->where('tblhosting.id', '=', $relid)
            ->first('tblproducts.id as product_id');

        $invoiceItemsWithProductsIds[] = [...$item, 'product_id' => $product->product_id];
    }

    return $invoiceItemsWithProductsIds;
}

function systemUrl(): string
{
    $url = Capsule::table('tblconfiguration')
        ->where('setting', 'SystemURL')
        ->first(['value'])
        ->value;

    return rtrim($url, '/');
}

function moduleUrl(): string
{
    return systemUrl() . '/modules/addons/lknhooknotification';
}

function get_passsword_reset_url_for_user(string $email): string
{
    /** @var null|object{reset_token: string, reset_token_expiry: string} $user */
    $user = Capsule::table('tblusers')->where('email', $email)->first(['reset_token', 'reset_token_expiry']);

    if (!$user ) {
        throw new Exception();
    }

    /** @var string $userResetToken */
    $userResetToken = $user->reset_token;

    /** @var string $systemUrl */
    $systemUrl = Capsule::table('tblconfiguration')->where('setting', 'SystemURL')->first(['value'])->value;

    return $systemUrl . '/index.php?rp=/password/reset/redeem/' . $userResetToken;
}

function get_user_password_reset_token_by_user_email(string $email): string
{
    $resetToken = Capsule::table('tblusers')->where('email', $email)->value('reset_token');

    if (!$resetToken ) {
        throw new Exception();
    }

    return $resetToken;
}
