<?php

if ( ! defined('BASEPATH')) {
    exit('No direct script access allowed');
}

/*
 * InvoicePlane
 *
 * @author      InvoicePlane Developers & Contributors
 * @copyright   Copyright (c) 2012 - 2018 InvoicePlane.com
 * @license     https://invoiceplane.com/license.txt
 * @link        https://invoiceplane.com
 */

#[AllowDynamicProperties]
class Mdl_Quotes extends Response_Model
{
    public $table = 'ip_quotes';

    public $primary_key = 'ip_quotes.quote_id';

    public $date_modified_field = 'quote_date_modified';

    /**
     * @return array
     */
    public function statuses()
    {
        return [
            '1' => [
                'label' => trans('draft'),
                'class' => 'draft',
                'href'  => 'quotes/status/draft',
            ],
            '2' => [
                'label' => trans('sent'),
                'class' => 'sent',
                'href'  => 'quotes/status/sent',
            ],
            '3' => [
                'label' => trans('viewed'),
                'class' => 'viewed',
                'href'  => 'quotes/status/viewed',
            ],
            '4' => [
                'label' => trans('approved'),
                'class' => 'approved',
                'href'  => 'quotes/status/approved',
            ],
            '5' => [
                'label' => trans('rejected'),
                'class' => 'rejected',
                'href'  => 'quotes/status/rejected',
            ],
            '6' => [
                'label' => trans('canceled'),
                'class' => 'canceled',
                'href'  => 'quotes/status/canceled',
            ],
        ];
    }

    public function default_select()
    {
        $this->db->select("
            SQL_CALC_FOUND_ROWS
            ip_users.*,
            ip_clients.*,
            ip_quote_amounts.quote_amount_id,
            IFnull(ip_quote_amounts.quote_item_subtotal, '0.00') AS quote_item_subtotal,
            IFnull(ip_quote_amounts.quote_item_tax_total, '0.00') AS quote_item_tax_total,
            IFnull(ip_quote_amounts.quote_tax_total, '0.00') AS quote_tax_total,
            IFnull(ip_quote_amounts.quote_total, '0.00') AS quote_total,
            ip_invoices.invoice_number,
            ip_quotes.*", false);
    }

    public function default_order_by()
    {
        $this->db->order_by('ip_quotes.quote_date_created DESC, ip_quotes.quote_number DESC, ip_quotes.quote_id DESC');
    }

    public function default_join()
    {
        $this->db->join('ip_clients', 'ip_clients.client_id = ip_quotes.client_id');
        $this->db->join('ip_users', 'ip_users.user_id = ip_quotes.user_id');
        $this->db->join('ip_quote_amounts', 'ip_quote_amounts.quote_id = ip_quotes.quote_id', 'left');
        $this->db->join('ip_invoices', 'ip_invoices.invoice_id = ip_quotes.invoice_id', 'left');
    }

    /**
     * @return array
     */
    public function validation_rules()
    {
        return [
            'client_id' => [
                'field' => 'client_id',
                'label' => trans('client'),
                'rules' => 'required',
            ],
            'quote_date_created' => [
                'field' => 'quote_date_created',
                'label' => trans('quote_date'),
                'rules' => 'required',
            ],
            'invoice_group_id' => [
                'field' => 'invoice_group_id',
                'label' => trans('quote_group'),
                'rules' => 'required',
            ],
            'quote_password' => [
                'field' => 'quote_password',
                'label' => trans('quote_password'),
            ],
            'user_id' => [
                'field' => 'user_id',
                'label' => trans('user'),
                'rule'  => 'required',
            ],
        ];
    }

    /**
     * @return array
     */
    public function validation_rules_save_quote()
    {
        return [
            'quote_number' => [
                'field' => 'quote_number',
                'label' => trans('quote') . ' #',
                'rules' => 'is_unique[ip_quotes.quote_number' . (($this->id) ? '.quote_id.' . $this->id : '') . ']',
            ],
            'quote_date_created' => [
                'field' => 'quote_date_created',
                'label' => trans('date'),
                'rules' => 'required',
            ],
            'quote_date_expires' => [
                'field' => 'quote_date_expires',
                'label' => trans('due_date'),
                'rules' => 'required',
            ],
            'quote_password' => [
                'field' => 'quote_password',
                'label' => trans('quote_password'),
            ],
        ];
    }

    /**
     * @return int|null
     */
    public function create($db_array = null)
    {
        $quote_id = parent::save(null, $db_array);

        // Create an quote amount record
        $db_array = [
            'quote_id' => $quote_id,
        ];

        $this->db->insert('ip_quote_amounts', $db_array);

        // Create the default invoice tax record if applicable
        if (get_setting('default_invoice_tax_rate')) {
            $db_array = [
                'quote_id'              => $quote_id,
                'tax_rate_id'           => get_setting('default_invoice_tax_rate'),
                'include_item_tax'      => get_setting('default_include_item_tax'),
                'quote_tax_rate_amount' => 0,
            ];

            $this->db->insert('ip_quote_tax_rates', $db_array);
        }

        return $quote_id;
    }

    /**
     * Copies quote items, tax rates, etc from source to target.
     *
     * @param int $source_id
     * @param int $target_id
     */
    public function copy_quote($source_id, $target_id)
    {
        $this->load->model('quotes/mdl_quote_items');

        // Discounts calculation - since v1.6.3 Need if taxes applied after discounts
        $quote           = $this->get_by_id($source_id); // This is the original quote
        $global_discount = [
            'amount'         => $quote->quote_discount_amount,
            'percent'        => $quote->quote_discount_percent,
            'item'           => 0.0, // Updated by ref (Need for quote_item_subtotal calculation in Mdl_quote_amounts)
            'items_subtotal' => $this->mdl_quote_items->get_items_subtotal($source_id),
        ];
        unset($quote); // Free memory

        // Update the discounts - since v1.6.3
        $this->where('quote_id', $target_id)->update('ip_quotes', [
            'quote_discount_percent' => $global_discount['percent'],
            'quote_discount_amount'  => $global_discount['amount'],
        ]);

        $quote_items = $this->mdl_quote_items->where('quote_id', $source_id)->get()->result();

        foreach ($quote_items as $quote_item) {
            $db_array = [
                'quote_id'             => $target_id,
                'item_tax_rate_id'     => $quote_item->item_tax_rate_id,
                'item_product_id'      => $quote_item?->item_product_id,
                'item_name'            => $quote_item->item_name,
                'item_description'     => $quote_item->item_description,
                'item_quantity'        => $quote_item->item_quantity,
                'item_price'           => $quote_item->item_price,
                'item_discount_amount' => $quote_item?->item_discount_amount,
                'item_order'           => $quote_item->item_order,
                'item_product_unit'    => $quote_item?->item_product_unit,
                'item_product_unit_id' => $quote_item?->item_product_unit_id,
            ];

            $this->mdl_quote_items->save(null, $db_array, $global_discount);
        }

        $quote_tax_rates = $this->mdl_quote_tax_rates->where('quote_id', $source_id)->get()->result();

        foreach ($quote_tax_rates as $quote_tax_rate) {
            $db_array = [
                'quote_id'              => $target_id,
                'tax_rate_id'           => $quote_tax_rate->tax_rate_id,
                'include_item_tax'      => $quote_tax_rate->include_item_tax,
                'quote_tax_rate_amount' => $quote_tax_rate->quote_tax_rate_amount,
            ];

            $this->mdl_quote_tax_rates->save(null, $db_array);
        }

        // Copy the custom fields
        $this->load->model('custom_fields/mdl_quote_custom');
        $db_array = $this->mdl_quote_custom->where('quote_id', $source_id)->get()->row_array() ?? [];

        if (count($db_array) > 2) {
            unset($db_array['quote_custom_id']);
            $db_array['quote_id'] = $target_id;
            $this->mdl_quote_custom->save_custom($target_id, $db_array);
        }
    }

    /**
     * @return array
     */
    public function db_array()
    {
        $db_array = parent::db_array();

        // Get the client id for the submitted quote
        $this->load->model('clients/mdl_clients');
        $cid                   = $this->mdl_clients->where('ip_clients.client_id', $db_array['client_id'])->get()->row()->client_id;
        $db_array['client_id'] = $cid;

        $db_array['quote_date_created'] = date_to_mysql($db_array['quote_date_created']);
        $db_array['quote_date_expires'] = $this->get_date_due($db_array['quote_date_created']);

        $db_array['notes'] = get_setting('default_quote_notes');

        if ( ! isset($db_array['quote_status_id'])) {
            $db_array['quote_status_id'] = 1;
        }

        $generate_quote_number = get_setting('generate_quote_number_for_draft');

        if ($db_array['quote_status_id'] === 1 && $generate_quote_number == 1) {
            $db_array['quote_number'] = $this->get_quote_number($db_array['invoice_group_id']);
        } elseif ($db_array['quote_status_id'] != 1) {
            $db_array['quote_number'] = $this->get_quote_number($db_array['invoice_group_id']);
        } else {
            $db_array['quote_number'] = '';
        }

        // Generate the unique url key
        $db_array['quote_url_key'] = $this->get_url_key();

        return $db_array;
    }

    /**
     * @param string $quote_date_created
     */
    public function get_date_due($quote_date_created)
    {
        $quote_date_expires = new DateTime($quote_date_created);
        $quote_date_expires->add(new DateInterval('P' . get_setting('quotes_expire_after') . 'D'));

        return $quote_date_expires->format('Y-m-d');
    }

    /**
     * @param $invoice_group_id
     *
     * @return mixed
     */
    public function get_quote_number($invoice_group_id)
    {
        $this->load->model('invoice_groups/mdl_invoice_groups');

        return $this->mdl_invoice_groups->generate_invoice_number($invoice_group_id);
    }

    /**
     * @return string
     */
    public function get_url_key()
    {
        $this->load->helper('string');

        return random_string('alnum', 32);
    }

    /**
     * @param $invoice_id
     *
     * @return mixed
     */
    public function get_invoice_group_id($invoice_id)
    {
        $invoice = $this->get_by_id($invoice_id);

        return $invoice->invoice_group_id;
    }

    /**
     * @param int $quote_id
     */
    public function delete($quote_id)
    {
        parent::delete($quote_id);

        $this->load->helper('orphan');
        delete_orphans();
    }

    /**
     * @return $this
     */
    public function is_draft()
    {
        $this->filter_where('quote_status_id', 1);

        return $this;
    }

    /**
     * @return $this
     */
    public function is_sent()
    {
        $this->filter_where('quote_status_id', 2);

        return $this;
    }

    /**
     * @return $this
     */
    public function is_viewed()
    {
        $this->filter_where('quote_status_id', 3);

        return $this;
    }

    /**
     * @return $this
     */
    public function is_approved()
    {
        $this->filter_where('quote_status_id', 4);

        return $this;
    }

    /**
     * @return $this
     */
    public function is_rejected()
    {
        $this->filter_where('quote_status_id', 5);

        return $this;
    }

    /**
     * @return $this
     */
    public function is_canceled()
    {
        $this->filter_where('quote_status_id', 6);

        return $this;
    }

    /**
     * Used by guest module; includes only sent and viewed.
     *
     * @return $this
     */
    public function is_open()
    {
        $this->filter_where_in('quote_status_id', [2, 3]);

        return $this;
    }

    /**
     * @return $this
     */
    public function guest_visible()
    {
        $this->filter_where_in('quote_status_id', [2, 3, 4, 5]);

        return $this;
    }

    /**
     * @param $client_id
     *
     * @return $this
     */
    public function by_client($client_id)
    {
        $this->filter_where('ip_quotes.client_id', $client_id);

        return $this;
    }

    /**
     * @param $quote_url_key
     */
    public function approve_quote_by_key($quote_url_key)
    {
        $this->db->where_in('quote_status_id', [2, 3]);
        $this->db->where('quote_url_key', $quote_url_key);
        $this->db->set('quote_status_id', 4);
        $this->db->update('ip_quotes');
    }

    /**
     * @param $quote_url_key
     */
    public function reject_quote_by_key($quote_url_key)
    {
        $this->db->where_in('quote_status_id', [2, 3]);
        $this->db->where('quote_url_key', $quote_url_key);
        $this->db->set('quote_status_id', 5);
        $this->db->update('ip_quotes');
    }

    /**
     * @param $quote_id
     */
    public function approve_quote_by_id($quote_id)
    {
        $this->db->where_in('quote_status_id', [2, 3]);
        $this->db->where('quote_id', $quote_id);
        $this->db->set('quote_status_id', 4);
        $this->db->update('ip_quotes');
    }

    /**
     * @param $quote_id
     */
    public function reject_quote_by_id($quote_id)
    {
        $this->db->where_in('quote_status_id', [2, 3]);
        $this->db->where('quote_id', $quote_id);
        $this->db->set('quote_status_id', 5);
        $this->db->update('ip_quotes');
    }

    /**
     * @param $quote_id
     */
    public function mark_viewed($quote_id)
    {
        $this->db->select('quote_status_id');
        $this->db->where('quote_id', $quote_id);

        $quote = $this->db->get('ip_quotes');

        if ($quote->num_rows() && $quote->row()->quote_status_id == 2) {
            $this->db->where('quote_id', $quote_id);
            $this->db->set('quote_status_id', 3);
            $this->db->update('ip_quotes');
        }
    }

    /**
     * @param $quote_id
     */
    public function mark_sent($quote_id)
    {
        $this->db->select('quote_status_id');
        $this->db->where('quote_id', $quote_id);

        $quote = $this->db->get('ip_quotes');

        if ($quote->num_rows() && $quote->row()->quote_status_id == 1) {
            $this->db->where('quote_id', $quote_id);
            $this->db->set('quote_status_id', 2);
            $this->db->update('ip_quotes');
        }
    }

    /**
     * @param $quote_id
     */
    public function generate_quote_number_if_applicable($quote_id)
    {
        $quote = $this->mdl_quotes->get_by_id($quote_id);

        // Generate new quote number if applicable
        if ( ! empty($quote) && ($quote->quote_status_id == 1 && $quote->quote_number == '') && get_setting('generate_quote_number_for_draft') == 0) {
            $quote_number = $this->mdl_quotes->get_quote_number($quote->invoice_group_id);
            // Set new quote number and save
            $this->db->where('quote_id', $quote_id);
            $this->db->set('quote_number', $quote_number);
            $this->db->update('ip_quotes');
        }
    }
}
