<?php

use Slim\Container;
use Aura\SqlQuery\QueryFactory;
use Slim\Http\UploadedFile;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception as PException;

class API
{
    protected $address;
    protected $port;
    protected $cookieFile;
    protected $email;
    protected $password;
    protected $certificate;
    protected $db;
    protected $query_factory;
    protected $session;

    protected $uploadDirectory;

    // cookie file for client
    protected $cookieFileClient;
    protected $admin;
    protected $smtp;


    function __construct(Container $c = null)
    {
        global $settings;

        $api = $settings['settings']['api'];
        $this->address = $api['address'];
        $this->port = $api['port'];
        $this->cookieFile = $api['cookieFile'];
        $this->email = $api['email'];
        $this->password = $api['password'];
        $this->certificate = $api['certificate'];
        $this->admin = $settings["settings"]["admin"];

        $this->smtp = $settings["settings"]["smtp"];

        $this->uploadDirectory = $settings['settings']['upload']['path'];
        if ($c) {
            $this->db = $c->get('db');
            $this->query_factory = new QueryFactory('mysql');
            $this->session = $c->get('session');
        }
        // cookie file for client
        $this->cookieFileClient = "/tmp/cookieFileClient.txt";
    }

    function GetPublications()
    {
        $settings = $this->GetSettings()['data'];
        $disabledDates = $settings['dates']['disabledDates'];
        $disabledWeekDays = $settings['dates']['disabledWeekDays'];

        $ret = $this::Request('GET',
            '/publications?active=true&onwebsite=true&&order=position');

        foreach ($ret['data']['list'] as $id => $val) {
            $logo = $this->address . '/publications/' . $val['id'] . '/logo';
            $ret['data']['list'][$id]['image'] = $logo;

            $enabledDates = [];
            $pubDisabledDates = $disabledDates;
            if (!empty($val['enabledDates'])) {
                foreach ($val['enabledDates'] as $enabledDate) {
                    $enabledDates[] = $enabledDate['date'];
                }
            }

            $enabledWeekDays = [];
            $pubDisabledWeekDays = $disabledWeekDays;
            if (!empty($val['enabledWeekDays'])) {
                foreach ($val['enabledWeekDays'] as $enabledWeekDay) {
                    $enabledWeekDays[] = $enabledWeekDay['day'];
                }
            }

            $ret['data']['list'][$id]['disabledDates'] = array_values(
                array_diff($pubDisabledDates, $enabledDates)
            );

            $ret['data']['list'][$id]['disabledWeekDays'] = array_values(
                array_diff($pubDisabledWeekDays, $enabledWeekDays)
            );
        }

        $ret['data'] = $this::preserve(
            $ret['data']['list'], [
                'id', 'name', 'image', 'description', 'topics', 'circulation',
                'type', 'disabledDates', 'disabledWeekDays'
            ]
        );

        return $ret;
    }

    function GetSettings()
    {
        return $this::Request('GET', '/application/settings');
    }

    function Request($method, $path, $data = [],
                     $saveCookies = false, $upload = false)
    {

        $curl = curl_init();

        $opts = array(
            CURLOPT_URL => $this->address . $path,
            CURLOPT_CUSTOMREQUEST => $method,
            CURLOPT_PORT => $this->port,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => '',
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_NONE,
        );

        if (empty($this->certificate)) {
            $opts[CURLOPT_SSL_VERIFYPEER] = false;
        } else {
            $opts[CURLOPT_SSL_VERIFYPEER] = true;
            $opts[CURLOPT_SSL_VERIFYHOST] = 2;
            $opts[CURLOPT_CAINFO] = $this->certificate;
        }

        if ($saveCookies) {
            $opts[CURLOPT_COOKIEJAR] = $this->cookieFile;
        } else {
            $opts[CURLOPT_COOKIEFILE] = $this->cookieFile;
        }

        if (strtoupper($method) !== 'GET') {
            if ($upload) {
                $opts[CURLOPT_POSTFIELDS] = $data;
                $opts[CURLOPT_HTTPHEADER] = array(
                    'Content-Type: multipart/form-data',
                    'cache-control: no-cache'
                );
            } else {
                $opts[CURLOPT_POSTFIELDS] = json_encode($data);
                $opts[CURLOPT_HTTPHEADER] = array(
                    'Content-Type: application/json',
                    'cache-control: no-cache'
                );
            }
        }

        curl_setopt_array($curl, $opts);

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

        curl_close($curl);


        if ($path !== '/users/login' && $info['http_code'] == 401) {
            $ret = $this::Login();
            if ($ret['info']['http_code'] == 200) {
                return $this->Request($method, $path, $data, $saveCookies);
            }
        }

        //        var_dump($response);
        //        die();

        if ($info['http_code'] != 200) {
            throw new Exception('Internal API error');
        }

        return ['info' => $info, 'data' => json_decode($response, true)];
    }

    function Login()
    {
        return $this::Request('POST', '/users/login',
            ['email' => $this->email, 'password' => $this->password], true);
    }

    function preserve($arr, $keys, $add = array())
    {
        foreach ($arr as $id => $v) {
            foreach ($v as $key => $value) {
                if (!in_array($key, $keys)) {
                    unset($arr[$id][$key]);
                }
            }

            $arr[$id] = array_merge($arr[$id], $add);
        }

        return $arr;
    }

    function GetPublicationsExt($id)
    {
        $ret = $this::Request('GET',
            '/publications/' . $id . '/columns?active=true&order=name');

        $ret['data'] = $this::preserve($ret['data']['list'],
            ['id', 'name', 'options']);

        foreach ($ret['data'] as $id => $col) {
            if (!is_array($ret['data'][$id]['options'])) {
                continue;
            }

            $ret['data'][$id]['options'] = $this::preserve(
                $ret['data'][$id]['options'],
                ['id', 'name', 'description', 'columnID', 'active']
            );

            $options = [];
            foreach ($ret['data'][$id]['options'] as $oid => $o) {
                if ($o['active'] !== true) {
                    continue;
                }

                $options[] = $o;
            }

            $ret['data'][$id]['options'] = $options;
        }

        return $ret;
    }

    function MarkPayment($id, $amount, $transaction)
    {
        $ad = $this::Request('GET', '/ads/' . $id);
        if (empty($ad['data'])) {
            return false;
        }

        $ad = $ad['data'];
        if (empty($ad['invoiceValue']['total']) ||
            $ad['invoiceValue']['total'] != $amount) {
            return false;
        }
        if (empty($transaction)) {
            return false;
        }
        if (!empty($ad['transaction'])) {
            return true;
        }
        $ad['transaction'] = $transaction;

        $result = $this::Request('PUT', '/ads/' . $id, $ad);
        if (empty($result['data'])) {
            return false;
        }

        $ad = $this::Request('GET', '/ads/' . $id);
        if (empty($ad['data']['transaction']) ||
            $ad['data']['transaction'] != $transaction) {
            return false;
        }

        return true;
    }

    function GetAd($id)
    {
        return $this::Request('GET', '/ads/' . $id);
    }

    function CancelDraft($id)
    {
        return $this::Request('POST', '/ads/' . $id . '/cancel', '');
    }

    function CreateDraft($data)
    {
        $data['source'] = 2;
        $data['hasImage'] = false;
        $data['kind'] = 1;
        $data['type'] = 1;

        if (is_string($data['contact'])) {
            $data["contact"] = json_decode($data["contact"], true);
        }

        if (is_string($data['columns'])) {
            $data["columns"] = json_decode($data["columns"], true);
        }

        if (isset($data["invoicePerAd"])) {
            unset($data["invoicePerAd"]);
        }

        if (isset($data["selectedKind"])) {
            unset($data["selectedKind"]);
        }

        return $this::Request('POST', '/ads', $data);
    }

    function UploadImage($id, $data)
    {
        $this::Request('POST', '/ads/' . $id . '/images/display/upload',
            $data, false, true);

        return $this::Request('POST', '/ads/' . $id . '/images/print/upload',
            $data, false, true);
    }

    function AuthenticateUser($params = [])
    {
        if (!empty($params)) {
            $ret = $this::LoginUser($params);
            return $ret;
        }

        return [];
    }

    function LoginUser($credentials)
    {
        return $this::RequestClient('POST', '/users/login',
            ['email' => $credentials['email'], 'password' => $credentials['password']], false, true);
    }

    function RequestClient($method, $path, $data = [],
                           $upload = false, $withHeaders = false, $cookie = '')
    {

        $curl = curl_init();

        $opts = array(
            CURLOPT_URL => $this->address . $path,
            CURLOPT_CUSTOMREQUEST => $method,
            CURLOPT_PORT => $this->port,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => '',
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_NONE,
        );

        if ($withHeaders) {
            $opts[CURLOPT_HEADER] = 1;
        }
        {
        }

        if (empty($this->certificate)) {
            $opts[CURLOPT_SSL_VERIFYPEER] = false;
        } else {
            $opts[CURLOPT_SSL_VERIFYPEER] = true;
            $opts[CURLOPT_SSL_VERIFYHOST] = 2;
            $opts[CURLOPT_CAINFO] = $this->certificate;
        }


        if (strtoupper($method) !== 'GET') {
            if ($upload) {
                $opts[CURLOPT_POSTFIELDS] = $data;
                $opts[CURLOPT_HTTPHEADER] = array(
                    'Content-Type: multipart/form-data',
                    'cache-control: no-cache'
                );
            } else {
                $opts[CURLOPT_POSTFIELDS] = json_encode($data);
                $opts[CURLOPT_HTTPHEADER] = array(
                    'Content-Type: application/json',
                    'cache-control: no-cache'
                );
            }
        }

        if (strlen($cookie)) {
            $opts[CURLOPT_HTTPHEADER][] = "Cookie: rl=$cookie;";
        }


        curl_setopt_array($curl, $opts);

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

        if ($path == '/users/login') {
            preg_match('/Set-Cookie:\s*rl=([^;]+);/i', $response, $m);
            if (count($m)) {
                $cookies = $m[1];
                if (strlen($cookies)) {
                    $newR = explode("\n\r\n", $response);
                    $user = json_decode($newR[1]);

                    $select = $this->query_factory->newSelect();
                    $select->from('cookies')->cols([
                        'user_id'
                    ])
                        ->where("user_id = :user_id")
                        ->bindValues([
                            'user_id' => $user->id
                        ]);

                    $result = $this->db->fetchOne($select->getStatement(), $select->getBindValues());

                    if (!$result) {
                        $insert = $this->query_factory->newInsert();
                        $insert->into('cookies')->cols([
                            'cookie' => $cookies,
                            'user_id' => $user->id
                        ]);

                        $sth = $this->db->prepare($insert->getStatement());
                        $sth->execute($insert->getBindValues());
                    } else {
                        $update = $this->query_factory->newUpdate();
                        $update->table('cookies')
                            ->cols([
                                'cookie'
                            ])
                            ->where('user_id = :user_id')
                            ->bindValues([
                                'user_id' => $user->id,
                                'cookie' => $cookies
                            ]);
                        $stmt = $this->db->prepare($update->getStatement());
                        $stmt->execute($update->getBindValues());
                    }

                    $exists = $this->session->exists('user_id');

                    if (!$exists) {
                        $this->session->set('user_id', $user->id);
                    }

                }


            }
        }

        curl_close($curl);

        return ['info' => $info, 'data' => json_decode($response, true)];
    }

    function AddQuote($params, $file)
    {
        if (count($params) && !empty($file) && $this->session->exists('user_id')) {

            if ($file->getError() === UPLOAD_ERR_OK) {
                $filename = $this->moveUploadedFile($this->uploadDirectory, $file);
            }

            $insert = $this->query_factory->newInsert();
            $insert->into("quotes")
                ->cols([
                    "user_id" => $this->session->user_id,
                    "created_at" => date("Y-m-d H:i:s"),
                    "status" => 1,
                    "price" => NULL,
                    "details" => json_encode($params),
                    "file" => $filename
                ]);

            $sth = $this->db->prepare($insert->getStatement());
            $sth->execute($insert->getBindValues());

            $contact = json_decode($params["contact"], true);

            // TODO de pus email-ul clientului aici
            $this::SendMail($this->admin["email"], $contact['email'], "Cotatie adaugata", "O noua cotatie a fost adaugata");


            return true;
        }

        return false;
    }

    function moveUploadedFile($directory, UploadedFile $uploadedFile)
    {
        $basename = bin2hex(openssl_random_pseudo_bytes(8));
        $filename = sprintf('%s', $basename . '_' . $uploadedFile->getClientFilename());
        $uploadedFile->moveTo($directory . DIRECTORY_SEPARATOR . $filename);

        return $filename;
    }

    function SendMail($from, $to, $subject, $body, $altBody = '')
    {
        $mail = new PHPMailer(true);
        try {
            //Server settings
            $mail->isSMTP();                                      // Set mailer to use SMTP
            $mail->Host = $this->smtp["host"];  // Specify main and backup SMTP servers
            $mail->SMTPAuth = true;                               // Enable SMTP authentication
            $mail->Username = $this->smtp["username"];                 // SMTP username
            $mail->Password = $this->smtp["password"];                           // SMTP password
            $mail->SMTPSecure = $this->smtp["secure"];                            // Enable TLS encryption, `ssl` also accepted
            $mail->Port = $this->smtp["port"];                                    // TCP port to connect to

            //Recipients
            $mail->setFrom($from);
            $mail->addAddress($to);     // Add a recipient
            //Content
            $mail->isHTML(true);                                  // Set email format to HTML
            $mail->Subject = $subject;
            $mail->Body = $body;
            $mail->AltBody = $altBody;
            $mail->send();
        } catch (PException $e) {
            echo 'Message could not be sent. Mailer Error: ', $mail->ErrorInfo;
        }
    }

    function GetUserQuotes($params = [])
    {
        if ($this->session->exists("user_id")) {
            $select = $this->query_factory->newSelect();
            $select->from('quotes')->cols(["*"])->where('user_id = :user_id')->orderBy(['status', 'created_at desc'])
                ->bindValues([
                    "user_id" => $this->session->user_id
                ]);

            $limit = isset($params['limit']) ? $params['limit'] : '';
            $offset = isset($params['offset']) ? $params['offset'] : 0;

            if (!empty($offset) && is_numeric($offset)) {
                $select->offset($offset);
            }

            if (!empty($limit) && is_numeric($limit)) {
                $select->limit($limit);
            }

            $results['quotes'] = $this->db->fetchAll($select->getStatement(), $select->getBindValues());

            $select->resetCols()->cols(['count(*) as total'])->bindValues([
                "user_id" => $this->session->user_id
            ])->limit(0)->offset(0);

            $total = $this->db->fetchOne($select->getStatement(), $select->getBindValues());

            $results['total'] = $total['total'];

            return $results;
        }
        return false;
    }

    function GetAdminQuotes($params = [])
    {
        if ($this->session->exists('user_id')) {
            $isAdmin = $this::CheckAdmin();
            if ($isAdmin) {
                $select = $this->query_factory->newSelect();
                $select->from('quotes')->cols(["*"])->orderBy(['status', 'created_at desc']);

                $limit = isset($params['limit']) ? $params['limit'] : '';
                $offset = isset($params['offset']) ? $params['offset'] : 0;

                if (!empty($offset) && is_numeric($offset)) {
                    $select->offset($offset);
                }

                if (!empty($limit) && is_numeric($limit)) {
                    $select->limit($limit);
                }

                $results['quotes'] = $this->db->fetchAll($select->getStatement());

                $select->resetCols()->cols(['count(*) as total'])->limit(0)->offset(0);

                $total = $this->db->fetchOne($select->getStatement());

                $results['total'] = $total['total'];


                return $results;
            }
        }
    }

    function CheckAdmin()
    {
        if ($this->session->exists('user_id')) {
            $userProfile = $this::Request('GET', '/users/' . $this->session->user_id);
            if ($userProfile['info']['http_code'] == 200) {
                return $userProfile['data']['admin'];
            }

            return false;
        }
        return false;
    }

    function GetQuote($quoteId)
    {
        if ($quoteId && $this->session->exists('user_id')) {

            $params = [
                "id" => (int)$quoteId,
            ];


            $select = $this->query_factory->newSelect();
            $select->from('quotes')->cols(["*"])
                ->where('id = :id');

            if (!$this::CheckAdmin()) {
                $params["user_id"] = $this->session->user_id;
                $select->where('user_id = :user_id');
            }

            $select->bindValues($params);

            $quote = $this->db->fetchOne($select->getStatement(), $select->getBindValues());

            if ($quote) {
                return $quote;
            }

            return false;
        }

        return false;
    }

    function UpdateQuote($params = [])
    {
        if (!empty($params) && isset($params['id'])) {
            $isAdmin = $this::CheckAdmin();
            $cols = ["status"];

            if (isset($params["price"]) || !empty($params['adText'])) {
                if ($isAdmin) {
                    $cols[] = "price";
                } else {
                    unset($params["price"]);
                }
            }

            if (!in_array($params["status"], [2, 3, 7]) && !empty($params["fromUser"]) && $params["fromUser"]) {
                return false;
            }

            if (isset($params["fromUser"])) {
                unset($params["fromUser"]);
            }


            $select = $this->query_factory->newSelect();
            $select->from("quotes")->cols(["*"])
                ->where('id = :id')
                ->bindValues([
                    "id" => $params["id"]
                ]);

            $quote = $this->db->fetchOne($select->getStatement(), $select->getBindValues());

            $quoteDetails = json_decode($quote["details"], true);

            if (!empty($params["adText"])) {
                $quoteDetails["text"] = $params["adText"];
                $quoteDetails["columns"] = json_decode($quoteDetails["columns"]);
                $quoteDetails["contact"] = json_decode($quoteDetails["contact"], true);
                $price = $this::GetPrice($quoteDetails);
                $params['price'] = $price['data']['price']['total'];
                unset($params['adText']);
                $params['details'] = json_encode($quoteDetails);
                $cols[] = 'details';
            }
            if ($params["status"] == 3) {
                // $quoteDetails["group_id"] = 8;
                $data = $this::ClientCreateDraft($quoteDetails);

                if (empty($data['data']['id'])) {
                    throw new Exception('Could not create ad');
                }

                $ret = array('ad_id' => $data['data']['id']);
                $quoteDetails['ad_id'] = $ret['ad_id'];
                $cols[] = 'details';
                $params['details'] = json_encode($quoteDetails);
            }

            if (!empty($quoteDetails["invoicePerAd"]) && $quoteDetails["invoicePerAd"] != 0 && $params["status"] == 3) {
                $params["status"] = 6;
            }

            $update = $this->query_factory->newUpdate();
            $update->table('quotes')
                ->cols($cols);

            if (!$isAdmin && empty($params["payment"])) {
                $update->where('user_id = :user_id');
                $params["user_id"] = $this->session->user_id;
            }

            $isPayment = !empty($params["payment"]);

            if ($isPayment) {
                unset($params["payment"]);
            }


            $update->where('id = :id')
                ->bindValues($params);

            $stmt = $this->db->prepare($update->getStatement());
            $stmt->execute($update->getBindValues());

            if ($params["status"] == 2 && $isAdmin) {
                $this::SendMail($this->admin["email"], $quoteDetails["contact"]["email"], "Cotatia calculata", "Cotatia a fost calculata la pretul de: " . $params["price"]);
            }

            if (($params["status"] == 3 || $params["status"] == 6) && !$isAdmin) {
                $this::SendMail($quoteDetails["contact"]["email"], $this->admin["email"], "Cotatia Acceptata", "Cotatia a fost acceptata");
            }

            if ($params["status"] == 7 && !$isAdmin) {
                $this::SendMail($quoteDetails["contact"]["email"], $this->admin["email"], "Cotatia Refuzata", "Cotatia a fost refuzata");
            }

            if ($params["status"] == 4 && $isPayment) {
                $this::SendMail($quoteDetails["contact"]["email"], $this->admin["email"], "Cotatia Platita ", "Cotatia a fost platita");
            }

            return true;
        }

        return false;
    }

    function GetPrice($data)
    {
        $data['source'] = 2;
        $data['hasImage'] = false;
        $data['type'] = 1;

        if (empty($data["selectedKind"])) {
            $data['kind'] = 1;
        } else {
            $data['kind'] = (int)$data["selectedKind"];
        }

        if (!empty($data["groupID"])) {
            $data["groupID"] = (int)$data["groupID"];
        }

        $ret = $this::Request('POST', '/ads/summary', $data);

        $nw = array('columns' => array());
        foreach ($ret['data']['columns'] as $column) {
            array_push($nw['columns'], array(
                'id' => $column['publicationID'],
                'price' => $column['invoiceValue'],
            ));
        }

        $nw['words'] = $ret['data']['words'];
        $nw['price'] = $ret['data']['invoiceValue'];

        $ret['data'] = $nw;
        return $ret;
    }

    function ClientCreateDraft($data)
    {
        $data['source'] = 2;
        $data['hasImage'] = false;
        $data['type'] = 1;
        if (is_string($data['contact'])) {
            $data["contact"] = json_decode($data["contact"], true);
        }

        if (is_string($data['columns'])) {
            $data["columns"] = json_decode($data["columns"], true);
        }

        if (isset($data["invoicePerAd"])) {
            unset($data["invoicePerAd"]);
        }


        if (isset($data["selectedKind"])) {
            $data["kind"] = (int)$data["selectedKind"];
            unset($data["selectedKind"]);
        }

        if (!empty($data["groupID"])) {
            $data["groupID"] = (int)$data["groupID"];
            $data["groupUserID"] = $data["groupID"];
        }

        $userCookie = $this::GetUserCookie($this->session->user_id)['cookie'];

        $profile = $this::FetchUserProfile();
        $data["group_id"] = $profile["data"]["group"]['id'];
        $response = $this::RequestClient('POST', '/ads', $data, false, false, $userCookie);

        return $response;
    }

    function GetUserCookie($userId)
    {
        $select = $this->query_factory->newSelect();
        $select->from('cookies')->cols([
            'cookie'
        ])
            ->where("user_id = :user_id")
            ->bindValues([
                'user_id' => $userId
            ]);

        return $this->db->fetchOne($select->getStatement(), $select->getBindValues());
    }

    function FetchUserProfile()
    {
        if ($this->session->exists('user_id')) {
            $userCookie = $this::GetUserCookie($this->session->user_id)['cookie'];
            $rsp = $this::RequestClient('GET', '/users/profile', [], false, false, $userCookie);
            return $rsp;
        }

        return false;
    }

    function DeleteQuote($id)
    {

        if ($this->session->exists('user_id')) {
            $delete = $this->query_factory->newDelete();
            $delete->from('quotes')->where('user_id = :user_id')
                ->where('id = :id')
                ->where('status = :status')
                ->bindValues([
                    "user_id" => $this->session->user_id,
                    "id" => $id,
                    "status" => 7
                ]);
            $stmt = $this->db->prepare($delete->getStatement());
            $stmt->execute($delete->getBindValues());

            return true;
        }

        return false;
    }

    function QuoteStatuses()
    {
        return [
            1 => 'Cotatie noua',
            2 => 'Calculata',
            3 => 'Acceptata',
            4 => 'Platit + Anunt preluat',
            5 => 'Eroare tranzactie',
            6 => 'Anunt preluat',
            7 => 'Refuzata',
            8 => 'Canceled'
        ];
    }

    function AdTypes()
    {
        return [
            1 => 'Simplu',
            2 => 'LTD',
            3 => 'UNEJ'
        ];
    }

    function RegisterUser($params)
    {
        // TODO validation on backend
        $data = [
            "active" => true,
            "admin" => false,
            "password" => $params["password"],
            "email" => $params["email"],
            // TODO get the group id from config file
            "groupID" => 32,
            "contactID" => 0,
            "contact" => [
                "active" => true,
                "address" => $params["address"],
                "mobile" => $params["phone"],
                "email" => $params["email"],
                "firstName" => $params["firstName"],
                "lastName" => $params["lastName"],
                "nin" => $params["nin"],
                "type" => 1
            ]
        ];

        return $this->Request('POST', '/users', $data);
    }
}

?>
