<?php

namespace KupShop\MailerLiteBundle;

use KupShop\KupShopBundle\Config;

class MailerLite
{
    public $api_key;
    public $groupID;
    public $version;
    public $group;
    public $error;
    public $limit = 5000;

    public const VERSION = 2;
    public $custom_fields = [
        ['Registered', 'TEXT'], ['Currency', 'TEXT'],
        ['User Groups', 'TEXT'], ['User Price Level', 'TEXT'],
        ['Greeting Name', 'TEXT'], ['Greeting Surname', 'TEXT'], ['Sex', 'TEXT'],
        ['Orders Count', 'NUMBER'], ['Orders Total', 'NUMBER'], ['Last Order Date', 'DATE'],
        ['Last Year Orders Count', 'NUMBER'], ['Last Year Orders Total', 'NUMBER'],
        ['Bonus Points', 'NUMBER', \Modules::BONUS_PROGRAM],
    ];

    public $webhooks = ['subscriber.unsubscribe' => '_mailerlite/webhook/'];

    public $upgrade_message;

    public function __construct()
    {
        $settings = \Settings::getDefault();
        if (isset($settings['mailerlite'])) {
            $this->api_key = $settings['mailerlite']['api_key'];
            $this->groupID = $settings['mailerlite']['group'];
            if (isset($settings['mailerlite']['version'])) {
                $this->version = $settings['mailerlite']['version'];
            }
        }
        if ($this->api_key && $this->groupID) {
        } else {
            $this->error = 'MailerLite modul je špatně nastavený!';
        }
    }

    public function getSubscriberGroup()
    {
        if ($this->api_key && $this->groupID) {
            $response = $this->execCurl('https://api.mailerlite.com/api/v2/groups/'.$this->groupID, 5);

            if (!$this->error) {
                $group_response = json_decode($response, true);
                if (!empty($group_response['error'])) {
                    $this->error = 'MailerLite: Group Error: '.$group_response['error']['message'];
                } else {
                    $this->group = $group_response;
                }
            }
        } else {
            $this->error = 'MailerLite modul je špatně nastavený!';
        }
    }

    public function upgradeCustomFields()
    {
        if ($this->error) {
            $this->upgrade_message[] = $this->error;
        }
        if ($this->api_key) {
            $response = $this->execCurl('https://api.mailerlite.com/api/v2/fields', 5);
            $fields = json_decode($response, true);
            if (!empty($fields['error'])) {
                $this->upgrade_message[] = 'MailerLite: Fields Error: '.$fields['error']['message'];
            } else {
                $fields_error = false;
                $fields_titles = array_flip(array_column($fields, 'title'));
                foreach ($this->custom_fields as $customField) {
                    if (isset($customField[2]) && !findModule($customField[2])) {
                        $this->upgrade_message[] = 'Field skipped - '.$customField[0].' - module not found - '.$customField[2].'; ';
                        continue;
                    }
                    if (!array_key_exists($customField[0], $fields_titles)) {
                        $new_field = '{"title": "'.$customField[0].'", "type": "'.$customField[1].'"}';
                        $response = $this->execCurl('https://api.mailerlite.com/api/v2/fields', 5, 'POST', $new_field);
                        $field_response = json_decode($response, true);
                        if (!empty($field_response['error'])) {
                            $fields_error = true;
                            $this->upgrade_message[] = 'MailerLite: Fields Error: '.$field_response['error']['message'];
                        } else {
                            $this->upgrade_message[] = 'New field created - '.$field_response['title'].', '.$field_response['id'].', '.$field_response['key'].'; ';
                        }
                    }
                }
                if (!$fields_error) {
                    $settings = \Settings::getDefault();
                    if ($this->version < self::VERSION) {
                        $mailerlite_settings = ['version' => self::VERSION];
                        $settings->updateValue('mailerlite', $mailerlite_settings);
                        $this->version = $settings['mailerlite']['version'];
                        $this->upgrade_message[] = 'MailerLite: version upgraded to \''.$this->version.'\' ';
                    }

                    return true;
                }
            }
        }

        return false;
    }

    public function upgradeWebhooks()
    {
        if ($this->error) {
            $this->upgrade_message[] = $this->error;
        }
        if ($this->api_key) {
            $response = $this->execCurl('https://api.mailerlite.com/api/v2/webhooks', 5);
            $webhooks = json_decode($response, true);
            if (!empty($webhooks['error'])) {
                $this->upgrade_message[] = 'MailerLite: Webhooks Error: '.$webhooks['error']['message'];
            } else {
                $webhooks_error = false;
                $domain = Config::get()['Addr']['full'];
                $webhooks = $webhooks['webhooks'];
                foreach ($webhooks as $webhook) {
                    $webhook_type = $webhook['event'];
                    if (array_key_exists($webhook_type, $this->webhooks)) {
                        $url = $domain.$this->webhooks[$webhook_type];
                        if ($webhook['url'] != $url) {
                            $response = $this->execCurl('https://api.mailerlite.com/api/v2/webhooks/'.$webhook['id'], 5, 'DELETE');
                            $webhook_response = json_decode($response, true);
                            if (!empty($webhook_response['error'])) {
                                $webhooks_error = true;
                                $this->upgrade_message[] = 'MailerLite: Webhooks Error: '.$webhook_response['error']['message'];
                            } else {
                                $this->upgrade_message[] = 'Webhook deleted - '.$webhook['id'].', '.$webhook_type.', '.$webhook['url'].'; ';
                            }
                        }
                    }
                }

                foreach ($this->webhooks as $event => $url) {
                    $new_webhook = '{"event": "'.$event.'", "url": "'.$domain.$url.'"}';
                    $response = $this->execCurl('https://api.mailerlite.com/api/v2/webhooks/', 5, 'POST', $new_webhook);
                    $webhook_response = json_decode($response, true);
                    if (!empty($webhook_response['error'])) {
                        $webhooks_error = true;
                        $this->upgrade_message[] = 'MailerLite: Webhooks Error: '.$webhook_response['error']['message'];
                    } else {
                        $this->upgrade_message[] = 'New webhook created - '.$webhook_response['id'].', '.$webhook_response['event'].', '.$webhook_response['url'].'; ';
                    }
                }

                return !$webhooks_error;
            }
        }

        return false;
    }

    public function execCurl($url, $timeout = 50, $http_method = 'GET', $post_data = null)
    {
        $curl = curl_init();

        curl_setopt_array(
            $curl,
            [
                CURLOPT_URL => $url,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_ENCODING => '',
                CURLOPT_MAXREDIRS => 10,
                CURLOPT_TIMEOUT => $timeout,
                CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
                CURLOPT_CUSTOMREQUEST => $http_method,
                CURLOPT_HTTPHEADER => [
                    'content-type: application/json',
                    'x-mailerlite-apikey: '.$this->api_key,
                ],
            ]
        );

        if ($post_data) {
            curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
        }

        $response = curl_exec($curl);
        $err = curl_error($curl);

        curl_close($curl);

        if ($err) {
            $this->error = 'MailerLite: cURL Error: '.$err;
        }

        return $response;
    }

    public function updateUser($user, $newsletter)
    {
        $mailerlite_data_array['email'] = $user->email;
        $cur_date = date('Y-m-d H:i:s');
        if ($newsletter == 'Y') {
            $mailerlite_data_array['type'] = 'active';
            $mailerlite_data_array['date_subscribe'] = $cur_date;

            $user_data = $this->getKupShopUsers([$user->id]);
            if ($user_data['subscribers_count'] == 1) {
                $mailerlite_data_array += reset($user_data['subscribers']);
            } elseif ($user_data['unsubscribed_count'] == 1) {
                $mailerlite_data_array += reset($user_data['unsubscribed']);
            }
            $mailerlite_data_array['resubscribe'] = true;
        } else {
            $mailerlite_data_array['type'] = 'unsubscribed';
            $mailerlite_data_array['date_unsubscribe'] = $cur_date;
        }

        $mailerlite_data = $this->prepareExportData($mailerlite_data_array);
        $response = $this->execCurl('https://api.mailerlite.com/api/v2/groups/'.$this->groupID.'/subscribers', 10, 'POST', $mailerlite_data);

        if ($this->error) {
            return false;
        }

        $subscriber_response = json_decode($response, true);
        if (!empty($subscriber_response['error'])) {
            $this->error = 'MailerLite: updateUser Error: '.$subscriber_response['error']['message'];

            return false;
        }

        return true;
    }

    public function getMailerLiteSubscribers($type = 'Unsubscribed', $page = null, $filter_date = null)
    {
        $data = [];

        $get_params = '?limit='.$this->limit;
        if ($filter_date && ($type == 'Unsubscribed')) {
            $get_params .= '&filters[date_unsubscribe][$gte]='.$filter_date;
            $data['filter_date'] = $filter_date;
        }
        if (!is_null($page)) {
            if ($page > 0) {
                $get_params .= '&offset='.($page * $this->limit);
            }
        }

        $response = $this->execCurl('https://api.mailerlite.com/api/v2/groups/'.$this->groupID.'/subscribers/'.strtolower($type).$get_params);

        if ($this->error) {
            $data['err'] = $this->error;
        } else {
            $subscribers = json_decode($response, true);
            if (is_null($subscribers)) {
                $data['err'] = 'MailerLite: Subscribers Error: '.$response;
            } else {
                if (!empty($subscribers['error'])) {
                    $data['err'] = 'MailerLite: Subscribers Error: '.$subscribers['error']['message'];
                } else {
                    $emails = array_column($subscribers, 'email');
                    $subscribers = array_combine($emails, $subscribers);
                    $data['subscribers_count'] = count($subscribers);
                    $data['subscribers'] = $subscribers;
                }
            }
        }

        return $data;
    }

    public function getMailerLiteSubscribers_All($type = 'Unsubscribed')
    {
        $data['subscribers_count'] = 0;
        $data['subscribers'] = [];
        $page = 0;
        $next_page = $this->getMailerLiteSubscribers($type, $page);
        while (!empty($next_page['subscribers_count']) && ($next_page['subscribers_count'] > 0)) {
            $data['subscribers_count'] += $next_page['subscribers_count'];
            $data['subscribers'] += $next_page['subscribers'];
            $page++;
            $next_page = $this->getMailerLiteSubscribers($type, $page);
        }
        if (!empty($next_page['err'])) {
            $data['err'] = $next_page['err'];
        }

        return $data;
    }

    public function MailerliteUnsubscribe($users_data)
    {
        $data = [];

        $cur_date = date('Y-m-d H:i:s');
        $batch_requests['requests'] = [];
        $i = 0;
        foreach ($users_data as $email => $value) {
            $i++;
            $mailerlite_request['method'] = 'POST';
            $mailerlite_request['path'] = '/api/v2/groups/'.$this->groupID.'/subscribers';
            $mailerlite_request['body']['email'] = $email;
            $mailerlite_request['body']['type'] = 'unsubscribed';
            $mailerlite_request['body']['date_unsubscribe'] = $cur_date;
            $batch_requests['requests'][] = $mailerlite_request;
            //            if ($i == 50) {
            //                break; // There is a limit of maximum 50 requests per single batch.
            //            }
        }
        /*        $batch_requests_json = json_encode($batch_requests);

                $response = $this->execCurl('https://api.mailerlite.com/api/v2/batch', 30, 'POST', $batch_requests_json);

                if ($this->error) {
                    $data['err'] = $this->error;
                } else {
                    $batch_response = json_decode($response, true);
                    if (!empty($batch_response['error'])) {
                        $data['err'] = 'MailerLite: Unsubscribe Error: '.$batch_response['error']['message'];
                    } else {
                        $data['success'] = $batch_response;
                    }
                }
        */
        //        if ($data['err']) {
        //            if (strstr($data['err'], 'A website error has occurred.')) {
        // 'batch' is not working - A website error has occurred. The website administrator has been notified of the issue. Sorry for the temporary inconvenience
        // send one by one
        $data['err'] = '';
        $unsubscribed_count = 0;
        foreach ($batch_requests['requests'] as $mailerlite_request) {
            $mailerlite_request_json = json_encode($mailerlite_request['body']);
            $response = $this->execCurl('https://api.mailerlite.com/api/v2/groups/'.$this->groupID.'/subscribers', 5, 'POST', $mailerlite_request_json);

            if ($this->error) {
                $data['err'] = $this->error;
            } else {
                $unsubscribe_response = json_decode($response, true);
                if (!empty($unsubscribe_response['error'])) {
                    $data['err'] = 'MailerLite: Unsubscribe Error: '.$unsubscribe_response['error']['message'];
                } else {
                    if ($unsubscribe_response['type'] == 'unsubscribed') {
                        $unsubscribed_count++;
                    }
                }
            }
            if ($data['err']) {
                break;
            }
        }
        if ($unsubscribed_count > 0) {
            $data['success'] = 'MailerliteUnsubscribe: '.$unsubscribed_count;
        }
        //            }
        //        }

        return $data;
    }

    public function MailerliteSubscribe($users_data)
    {
        $data = [];

        if (!empty($users_data)) {
            $subscribers = '{"subscribers":['.implode(',', $users_data).'], "resubscribe":true}';

            $response = $this->execCurl('https://api.mailerlite.com/api/v2/groups/'.$this->groupID.'/subscribers/import', 300, 'POST', $subscribers);

            if ($this->error) {
                $data['err'] = $this->error;
            } else {
                $export_result = json_decode($response, true);
                if (!empty($export_result['error'])) {
                    $data['err'] = 'MailerLite: Subscribe Error: '.$export_result['error']['message'];
                } else {
                    $export_success_str = 'Export: imported = '.count($export_result['imported'] ?? []);
                    $export_success_str .= ', updated = '.count($export_result['updated'] ?? []);
                    $export_success_str .= ', unchanged = '.count($export_result['unchanged'] ?? []);
                    $export_success_str .= ', errors = '.count($export_result['errors'] ?? []);
                    $data['success'] = $export_success_str;
                }
            }
        }

        return $data;
    }

    public function getKupShopUsers($user_ids = null)
    {
        $data = [];

        if (findModule(\Modules::CURRENCIES)) {
            $currency = 'u.currency';
        } else {
            $currency = "NULL AS 'currency'";
        }
        $sql = sqlQueryBuilder()
            ->select('u.id', 'LOWER(u.email) as email', 'u.get_news',
                'u.figure as `registered`', 'u.name', 'u.surname as `last_name`',
                'u.firm as `company`', 'u.date_reg', 'u.date_updated',
                'l.name as user_price_level', 'u.country', $currency,
                '(SELECT CONCAT("\"", GROUP_CONCAT(ug.name SEPARATOR "\", \""), "\"") 
    FROM users_groups_relations ugr LEFT JOIN users_groups ug ON ugr.id_group = ug.id
    WHERE ugr.id_user = u.id) AS user_groups',
                'COUNT(o.id) AS orders_count',
                'ROUND(SUM(o.total_price*o.currency_rate)) AS orders_total',
                'MAX(o.date_created) AS last_order_date',
                'SUM(o.date_created >= DATE_SUB(current_date, INTERVAL 1 YEAR)) as last_year_orders_count',
                'ROUND(SUM(o.total_price*o.currency_rate*(o.date_created >= DATE_SUB(current_date, INTERVAL 1 YEAR)))) as last_year_orders_total')
            ->from('users', 'u')
            ->leftJoin('u', 'users_dealer_price_level', 'udpl', 'u.id = udpl.id_user')
            ->leftJoin('u', 'price_levels', 'l', 'udpl.id_price_level = l.id')
            ->leftJoin('u', 'orders', 'o', 'u.email = o.invoice_email AND o.status_storno=0')
            ->groupBy('u.id')
            ->orderBy('u.date_updated', 'DESC');
        if (findModule(\Modules::BONUS_PROGRAM)) {
            $sql->addSelect('(SELECT SUM(bp.points)
    FROM bonus_points bp
    WHERE bp.id_user = u.id AND bp.status = "active") AS bonus_points');
        }
        if (!is_null($user_ids)) {
            $sql->where(\Query\Operator::inIntArray($user_ids, 'u.id'));
        }
        $qb = $sql->execute();

        $users = $qb->fetchAll();
        $users_emails = array_column($users, 'email');
        $users = array_combine($users_emails, $users);

        $subscribers = array_filter($users, function ($v) {return $v['get_news'] == 'Y'; });
        $unsubscribed = array_filter($users, function ($v) {return $v['get_news'] == 'N'; });
        $data['subscribers'] = $subscribers;
        $data['unsubscribed'] = $unsubscribed;
        $data['subscribers_count'] = count($data['subscribers']);
        $data['unsubscribed_count'] = count($data['unsubscribed']);

        return $data;
    }

    public function ShopUnsubscribe($users_data)
    {
        $data = [];

        if (!empty($users_data)) {
            $qb = sqlQueryBuilder()->update('users')
                ->set('get_news', ':news')->setParameter('news', 'N')
                ->set('date_unsubscribe', 'NOW()')
                ->set('date_updated', 'NOW()')
                ->where(\Query\Operator::inIntArray($users_data, 'id'))
                ->andWhere('get_news=\'Y\'')
                ->execute();

            $data['success'] = 'ShopUnsubscribe: '.$qb;
        }

        return $data;
    }

    public function ShopSubscribe($users_data)
    {
        $data = [];

        if (!empty($users_data)) {
            $qb = sqlQueryBuilder()->update('users')
                ->set('get_news', ':news')->setParameter('news', 'Y')
                ->where(\Query\Operator::inIntArray($users_data, 'id'))
                ->execute();

            $data['success'] = 'ShopSubscribe: '.$qb;
        }

        return $data;
    }

    public function Synchronize($filter_date = null)
    {
        $success = null;
        $error = null;
        if ($filter_date) {
            $data['Unsubscribed'] = $this->getMailerLiteSubscribers('Unsubscribed', null, $filter_date);
        } else {
            $data['Unsubscribed'] = $this->getMailerLiteSubscribers_All('Unsubscribed');
        }
        if (array_key_exists('err', $data['Unsubscribed'])) {
            $error[] = $data['Unsubscribed']['err'];
        }
        $data['Junk'] = $this->getMailerLiteSubscribers('Junk');
        if (!empty($data['Junk']['subscribers_count'])) {
            if ($error) {
                $data['Unsubscribed'] = $data['Junk'];
            } else {
                $data['Unsubscribed']['subscribers_count'] += $data['Junk']['subscribers_count'];
                $data['Unsubscribed']['subscribers'] += $data['Junk']['subscribers'];
            }
        }
        $data['Bounced'] = $this->getMailerLiteSubscribers('Bounced');
        if (!empty($data['Bounced']['subscribers_count'])) {
            if ($error && empty($data['Unsubscribed'])) {
                $data['Unsubscribed'] = $data['Bounced'];
            } else {
                $data['Unsubscribed']['subscribers_count'] += $data['Bounced']['subscribers_count'];
                $data['Unsubscribed']['subscribers'] += $data['Bounced']['subscribers'];
            }
        }

        $data['ShopUsers'] = $this->getKupShopUsers(); // all
        if (!empty($data['Unsubscribed']['subscribers_count'])) {
            $mailerlite_unsubscribed = $data['Unsubscribed']['subscribers'];
            $shop_users_subscribers = $data['ShopUsers']['subscribers'];
            $array_unsubscribe = array_intersect_key($shop_users_subscribers, $mailerlite_unsubscribed);
            if ($array_unsubscribe) {
                // Unsubscribe on Shop
                $ids = array_column($array_unsubscribe, 'id');
                $result = $this->ShopUnsubscribe($ids);
                if (!empty($result['success'])) {
                    $success[] = $result['success'];
                }
                if (!empty($result['error'])) {
                    $error[] = $result['error'];
                }
            }
        }

        $user_ids = null;
        if ($filter_date) {
            $sql = sqlQueryBuilder()
                ->select('u.id')
                ->from('users', 'u')
                ->where('u.date_updated >= "'.$filter_date.'" ');
            $qb = $sql->execute();
            $user_ids = $qb->fetchAll();
            $user_ids = array_column($user_ids, 'id');

            $sql = sqlQueryBuilder()
                ->select('DISTINCT o.id_user')
                ->from('orders', 'o')
                ->where('o.id_user IS NOT NULL')
                ->andWhere('(o.date_created >= "'.$filter_date.'") OR (o.date_created >= DATE_SUB(current_date, INTERVAL 370 DAY) AND o.date_created < DATE_SUB(current_date, INTERVAL 1 YEAR) )');
            $qb = $sql->execute();
            $user_ids2 = $qb->fetchAll();
            $user_ids2 = array_column($user_ids2, 'id_user');

            $user_ids = array_unique(array_merge($user_ids, $user_ids2));
        }
        $data['ShopUsers'] = $this->getKupShopUsers($user_ids);

        if ($data['ShopUsers']['subscribers_count']) {
            // Subscribe on MailerLite
            $shop_users_subscribers = $data['ShopUsers']['subscribers'];

            foreach (array_chunk($shop_users_subscribers, 10000) as $chunk) {
                $users_data = array_map([$this, 'prepareExportData'], $chunk);
                $result = $this->MailerliteSubscribe($users_data);
                if (!empty($result['success'])) {
                    $success[] = $result['success'];
                }
                if (!empty($result['error'])) {
                    $error[] = $result['error'];
                }
            }
        }

        if ($data['ShopUsers']['unsubscribed_count']) {
            $shop_users_unsubscribed = $data['ShopUsers']['unsubscribed'];
            $mailerlite_unsubscribed = $data['Unsubscribed']['subscribers'] ?? [];
            $array_unsubscribe = array_diff_key($shop_users_unsubscribed, $mailerlite_unsubscribed);
            if ($array_unsubscribe) {
                // Unsubscribe on MailerLite
                $result = $this->MailerliteUnsubscribe($array_unsubscribe);
                if (!empty($result['success'])) {
                    $success[] = $result['success'];
                }
                if (!empty($result['error'])) {
                    $error[] = $result['error'];
                }
            }
        }

        $result = null;
        if ($success) {
            $result['success'] = $success;
        }
        if ($error) {
            $result['error'] = $error;
        }

        if (empty($success) && empty($error)) {
            $result['success'][] = 'Nothing to Synchronize';
        }

        return $result;
    }

    public static function prepareExportData($subscriber)
    {
        $export_data = null;
        $export_data['email'] = $subscriber['email'];
        foreach ($subscriber as $key => $value) {
            if (in_array($key, ['email', 'type', 'date_subscribe', 'date_unsubscribe', 'resubscribe'])) {
                $export_data[$key] = $value;
            } else {
                $export_data['fields'][$key] = $value;
            }
        }
        $unsubscribed = (isset($subscriber['type']) && ($subscriber['type'] == 'unsubscribed'));
        if (!$unsubscribed) {
            $greeting = \Greeting::getGreeting($subscriber['name'], $subscriber['last_name'], 'name');
            $export_data['fields']['greeting_name'] = $greeting['greeting'];
            $export_data['fields']['sex'] = (!empty($greeting['sex']) ? $greeting['sex'] : '');
            $greeting = \Greeting::getGreeting($subscriber['name'], $subscriber['last_name'], 'surname');
            $export_data['fields']['greeting_surname'] = $greeting['greeting'];
        }

        $export_str = json_encode($export_data, 256);

        return $export_str;
    }
}
