<?php

namespace apexl\Io\modules\invoice\controllers;

use apexl\Config\Singleton;
use apexl\encryption\Encrypt;
use apexl\Io\includes\Controller;
use apexl\Io\includes\Hook;
use apexl\Io\includes\Routes;
use apexl\Io\includes\System;
use apexl\Io\modules\invoice\entities\invoiceEntity;
use apexl\Io\modules\invoice\services\invoiceService;
use apexl\Io\modules\payment\entities\paymentEntity;
use apexl\Io\modules\payment\entities\paymentTokenEntity;
use apexl\Io\modules\payment\entities\userCreditEntity;
use apexl\Io\modules\payment\providers\stripeProvider;
use apexl\Io\modules\payment\services\cardValidationService;

use apexl\Io\modules\payment\services\paymentService;
use apexl\Io\modules\payment\services\woocommerceIntegrationService;
use apexl\Io\modules\product\entities\productEntity;
use apexl\Io\modules\subscription\entities\subscriptionEntity;
use apexl\Io\modules\subscription\services\subscriptionService;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\services\currentUser;
use apexl\Io\modules\user\services\userTools;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class invoiceController extends Controller{

    public function __construct(invoiceService $invoiceService)
    {
        parent::__construct();
        $this->invoiceService = $invoiceService;
    }

    public function triggerInvoicePayments(Request $request, Response $response, $args)
    {
        $result = $this->invoiceService->triggerInvoicePayments($request, $response, $args);

        System::setVariable('cron_invoice_payments_completed', date('U'));

        return System::asJson($response, ['result' => $result]);
    }

    public function triggerSubscriptionInvoices(Request $request, Response $response, $args)
    {
        $result = $this->invoiceService->triggerSubscriptionInvoices($request, $response, $args);

        System::setVariable('cron_subscription_billing_completed', date('U'));

        return System::asJson($response, ['result' => $result]);
    }

    public function triggerProviderUploads(Request $request, Response $response, $args)
    {
        $result = $this->invoiceService->triggerProviderUploads($args['id'], $args['providerName'] ?? null);

        System::setVariable('cron_provider_uploads_completed', date('U'));

        return System::asJson($response, ['result' => $result]);
    }

    public function sendInvoiceToProvider(Request $request, Response $response, $args)
    {
        if (isset($args['id']) && is_numeric($args['id'])) {
            $result = $this->invoiceService->sendInvoiceToProvider($args['id'], $args['providerName'] ?? null);

            return System::asJson($response, ['result' => $result]);
        }
    }

    public function viewInvoice(Request $request, Response $response, $args)
    {
        if (empty($args['id'])) {
            $this->output::addMessage('provider.invoices.view', 'error','ID not provided');
            return System::asJson($response, [], 404);
        }

        $url = $this->invoiceService->getInvoiceUrl($args['id']);

        if ($url) {
            $this->output::addMetadata(
                'provider.viewInvoice.post.redirect',
                'redirectToExternal',
                $url);
            return System::asJson($response);
        } else {
            $this->output::addMessage('provider.invoices.view', 'error','Invoice could not be found');
            return System::asJson($response, [], 404);
        }
    }

    public function searchInvoices(Request $request, Response $response)
    {
        $user = currentUser::getCurrentUser();

        if (!($user->email ?? FALSE)) {
            die(); // No one logged in!
        } else $initialEmail = $user->email;

        $params = $request->getQueryParams();
        $initialEmail = $params['user_email'] ?? $user->email;

        $entityData['tableHeader'] = [
            'Invoice Number',
            'Email',
            'Invoice Date',
            'Reference',
            'Subtotal',
            'Tax',
            'Total',
            ''
        ];

        $emails[] = $initialEmail;

        // Check for companyEmail
        if (class_exists('\apexl\Io\modules\company\entities\companyEntity')) { // If io-company module is installed

            // 1. if supplied email is company email address, load child accounts
            $companyEntity = new \apexl\Io\modules\company\entities\companyEntity();
            $companyEntity->loadByCompanyContactEmail($initialEmail);
            if (isset($companyEntity->CompanyContactEmail) && trim($initialEmail) == trim($companyEntity->CompanyContactEmail)) {

                $users = $companyEntity->getAllCompanyUsers($companyEntity->id);
                foreach ($users as $user) {
            $userEntity = new userEntity();
                    $userEntity->load($user->UserId);
                    if (isset($userEntity->id) && $userEntity->id > 0) {
                        $emails[] = $userEntity->email;
                    }
                }
            } else {

                // 2. if this user is linked to a company, use the company email address
                $userEntity = new userEntity();
                $userEntity->getUserByEmail($initialEmail);
            if (isset($userEntity->id) && $userEntity->id > 0) {
            $companyEntity = new \apexl\Io\modules\company\entities\companyEntity();
                $companyEntity->loadByUser($userEntity->id);
                    if (isset($companyEntity->CompanyContactEmail) && trim($initialEmail) != trim($companyEntity->CompanyContactEmail)) {
                        $emails[] = $companyEntity->CompanyContactEmail;
                    }
            }
        }
        }

        $emails = array_unique($emails);
        $entityData['rows'] = [];
        foreach ($emails as $email) {
            $entityData['rows'] = array_merge($entityData['rows'], $this->invoiceService->getProviderInvoices($email));
        }

        /*if (empty($entityData['rows'])) {
            $entityData['tableHeader'] = [];
            $entityData['rows'][] = ['id' => 'No invoices on record'];
        }*/

        return System::asJson($response, $entityData);
    }

    public function invoiceListTableDataBasic(Request $request, Response $response){
        $params = $request->getQueryParams();
        $entity = new invoiceEntity();

        $currentUser = currentUser::getCurrentUser();
        $userEntity = new userEntity();

        $subscriptionService = new subscriptionService();
        $productEntity = new productEntity();

        $showCust = false;
        if (!$currentUser->isAllowed('administer')) {
            //$userEntity->getUserByEmail($currentUser->email); // Customers invoices shown based on access rules in config.json
            //$params['user_id'] = $userEntity->id ?? 0;
        } elseif (isset($params['userEmail'])) {
            $userEntity->getUserByEmail($params['userEmail']);
            $params['user_id'] = $userEntity->id ?? 0;
        } else {
            $showCust = true;
            $usersById = $userEntity->getCached('id', $selectFields=['email']);
        }

        $filters = $this->buildFilterConditions($entity, $params);

        $invoices = $entity->loadMultiple($filters, $params['orderBy'] ?? [], $params['limit']??20, $params['offset']); // Note - this inclueds access control with correct config

        $entityData['tableHeader'] = ['#', 'Invoice Date', 'Invoice No.'];
        if ($showCust) {
            $entityData['tableHeader'] = array_merge($entityData['tableHeader'], ['Customer', 'Type', /*'Product'*/]);
        }
        $entityData['tableHeader'] = array_merge($entityData['tableHeader'], ['Total', 'VAT', 'Paid?', 'Credit Notes', 'Last Payment Attempt']);

        $rows = [];

        foreach($invoices as $invoice){

            $invoiceDate = \DateTime::createFromFormat('Y-m-d', $invoice->invoice_date);
            $invoiceCreatedTime = \DateTime::createFromFormat('U', $invoice->created);
            $lastAttempt = $invoice->last_payment_attempt ? \DateTime::createFromFormat('Y-m-d H:i:s', $invoice->last_payment_attempt) : null;

            $row = [
                'id' => $invoice->id,
                'created' => $invoiceDate->format('d M Y') .' at '.$invoiceCreatedTime->format('H:i'),
                'number' => $invoice->invoice_number,

            ];
            if ($showCust) {
                $row['customer'] = $usersById[$invoice->user_id]->email ?? '';
                $lineItems = json_decode($invoice->line_items);
                $type = 'PURCHASE';
                $products = [];
                if (is_array($lineItems)) {
                    foreach ($lineItems as $item) {
                        $type = $subscriptionService->isLineItemScheduledPayment($item, $invoice) ? 'SUBSCRIPTION' : 'PURCHASE';
                        $result = Hook::processHook('modify_is_item_scheduled_payment_label', $item, $invoice, $type);
                        if ($result) $type = $result;
                        if (isset($item->productId)) {
                            $product = $productEntity->getByIdOrCode($item->productId);
                            $products[] = $product['product_name'] ?? '';
                        }
                    }
                }
                $row['type'] = $type;
                //$row['product'] = implode("<br>", array_unique(array_filter($products)));
            }
            $row['total'] = '£'.$invoice->total;
            $row['vat'] = '£'.$invoice->vat;
            $row['paid'] = $invoice->credit_note_info ? 'Credited' : ($invoice->paid ? 'Yes' : 'No');
            $row['credit_notes'] = $invoice->credit_note_info;
            $row['last_payment_attempt'] = $lastAttempt ? $lastAttempt->format('d M Y') : 'N/A';
            //$row['abandoned'] = $invoice->abandoned ? 'Yes' : 'No';

            $rows[] = $row;
        }

        $entityData['rows'] = $rows;
        $entityData['totalData'] = count($rows);
        unset($entityData['data']);

        $newData = Hook::processHook('modify_invoice_list_table_data_basic', $entityData, $showCust);
        if ($newData) $entityData = $newData;

        return System::asJson($response, $entityData);
    }

    public function buildFilterConditions($entity, $params, $validFields = []): array
    {
        $filters = parent::buildFilterConditions($entity, $params, ['startDate', 'endDate']);
        if(isset($filters['startDate'])) {
            $filters['startDate'] = ['invoice_date', $params['startDate'], '>='];
        }
        if(isset($filters['endDate'])) {
            $filters['endDate'] = ['invoice_date', $params['endDate'], '<='];
        }
        return $filters;
    }

    public function subscriptionInvoicesTableDataBasic(Request $request, Response $response) {

        $params = $request->getQueryParams();
        $entityData['tableHeader'] = ['#', 'Invoice Date', 'Total', 'VAT', 'Paid?', 'Last Payment Attempt', 'Abandoned?'];
        $rows = [];
        $invoiceEntity = new invoiceEntity();
        $invoices = $invoiceEntity->getForSubscription($params['subscriptionId']);

        foreach($invoices as $invoice){

            $invoiceDate = \DateTime::createFromFormat('Y-m-d', $invoice->invoice_date);
            $invoiceCreatedTime = \DateTime::createFromFormat('U', $invoice->created);
            $lastAttempt = $invoice->last_payment_attempt ? \DateTime::createFromFormat('Y-m-d H:i:s', $invoice->last_payment_attempt) : null;

            $row = [
                'id' => $invoice->id,
                'created' => $invoiceDate->format('d M Y') . ' at ' . $invoiceCreatedTime->format('H:i'),
                'total' => '£'.$invoice->total,
                'vat' => '£'.$invoice->vat,
                'paid' => $invoice->paid ? 'Yes' : 'No',
                'Last Payment Attempt' => $lastAttempt ? $lastAttempt->format('d M Y') : 'N/A',
                'Abandoned?' => $invoice->abandoned ? 'Yes' : 'No',
            ];
            $rows[] = $row;
        }

        $entityData['rows'] = $rows;
        $entityData['totalData'] = count($rows);
        unset($entityData['data']);

        return System::asJson($response, $entityData);
    }

    public function brandingThemes(Request $request, Response $response)
    {
        $this->invoiceService->getProviderBrandingThemes();
    }

    public function accounts(Request $request, Response $response)
    {
        //$this->invoiceService->changeInvoiceContact('eb2ef0ed-5afa-4c8f-8233-cc61c596631c', 'Total Care and Support');
        $this->invoiceService->getProviderAccounts();
    }

    public function contactHistory(Request $request, Response $response, $args)
    {
        $this->invoiceService->getProviderContactHistory($args['identifier'], $args['type']);
    }

    public function backfillProviderPaidStatus(Request $request, Response $response, $args)
    {
        $this->invoiceService->backfillProviderPaidStatus();

        return System::asJson($response, ['result' => true]);
    }

    public function triggerPaymentAlerts(Request $request, Response $response, $args)
    {
        $result = $this->invoiceService->triggerPaymentAlerts($request, $response, $args);

        return System::asJson($response, ['result' => $result]);
    }

    public function backfillUserInvoices(Request $request, Response $response, $args)
    {
        $this->invoiceService->backfillUserInvoices($args['userId']);

        $this->output::addMetadata('provider.invoices.backfill_1', 'refreshByTag', ['user-invoices-table']);
        $this->output::addMessage('provider.invoices.backfill', 'success', 'Invoice Backfill complete');

        return System::asJson($response, ['result' => true]);
    }

    public function manuallyChargeInvoices(Request $request, Response $response)
    {
        $invoiceIds = [];
        $invoiceService = new invoiceService();
        $numInvoices = 0;
        foreach ($invoiceIds as $invoiceId) {
            $invoiceEntity = new invoiceEntity();
            $invoiceEntity->load($invoiceId);
            $invoiceService->attemptInvoicePayment($invoiceEntity, 'InvId-' . $invoiceEntity->id, false, $invoiceService->getPaymentNotesFromInvoice($invoiceEntity), null, false, false);
            $numInvoices++;
        }

        return System::asJson($response, ['numInvoicesProcessed' => $numInvoices]);
    }

}