<?php

namespace App\Controllers;

use App\Libraries\Mailchimp_lib;

use App\Models\Customer;
use App\Models\Customer_rewards;
use App\Models\Tax_code;
use CodeIgniter\HTTP\DownloadResponse;
use Config\OSPOS;
use Config\Services;
use stdClass;

class Customers extends Persons
{
    private string $_list_id;
    private Mailchimp_lib $mailchimp_lib;
    private Customer_rewards $customer_rewards;
    private Customer $customer;
    private Tax_code $tax_code;
    private array $config;

    public function __construct()
    {
        parent::__construct('customers');
        $this->mailchimp_lib = new Mailchimp_lib();
        $this->customer_rewards = model(Customer_rewards::class);
        $this->customer = model(Customer::class);
        $this->tax_code = model(Tax_code::class);
        $this->config = config(OSPOS::class)->settings;

        $encrypter = Services::encrypter();

        if (!empty($this->config['mailchimp_list_id'])) {
            $this->_list_id = $encrypter->decrypt($this->config['mailchimp_list_id']);
        } else {
            $this->_list_id = '';
        }
    }

    /**
     * @return void
     */
    public function getIndex(): void
    {
        $data['table_headers'] = get_customer_manage_table_headers();

        echo view('people/manage', $data);
    }

    /**
     * Gets one row for a customer manage table. This is called using AJAX to update one row.
     */
    public function getRow(int $row_id): void
    {
        $person = $this->customer->get_info($row_id);

        // Retrieve the total amount the customer spent so far together with min, max and average values
        $stats = $this->customer->get_stats($person->person_id);    // TODO: This and the next 11 lines are duplicated in search().  Extract a method.

        if (empty($stats)) {
            // Create object with empty properties.
            $stats = new stdClass();
            $stats->total = 0;
            $stats->min = 0;
            $stats->max = 0;
            $stats->average = 0;
            $stats->avg_discount = 0;
            $stats->quantity = 0;
        }

        $data_row = get_customer_data_row($person, $stats);

        echo json_encode($data_row);
    }


    /**
     * Returns customer table data rows. This will be called with AJAX.
     *
     * @return void
     */
    public function getSearch(): void
    {
        $search = $this->request->getGet('search');
        $limit = $this->request->getGet('limit', FILTER_SANITIZE_NUMBER_INT);
        $offset = $this->request->getGet('offset', FILTER_SANITIZE_NUMBER_INT);
        $sort = $this->sanitizeSortColumn(customer_headers(), $this->request->getGet('sort', FILTER_SANITIZE_FULL_SPECIAL_CHARS), 'people.person_id');
        $order = $this->request->getGet('order', FILTER_SANITIZE_FULL_SPECIAL_CHARS);

        $customers = $this->customer->search($search, $limit, $offset, $sort, $order);
        $total_rows = $this->customer->get_found_rows($search);

        $data_rows = [];

        foreach ($customers->getResult() as $person) {
            // Retrieve the total amount the customer spent so far together with min, max and average values
            $stats = $this->customer->get_stats($person->person_id);    // TODO: duplicated... see above
            if (empty($stats)) {
                // Create object with empty properties.
                $stats = new stdClass();
                $stats->total = 0;
                $stats->min = 0;
                $stats->max = 0;
                $stats->average = 0;
                $stats->avg_discount = 0;
                $stats->quantity = 0;
            }

            $data_rows[] = get_customer_data_row($person, $stats);
        }

        echo json_encode(['total' => $total_rows, 'rows' => $data_rows]);
    }

    /**
     * Gives search suggestions based on what is being searched for
     */
    public function getSuggest(): void
    {
        $search = $this->request->getGet('term');
        $suggestions = $this->customer->get_search_suggestions($search);

        echo json_encode($suggestions);
    }

    /**
     * @return void
     */
    public function suggest_search(): void
    {
        $search = $this->request->getGet('term');
        $suggestions = $this->customer->get_search_suggestions($search, 25, false);

        echo json_encode($suggestions);
    }

    /**
     * Loads the customer edit form
     */
    public function getView(int $customer_id = NEW_ENTRY): void
    {
        // Set default values
        if ($customer_id == null) $customer_id = NEW_ENTRY;

        $info = $this->customer->get_info($customer_id);
        foreach (get_object_vars($info) as $property => $value) {
            $info->$property = $value;
        }
        $data['person_info'] = $info;

        if (empty($info->person_id) || empty($info->date) || empty($info->employee_id)) {
            $data['person_info']->date = date('Y-m-d H:i:s');
            $data['person_info']->employee_id = $this->employee->get_logged_in_employee_info()->person_id;
        }

        $employee_info = $this->employee->get_info($info->employee_id);
        $data['employee'] = $employee_info->first_name . ' ' . $employee_info->last_name;

        $tax_code_info = $this->tax_code->get_info($info->sales_tax_code_id);

        if ($tax_code_info->tax_code != null) {
            $data['sales_tax_code_label'] = $tax_code_info->tax_code . ' ' . $tax_code_info->tax_code_name;
        } else {
            $data['sales_tax_code_label'] = '';
        }

        $packages = ['' => lang('Items.none')];
        foreach ($this->customer_rewards->get_all()->getResultArray() as $row) {
            $packages[$row['package_id']] = $row['package_name'];
        }
        $data['packages'] = $packages;
        $data['selected_package'] = $info->package_id;

        $data['use_destination_based_tax'] = $this->config['use_destination_based_tax'];

        // Retrieve the total amount the customer spent so far together with min, max and average values
        $stats = $this->customer->get_stats($customer_id);
        if (!empty($stats)) {
            foreach (get_object_vars($stats) as $property => $value) {
                $info->$property = $value;
            }
            $data['stats'] = $stats;
        }

        // Retrieve the info from Mailchimp only if there is an email address assigned
        if (!empty($info->email)) {
            // Collect Mailchimp customer info
            if (($mailchimp_info = $this->mailchimp_lib->getMemberInfo($this->_list_id, $info->email)) !== false) {
                $data['mailchimp_info'] = $mailchimp_info;

                // Collect customer Mailchimp emails activities (stats)
                if (($activities = $this->mailchimp_lib->getMemberActivity($this->_list_id, $info->email)) !== false) {
                    if (array_key_exists('activity', $activities)) {
                        $open = 0;
                        $unopen = 0;
                        $click = 0;
                        $total = 0;
                        $lastopen = '';

                        foreach ($activities['activity'] as $activity) {
                            if ($activity['action'] == 'sent') {
                                ++$unopen;
                            } elseif ($activity['action'] == 'open') {
                                if (empty($lastopen)) {
                                    $lastopen = substr($activity['timestamp'], 0, 10);
                                }
                                ++$open;
                            } elseif ($activity['action'] == 'click') {
                                if (empty($lastopen)) {
                                    $lastopen = substr($activity['timestamp'], 0, 10);
                                }
                                ++$click;
                            }

                            ++$total;
                        }

                        $data['mailchimp_activity']['total'] = $total;
                        $data['mailchimp_activity']['open'] = $open;
                        $data['mailchimp_activity']['unopen'] = $unopen;
                        $data['mailchimp_activity']['click'] = $click;
                        $data['mailchimp_activity']['lastopen'] = $lastopen;
                    }
                }
            }
        }

        echo view("customers/form", $data);
    }

    /**
     * Inserts/updates a customer
     */
    public function postSave(int $customer_id = NEW_ENTRY): void
    {
        $first_name = $this->request->getPost('first_name');
        $last_name = $this->request->getPost('last_name');
        $email = strtolower($this->request->getPost('email', FILTER_SANITIZE_EMAIL));

        // Format first and last name properly
        $first_name = $this->nameize($first_name);
        $last_name = $this->nameize($last_name);

        $person_data = [
            'first_name'   => $first_name,
            'last_name'    => $last_name,
            'gender'       => $this->request->getPost('gender', FILTER_SANITIZE_NUMBER_INT),
            'email'        => $email,
            'phone_number' => $this->request->getPost('phone_number'),
            'address_1'    => $this->request->getPost('address_1'),
            'address_2'    => $this->request->getPost('address_2'),
            'city'         => $this->request->getPost('city'),
            'state'        => $this->request->getPost('state'),
            'zip'          => $this->request->getPost('zip'),
            'country'      => $this->request->getPost('country'),
            'comments'     => $this->request->getPost('comments')
        ];

        $date_formatter = date_create_from_format($this->config['dateformat'] . ' ' . $this->config['timeformat'], $this->request->getPost('date'));

        $customer_data = [
            'consent'           => $this->request->getPost('consent') != null,
            'account_number'    => $this->request->getPost('account_number') == '' ? null : $this->request->getPost('account_number'),
            'tax_id'            => $this->request->getPost('tax_id'),
            'company_name'      => $this->request->getPost('company_name') == '' ? null : $this->request->getPost('company_name'),
            'discount'          => $this->request->getPost('discount') == '' ? 0.00 : parse_decimals($this->request->getPost('discount')),
            'discount_type'     => $this->request->getPost('discount_type') == null ? PERCENT : $this->request->getPost('discount_type', FILTER_SANITIZE_NUMBER_INT),
            'package_id'        => $this->request->getPost('package_id') == '' ? null : $this->request->getPost('package_id'),
            'taxable'           => $this->request->getPost('taxable') != null,
            'date'              => $date_formatter->format('Y-m-d H:i:s'),
            'employee_id'       => $this->request->getPost('employee_id', FILTER_SANITIZE_NUMBER_INT),
            'sales_tax_code_id' => $this->request->getPost('sales_tax_code_id') == '' ? null : $this->request->getPost('sales_tax_code_id', FILTER_SANITIZE_NUMBER_INT)
        ];

        if ($this->customer->save_customer($person_data, $customer_data, $customer_id)) {
            // Save customer to Mailchimp selected list    // TODO: addOrUpdateMember should be refactored. Potentially pass an array or object instead of 6 parameters.
            $mailchimp_status = $this->request->getPost('mailchimp_status');
            $this->mailchimp_lib->addOrUpdateMember(
                $this->_list_id,
                $email,
                $first_name,
                $last_name,
                $mailchimp_status == null ? "" : $mailchimp_status,
                ['vip' => $this->request->getPost('mailchimp_vip') != null]
            );

            // New customer
            if ($customer_id == NEW_ENTRY) {
                echo json_encode([
                    'success' => true,
                    'message' => lang('Customers.successful_adding') . ' ' . $first_name . ' ' . $last_name,
                    'id'      => $customer_data['person_id']
                ]);
            } else { // Existing customer
                echo json_encode([
                    'success' => true,
                    'message' => lang('Customers.successful_updating') . ' ' . $first_name . ' ' . $last_name,
                    'id'      => $customer_id
                ]);
            }
        } else { // Failure
            echo json_encode([
                'success' => false,
                'message' => lang('Customers.error_adding_updating') . ' ' . $first_name . ' ' . $last_name,
                'id'      => NEW_ENTRY
            ]);
        }
    }

    /**
     * Verifies if an email address already exists. Used in app/Views/customers/form.php
     *
     * @return void
     * @noinspection PhpUnused
     */
    public function postCheckEmail(): void
    {
        $email = strtolower($this->request->getPost('email', FILTER_SANITIZE_EMAIL));
        $person_id = $this->request->getPost('person_id', FILTER_SANITIZE_NUMBER_INT);

        $exists = $this->customer->check_email_exists($email, $person_id);

        echo !$exists ? 'true' : 'false';
    }

    /**
     * Verifies if an account number already exists. Used in app/Views/customers/form.php
     *
     * @return void
     * @noinspection PhpUnused
     */
    public function postCheckAccountNumber(): void
    {
        $exists = $this->customer->check_account_number_exists($this->request->getPost('account_number'), $this->request->getPost('person_id', FILTER_SANITIZE_NUMBER_INT));

        echo !$exists ? 'true' : 'false';
    }

    /**
     * This deletes customers from the customers table
     */
    public function postDelete(): void
    {
        $customers_to_delete = $this->request->getPost('ids');
        $customers_info = $this->customer->get_multiple_info($customers_to_delete);

        $count = 0;

        foreach ($customers_info->getResult() as $info) {
            if ($this->customer->delete($info->person_id)) {
                // remove customer from Mailchimp selected list
                $this->mailchimp_lib->removeMember($this->_list_id, $info->email);

                $count++;
            }
        }

        if ($count == count($customers_to_delete)) {
            echo json_encode([
                'success' => true,
                'message' => lang('Customers.successful_deleted') . ' ' . $count . ' ' . lang('Customers.one_or_multiple')
            ]);
        } else {
            echo json_encode(['success' => false, 'message' => lang('Customers.cannot_be_deleted')]);
        }
    }

    /**
     * Customers import from csv spreadsheet
     *
     * @return DownloadResponse The template for Customer CSV imports is returned and download forced.
     * @noinspection PhpUnused
     */
    public function getCsv(): DownloadResponse
    {
        $name = 'importCustomers.csv';
        $data = file_get_contents(WRITEPATH . "uploads/$name");
        return $this->response->download($name, $data);
    }

    /**
     * Displays the customer CSV import modal. Used in app/Views/people/manage.php
     *
     * @return void
     * @noinspection PhpUnused
     */
    public function getCsvImport(): void
    {
        echo view('customers/form_csv_import');
    }

    /**
     * Imports a CSV file containing customers. Used in app/Views/customers/form_csv_import.php
     *
     * @return void
     * @noinspection PhpUnused
     */
    public function postImportCsvFile(): void
    {
        if ($_FILES['file_path']['error'] != UPLOAD_ERR_OK) {
            echo json_encode(['success' => false, 'message' => lang('Customers.csv_import_failed')]);
        } else {
            if (($handle = fopen($_FILES['file_path']['tmp_name'], 'r')) !== false) {
                // Skip the first row as it's the table description
                fgetcsv($handle);
                $i = 1;

                $failCodes = [];

                while (($data = fgetcsv($handle)) !== false) {
                    $consent = $data[3] == '' ? 0 : 1;

                    if (sizeof($data) >= 16 && $consent) {
                        $email = strtolower($data[4]);
                        $person_data = [
                            'first_name'   => $data[0],
                            'last_name'    => $data[1],
                            'gender'       => $data[2],
                            'email'        => $email,
                            'phone_number' => $data[5],
                            'address_1'    => $data[6],
                            'address_2'    => $data[7],
                            'city'         => $data[8],
                            'state'        => $data[9],
                            'zip'          => $data[10],
                            'country'      => $data[11],
                            'comments'     => $data[12]
                        ];

                        $customer_data = [
                            'consent'       => $consent,
                            'company_name'  => $data[13],
                            'discount'      => $data[15],
                            'discount_type' => $data[16],
                            'taxable'       => $data[17] == '' ? 0 : 1,
                            'date'          => date('Y-m-d H:i:s'),
                            'employee_id'   => $this->employee->get_logged_in_employee_info()->person_id
                        ];
                        $account_number = $data[14];

                        // Don't duplicate people with same email
                        $invalidated = $this->customer->check_email_exists($email);

                        if ($account_number != '') {
                            $customer_data['account_number'] = $account_number;
                            $invalidated &= $this->customer->check_account_number_exists($account_number);
                        }
                    } else {
                        $invalidated = true;
                    }

                    if ($invalidated) {
                        $failCodes[] = $i;
                        log_message('error', "Row $i was not imported: Either email or account number already exist or data was invalid.");
                    } elseif ($this->customer->save_customer($person_data, $customer_data)) {
                        // Save customer to Mailchimp selected list
                        $this->mailchimp_lib->addOrUpdateMember($this->_list_id, $person_data['email'], $person_data['first_name'], '', $person_data['last_name']);
                    } else {
                        $failCodes[] = $i;
                    }

                    ++$i;
                }

                if (count($failCodes) > 0) {
                    $message = lang('Customers.csv_import_partially_failed', [count($failCodes), implode(', ', $failCodes)]);

                    echo json_encode(['success' => false, 'message' => $message]);
                } else {
                    echo json_encode(['success' => true, 'message' => lang('Customers.csv_import_success')]);
                }
            } else {
                echo json_encode(['success' => false, 'message' => lang('Customers.csv_import_nodata_wrongformat')]);
            }
        }
    }
}
