<?php

declare(strict_types=1);

namespace PortalPeople\datadash\services;

use Yii;
use yii\db\Expression;
use yii\web\UploadedFile;

/**
 * DataService
 *
 * Handles the datafeeds dashboard data operations (upload, order downloads, CSV exports).
 * Extracted from api/modules/df/controllers/DataController (namespace api\modules\v3\controllers).
 */
class DataService
{
    /**
     * Accept a compressed data file upload and queue an import job.
     */
    public function upload(string $modelname, string $connection, string $db, string $company): mixed
    {
        $model    = '\\api\\modules\\df\\models\\' . str_replace('DF', '', $modelname);
        $base     = Yii::getAlias('@console');
        $upload   = UploadedFile::getInstanceByName('compresseddata');
        $filepath = $base . '/temp/data/' . date('d-Hi') . '-' . $upload->name;

        file_put_contents($filepath, file_get_contents($upload->tempName));

        return Yii::$app->dfqueue
            ->jobname('Import ' . date('d-Hi') . '-' . $model)
            ->ttr(7200)
            ->push(new \console\jobs\system\Apiuploaddata([
                'filepath' => date('d-Hi') . '-' . $upload->name,
                'model'    => $model,
            ]));
    }

    /**
     * Return un-downloaded orders (Paid/Ordered) formatted for ERP.
     *
     * @return array<string, mixed>|array<int, string>
     */
    public function downloadOrders(string $type): array
    {
        $down   = new Expression("JSON_EXTRACT(basket.extended, '$.downloaded')");
        $params = $this->frontendParams();

        $carts = \frontend\models\Cart::find()
            ->where(['basket.status' => ['Paid', 'Ordered']])
            ->andWhere(['or', ['is', $down, null], ['=', $down, false]])
            ->andWhere(['not', ['in', 'basketlines.productid', [
                $params['site']['account']['pay_invoices']['invoice_code'],
                $params['site']['account']['pay_invoices']['credit_code'],
            ]]])
            ->leftJoin('basketlines', 'basketlines.basketid = basket.id and basketlines.status = "Active"')
            ->all();

        if ($type === 'csv') {
            return array_column($carts, 'orderref');
        }

        $output = [];
        foreach ($carts as $cart) {
            foreach ($cart->cartlines as $line) {
                $stockCode = empty($line->stock_code) === false
                    ? trim($line->stock_code)
                    : ($line->product ? $line->product->stock_code : null);

                $output[$cart->orderref][] = [
                    'order_number'  => $cart->orderref,
                    'order_date'    => date('d/m/y', strtotime(str_replace('/', '-', $cart->dateordered))),
                    'time'          => date('Hi', strtotime(str_replace('/', '-', $cart->dateordered))),
                    'product'       => $stockCode,
                    'product_desc'  => trim($line->product_name),
                    'product_qty'   => $line->qty,
                    'prod_price_ea' => $line->price,
                    'prod_price_to' => $line->linetotal,
                    'goods_total'   => $cart->subtotal,
                    'ship_total'    => $cart->shipping,
                    'tax_total'     => $cart->tax,
                    'disc_total'    => $cart->discount,
                    'order_total'   => $cart->total,
                    'cust_name'     => trim($cart->contact),
                    'cust_company'  => trim($cart->companyname),
                    'cust_add1'     => trim($cart->fulladdresses['billing']->address1),
                    'cust_add2'     => trim($cart->fulladdresses['billing']->address2),
                    'cust_town'     => trim($cart->fulladdresses['billing']->town),
                    'cust_county'   => trim($cart->fulladdresses['billing']->county),
                    'cust_country'  => trim($cart->fulladdresses['billing']->country),
                    'cust_postcode' => trim($cart->fulladdresses['billing']->postcode),
                    'cust_tel'      => trim($cart->fulladdresses['billing']->telephone),
                    'cust_fax'      => trim($cart->fulladdresses['billing']->fax),
                    'cust_email'    => trim($cart->fulladdresses['billing']->email),
                    'del_name'      => null,
                    'del_company'   => trim($cart->fulladdresses['shipping']->company_name),
                    'del_add1'      => trim($cart->fulladdresses['shipping']->address1),
                    'del_add2'      => trim($cart->fulladdresses['shipping']->address2),
                    'del_town'      => trim($cart->fulladdresses['shipping']->town),
                    'del_county'    => trim($cart->fulladdresses['shipping']->county),
                    'del_country'   => trim($cart->fulladdresses['shipping']->country),
                    'del_postcode'  => trim($cart->fulladdresses['shipping']->postcode),
                    'del_tel'       => trim($cart->fulladdresses['shipping']->telephone),
                    'paid'          => $cart->paid == 1 ? 'Y' : 'N',
                    'pay_type'      => null,
                    'ipaddress'     => $cart->ipaddress,
                    'account_code'  => trim($cart->account_code),
                    'cust_ref'      => trim($cart->porderref),
                    'orderID'       => $cart->id,
                ];
            }
        }

        return $output;
    }

    /**
     * Return un-downloaded invoice/credit payment order refs.
     *
     * @return array<int, string>
     */
    public function downloadPaidOrders(string $type): array
    {
        $down   = new Expression("JSON_EXTRACT(basket.extended, '$.downloaded')");
        $params = $this->frontendParams();

        $carts = \frontend\models\Cart::find()
            ->where(['basket.status' => 'Paid'])
            ->andWhere(['or', ['is', $down, null], ['=', $down, false]])
            ->andWhere(['in', 'basketlines.productid', [
                $params['site']['account']['pay_invoices']['invoice_code'],
                $params['site']['account']['pay_invoices']['credit_code'],
            ]])
            ->leftJoin('basketlines', 'basketlines.basketid = basket.id and basketlines.status = "Active"')
            ->all();

        if ($type === 'csv') {
            return array_column($carts, 'orderref');
        }

        return [];
    }

    /**
     * Stream a CSV download for a single order.
     *
     * @return mixed
     */
    public function downloadCsv(string $orderref): mixed
    {
        $down = new Expression("JSON_EXTRACT(basket.extended, '$.downloaded')");

        $cart = \frontend\models\Cart::find()
            ->where(['basket.orderref' => $orderref])
            ->andWhere(['or', ['is', $down, null], ['=', $down, false]])
            ->one();

        if ($cart) {
            Yii::$app->response->statusCode = 200;
            return $cart->exporttoCsv('cartlines', 'download');
        }

        Yii::$app->response->statusCode = 100;
        return ['status' => 100, 'message' => 'No Cart at this address'];
    }

    /**
     * Stream a CSV download for a single paid/invoice order.
     *
     * @return mixed
     */
    public function downloadPaidCsv(string $orderref): mixed
    {
        $params = $this->frontendParams();
        $down   = new Expression("JSON_EXTRACT(basket.extended, '$.downloaded')");

        $cart = \frontend\models\Cart::find()
            ->where(['basket.orderref' => $orderref])
            ->andWhere(['or', ['is', $down, null], ['=', $down, false]])
            ->one();

        if ($cart) {
            Yii::$app->response->statusCode = 200;

            if (in_array(
                $params['site']['account']['pay_invoices']['invoice_code'],
                array_column($cart->cartlines, 'productid')
            )) {
                return $cart->ExportPaidInvoice('cartlines', 'download');
            }

            return $cart->exporttoCsv('cartlines', 'download');
        }

        Yii::$app->response->statusCode = 100;
        return ['status' => 100, 'message' => 'No Cart at this address'];
    }

    /**
     * Stream a grouped CSV download for a month.
     *
     * @return mixed
     */
    public function downloadCsvGroup(?string $month): mixed
    {
        $month = $month ?? date('M', strtotime('last month'));
        $data  = \modules\custom\models\Cart::exporttoCsvmonth('cartlines', 'download', $month);

        if ($data) {
            Yii::$app->response->statusCode = 200;
            return $data;
        }

        Yii::$app->response->statusCode = 100;
        return ['status' => 100, 'message' => 'No Carts at this address'];
    }

    /**
     * Mark order(s) as downloaded.
     *
     * @param array<string, mixed> $params  POST params
     * @return mixed
     */
    public function markDownloaded(array $params): mixed
    {
        $down = new Expression("JSON_EXTRACT(basket.extended, '$.downloaded')");

        if (($params['type'] ?? null) === 'csv') {
            $orderref = $params['orderref'];

            $model = \frontend\models\Cart::find()
                ->where(['basket.orderref' => $orderref])
                ->andWhere(['in', 'basket.status', ['Ordered', 'Paid']])
                ->andWhere(['or', ['is', $down, null], ['=', $down, false]])
                ->one();

            if ($model) {
                $extended               = $model->extended;
                $extended->downloaded   = 1;
                $extended->downloadeddate = date('d/m/y H:i');

                return $model->updateAttributes([
                    'billingstatus' => 'Sent to Erp',
                    'extended'      => json_encode($extended),
                ]);
            }

            return null;
        }

        $carts   = \frontend\models\Cart::find()
            ->where(['basket.status' => 'Ordered'])
            ->andWhere(['or', ['is', $down, null], ['=', $down, false]])
            ->all();

        $results = [];
        $saved   = $params['saved'] ?? [];

        foreach ($params['rows'] ?? [] as $orderno => $drows) {
            if (isset($saved[$orderno]) && count($saved[$orderno]) === $drows) {
                $results[$orderno] = $drows;
            }
        }

        foreach ($carts as $model) {
            if (isset($results[$model->orderref])
                && (count($model->cartlines) === $results[$model->orderref]
                    || $results[$model->orderref] === '*')
            ) {
                $extended               = $model->extended;
                $extended->downloaded   = 1;
                $extended->downloadeddate = date('d/m/y H:i');

                return $model->updateAttributes([
                    'billingstatus' => 'Sent to Erp',
                    'extended'      => json_encode($extended),
                ]);
            }
        }

        return null;
    }

    // -------------------------------------------------------------------------
    // Private helpers
    // -------------------------------------------------------------------------

    /**
     * Merge frontend params whether running in API or frontend context.
     *
     * @return array<string, mixed>
     */
    private function frontendParams(): array
    {
        if (str_contains(Yii::getAlias('@app'), 'frontend')) {
            return Yii::$app->params;
        }

        return array_merge(
            Yii::$app->params,
            include(Yii::getAlias('@frontend') . '/config/params.php'),
            include(Yii::getAlias('@frontend') . '/config/params-local.php')
        );
    }
}
