<?php
class UsersModel{
    /**
     * @return array with all the users available from the webshop
     */
    public function getUsers() {
        global $database;

        $sql = "SELECT
                  u.id AS idUser,
                  info.name,
                  info.phone,
                  u.mail,
                  ut.type,
                  u.username,
                  u.isCompanyAdmin
                FROM
                  (SELECT
                    c.idUser,
                    c.name,
                    c.phone
                  FROM
                    ".TABLES['customers']." c
                  UNION
                  SELECT
                    cl.idUser,
                    cl.name,
                    cl.phone
                  FROM
                    ".TABLES['commercial_leads']." cl
                  UNION
                  SELECT
                    b.idUser,
                    b.name,
                    b.phone
                  FROM
                    ".TABLES['brokers']." b
                  UNION
                  SELECT
                    s.idUser,
                    s.name,
                    s.phone
                  FROM
                  ".TABLES['suppliers']." s) info
                  INNER JOIN ".TABLES['rel_user_type']." rut
                    ON rut.idUser = info.idUser
                  INNER JOIN ".TABLES['user_types']." ut
                    ON ut.id = rut.idType
                  INNER JOIN users u
                    ON u.id = info.idUser
                  ORDER BY name";

        $query = $database->query($sql);
        while($row = $database->fetchArray($query)) {
            $row['type'] = ucfirst($row['type']);
            $row['type'] = str_replace('_', ' ', $row['type']);
            $data[$row['type']][] = $row;
        }

        return $data;
    }

    /**
     * gets all the user tyeps/roles from the DB
     * @return Array of user types
     */
    public function getUserTypes() {
        global $database;
        $data=[];

        $sql = "SELECT
                  id,
                  type AS name
                FROM ".TABLES['user_types']."
        ";

        $query = $database->query($sql);
        while($row = $database->fetchArray($query)){
            $row['name'] = ucfirst($row['name']);
            $row['name'] = str_replace('_', ' ', $row['name']);
            $data[] = $row;
        }

        return $data;
    }

    /**
     * gets all the commercial leads from the DB
     * @return Array with all the commercial leads
     */
    public function getCommercialLeads() {
        global $database;

        $sql = "SELECT
                   id,
                   name
                 FROM ".TABLES['commercial_leads']."
        ";

        return $database->fetchResultArray($sql);
    }

    /**
     * @param info - array with all the details regarding new user
     * @param commercialLeads - array with all the commercial leads
     * @return Array array with the code and the message of confirmation for adding the user in the DB
     */
    public function saveUserInDB($info, $commercialLeads = '') {
        global $database;
        $info = (array) json_decode($info);
        $commercialLeads = json_decode($commercialLeads);
        $data = [];

        foreach($info as $key => $value) {
            $info['$key'] = $database->escapeValue($value);
            $checkMessage = $database->isEmpty($key, $value);
            if($checkMessage){
                $data['messages'][] = $checkMessage;

                return $data;
            }
        }

        $messageData = $this->validateUserData($info, $commercialLeads);
        if(!empty($messageData)){
            return $messageData;
        }

        $token = bin2hex(random_bytes(16));
        $idUser = $this->getInsertedIdForUsers($info, $token);
        if(is_array($idUser)) {
            return $idUser;
        }

        if($data = $this->insertUserTypeRelation($idUser, $info['idUserType'])) {
            return $data;
        }

        $data = $this->insertUserInfo($idUser, $info, $commercialLeads);
        $info['type'] = $this->getUserTypeByIdForMail($info['idUserType']);
        $data['messages'][] = UtilsModel::sendUserConfirmationMail($info, $info['mail'], 'create', $token);

        return $data;
    }

    /**
     * get user type by id for mail
     * @param Int $idUserType   the id of the user type
     * @return String 'customer' or 'other' based on the user type
     */
    private function getUserTypeByIdForMail($idUserType) {
        global $database;

        $sql = "SELECT type FROM ".TABLES['user_types']." ut WHERE ut.id=".$idUserType;
        $result = $database->fetchResultArray($sql);

        return $result && $result[0]['type'] === USER_TYPES['CUSTOMER'] ? 'customer' : 'other';
    }

    /**
     * validate user data from GUI
     * @param  Array $info             all information about the user to be inserted
     * @param  Array $commercialLeads  all the commercial leads linked to a customer
     * @return Array                   empty or error message
     */
    private function validateUserData($info, $commercialLeads) {
        global $database;
        $data = [];

        if(!$info['idUserType']) {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'SELECT_USER_TYPE'
            ];

            return $data;
        }

        if($info['idUserType'] === '2' && empty($commercialLeads)) {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'NO_COMMERCIAL_LEAD_LINK'
            ];

            return $data;
        }

        if(!isset($info['name']) || empty($info['name'])) {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'ADD_NAME'
            ];

            return $data;
        }
        $checkMessage = $database->invalidLength('name', $info['name'], 70);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        if(array_key_exists('selectedCompanyId', $info)) {
            if(!$info['selectedCompanyId']) {
                $data['messages'][] = [
                    'code' => 'error',
                    'message' => 'COMPANY_EMPTY'
                ];

                return $data;
            }
        }

        if(!array_key_exists('selectedCompanyId', $info)) {
            if(!isset($info['companyName']) || empty($info['companyName'])) {
                $data['messages'][] = [
                    'code' => 'error',
                    'message' => 'ADD_COMPANY_NAME'
                ];

                return $data;
            }
            $checkMessage = $database->invalidLength('companyName', $info['companyName'], 100);
            if($checkMessage){
                $data['messages'][] = $checkMessage;
            }

            if(!isset($info['vat']) || empty($info['vat'])) {
                $data['messages'][] = [
                    'code' => 'error',
                    'message' => 'ADD_VAT'
                ];

                return $data;
            }
        }

        if(!isset($info['phone']) || empty($info['phone'])) {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'ADD_PHONE'
            ];

            return $data;
        }
        if(!preg_match('/^([0-9\(\)\/\+ \-]*)$/', $info['phone'])){
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'INVALID_PHONE_NUMBER'
            ];
        }

        $checkMessage = $database->invalidLength('phone', $info['phone'], 40);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        if(!isset($info['username']) || empty($info['username'])) {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'ADD_USERNAME'
            ];

            return $data;
        }
        $checkMessage = $database->invalidLength('username', $info['username'], 20);
        if($checkMessage){
            $data['messages'][] = $checkMessage;
        }

        if(!preg_match('/^[a-zA-Z\d\.\-_]+$/',$info['username'])) {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'INVALID_USERNAME'
            ];
        }

        $sql = "SELECT username
                    FROM ".TABLES['users']."
                WHERE username='".$info['username']."'
                LIMIT 1";
        $result = $database->query($sql);
        if($database->numRows($result) > 0) {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'USERNAME_EXISTS'
            ];
        }

        if(!isset($info['mail']) || empty($info['mail'])) {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'ADD_MAIL'
            ];

            return $data;
        }
        if(!filter_var($info['mail'], FILTER_VALIDATE_EMAIL)){
            $data['messages'][] = [
                    'code' => 'error',
                    'message' => 'INVALID_MAIL'
            ];
        }

        return $data;
    }

    /**
     * inserts username, password and mail into users table
     * @param  String $info     the data array with all user info
     * @param  String $token    the token for newly created user
     * @return Int              the id of the user inserted
     */
    private function getInsertedIdForUsers($info, $token) {
        global $database, $user;
        $tokenTimestamp = new DateTime();

        if(!array_key_exists('selectedCompanyId', $info)) {
            $idCompany = $this->insertCompanyAndReturnId($info['companyName'], $info['vat']);

            if(is_array($idCompany)) {
                return $idCompany;
            }
        } else {
            $idCompany = $info['selectedCompanyId'];
        }
        $isCompanyAdmin = array_key_exists('companyAdmin', $info) && $info['companyAdmin'] ? 1 : 0;

        $sql = "INSERT INTO ".TABLES['users']." (idCompany, username, mail, token, tokenTS, isCompanyAdmin)
                VALUES (
                    $idCompany,
                    '".$info['username']."',
                    '".$info['mail']."',
                    '".$token."',
                    '".$tokenTimestamp->format('Y-m-d H:i:s')."',
                    $isCompanyAdmin
                )";
        $result = $database->query($sql);

        return $database->getInsertId();
    }

    /**
     * inserts the new company data
     * @param  String $name the name of the company
     * @param  String $vat  the vat code for the company
     * @return Int          the id of the company inserted
     */
    private function insertCompanyAndReturnId($name, $vat) {
        global $database;

        $sql = "SELECT name FROM ".TABLES['company']." WHERE name='$name'";
        $query = $database->query($sql);
        if($database->numRows($query)) {
            $data['messages'][] = [
                'code' => 'warning',
                'message' => 'COMPANY_EXISTS'
            ];

            return $data;
        }

        $sql = "INSERT INTO ".TABLES['company']."
                    (vatCode, name)
                VALUES
                    ('$vat', '$name')
        ";
        $result = $database->query($sql);

        return $database->getInsertId();
    }

    /**
     * inserts the relation between user and user type
     * @param  Int $idUser id of the user inserted
     * @param  Int $idType id of the user type to be inserted
     * @return Array       empty or error message
     */
    private function insertUserTypeRelation($idUser, $idType) {
        global $database;
        $data = [];

        $sql = "INSERT INTO ".TABLES['rel_user_type']."
                VALUES(
                    $idUser,
                    $idType
                )";
        $result = $database->query($sql);

        if(!$database->affectedRows()) {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'ERROR_USER_TYPE'
            ];
        }

        return $data;
    }

    /**
     * insert user information
     * @param  Int $idUser             id of the user inserted
     * @param  Array $info             all the information needed for the user
     * @param  Array $commercialLeads  commercial leads to link to customer
     * @return Array                   confirmation message
     */
    private function insertUserInfo($idUser, $info, $commercialLeads) {
        global $database;
        $data = [];
        $shouldLinkCommercialLeads = false;

        switch ($info['idUserType']) {
            // broker
            case '1':
                $table = TABLES['brokers'];
                break;
            // customer
            case '2':
                $table = TABLES['customers'];
                $shouldLinkCommercialLeads = true;
                break;
            // commercial lead
            case '3':
                $table = TABLES['commercial_leads'];
                break;
            // supplier
            case '4':
                $table = TABLES['suppliers'];
                break;
            default:
                break;
        }

        $sql = "INSERT INTO $table (idUser, name, phone)
                VALUES (
                    ".$idUser.",
                    '".$info['name']."',
                    '".$info['phone']."'
                    )";
        $result = $database->query($sql);
        if($database->affectedRows()) {
            $data['messages'][] = [
                'code' => 'success',
                'message' => 'USER_INSERTED'
            ];
        } else {
            $data['messages'][] = [
                'code' => 'error',
                'message' => 'ERROR_USER_INFO'
            ];
        }

        if($shouldLinkCommercialLeads) {
            $idCustomer = $database->getInsertId();
            $values = '';
            foreach($commercialLeads as $commercialLead) {
                $values .= "(".$commercialLead->id.", $idCustomer),";
            }
            $values = rtrim($values, ',');

            $sql = "INSERT INTO ".TABLES['rel_commercial_lead_customers']."
                        (idCommercialLead, idCustomer)
                    VALUES $values";
            $result = $database->query($sql);

            if(!$database->affectedRows()) {
                $data['messages'][] = [
                    'code' => 'error',
                    'message' => 'ERROR_USER_CL'
                ];
            }
        }

        return $data;
    }

    /**
     * get customers linked to comemrcial leads
     * @return Array list of customers grouped by commercial lead id
     */
    private function getCommercialLeadsCustomers(){
        global $database;
        $data = [];

        $sql = "SELECT
                    rclc.idCommercialLead,
                    rclc.idCustomer as id,
                    c.name
                FROM ".TABLES['rel_commercial_lead_customers']." rclc
                INNER JOIN ".TABLES['customers']." c
                    ON c.id=rclc.idCustomer
                WHERE rclc.isLinkEnabled=1
                ORDER BY name";
        $query = $database->query($sql);
        while($row = $database->fetchArray($query)){
            $data[$row['idCommercialLead']][] = $row;
        }

        return $data;
    }

    /**
     * get all customers and commercial leads in the system (included linked custoemrs to commercial lead)
     * @return Array list of customers and list of commercial leads
     */
    public function getCustomersAndCl() {
        global $database;
        $data = [];
        $clCustomers = $this->getCommercialLeadsCustomers();

        $sql = "SELECT
                    c.id AS id,
                    c.name AS name,
                    'customers' AS userType
                FROM ".TABLES['customers']." c
                UNION ALL
                SELECT
                    cl.id AS id,
                    cl.name AS name,
                    'commercialLeads' AS userType
                FROM ".TABLES['commercial_leads']." cl
                ORDER BY userType, name";
        $query = $database->query($sql);
        while($row = $database->fetchArray($query)){
            if($row['userType'] === 'commercialLeads'){
                $row['linkedCustomers'] = isset($clCustomers[$row['id']]) ? $clCustomers[$row['id']] : [];
            }
            $data[$row['userType']][] = $row;
        }

        return $data;
    }

    /**
     * update customers for a commercial lead
     * @param  INT $idCommercialLead id for the commercial lead
     * @param  Array $customers       list of customers to be linked
     * @return Array                  update message
     */
    public function updateLinkedCustomers($idCommercialLead, $customers){
        global $database;
        $data = [];
        $idCommercialLead = $database->escapeValue($idCommercialLead);
        $customers = json_decode($customers);

        if(intval($idCommercialLead) == 0){
            $data['messages'][] = [
                    'code' => 'error',
                    'message' => 'INVALID_COMMERCIAL_LEAD'
            ];
        }

        $sqlCustomers = "SELECT rclc.idCustomer
                FROM ".TABLES['rel_commercial_lead_customers']." rclc
                WHERE idCommercialLead=$idCommercialLead";
        $query = $database->query($sqlCustomers);
        $availableCustomers = [];
        while($row = $database->fetchArray($query)){
            $availableCustomers[] = $row['idCustomer'];
        }

        $sqlIns = "";
        $customersToUpdate = [];
        $updated = 0;
        foreach ($customers as $customer) {
            if(!in_array($customer->id, $availableCustomers)){
                $customer->id = $database->escapeValue($customer->id);
                $sqlIns .= "($idCommercialLead, ".$customer->id."),";
            }

            $customersToUpdate[] = $customer->id;
        }
        $sqlIns = rtrim($sqlIns, ',');

        if(!empty($sqlIns)){
            $sql = "INSERT IGNORE INTO ".TABLES['rel_commercial_lead_customers']."
                   (idCommercialLead, idCustomer)
                   VALUES $sqlIns";
            $query = $database->query($sql);
            $updated += $database->affectedRows();
        }

        if(!empty($customersToUpdate)){
            $customersToUpdate = implode(',', $customersToUpdate);
            $sqlUpd = "UPDATE ".TABLES['rel_commercial_lead_customers']."
                      SET isLinkEnabled=1
                      WHERE idCommercialLead=$idCommercialLead AND idCustomer IN($customersToUpdate)";
            $query = $database->query($sqlUpd);
            $updated += $database->affectedRows();

            $sqlUnlink = "UPDATE ".TABLES['rel_commercial_lead_customers']."
                          SET isLinkEnabled=0
                          WHERE idCommercialLead=$idCommercialLead AND idCustomer NOT IN($customersToUpdate)";
            $query = $database->query($sqlUnlink);
            $updated += $database->affectedRows();
        }


        if($updated > 0){
            $data['messages'][] = [
                'code' => 'success',
                'message' => 'CUSTOMERS_LINKED_TO_CL'
            ];
        }else{
            $data['messages'][] = [
                'code' => 'warning',
                'message' => 'NO_CHANGES'
            ];
        }

        return $data;

    }

    /**
     * returns the companies name and id
     * @return Array             companies available in the application
     */
    public function getCompanies() {
        global $database;

        $sql = "
            SELECT
              c.id,
              c.name
            FROM
              ".TABLES['company']." c
            ORDER BY name";

        return $database->fetchResultArray($sql);
    }
}
