<?php

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

require_once APPPATH.'us_libraries/Value_object.php';

class Expenditure_bill_request_values extends Value_object
{
    private const REPORT_ID_PROJECTS = 'calcexp_projects_merged';
    private const REPORT_ID_WORKAREAS = 'calcexp_workareas_merged';

    protected int $category_rate_type;

    protected UsDateTime $datetime;
    protected UsDateTime $date;
    protected ?int $project_id = null;
    protected ?int $project_group_id = null;
    protected string $description = '';

    protected bool $is_project = false;
    protected bool $is_woa = false;
    protected bool $is_staff = false;

    protected array $replace_offers_estimation = [];
    protected array $add_existing_fees_to_offer = [];
    protected bool $invoice_inactive_yes = false;
    protected bool $add_to_offers = false;
    protected bool $add_to_fees = false;
    protected bool $round_commercial_row = false;
    protected bool $add_to_loc = false;

    protected string $output_template = 'basic';
    protected ?int $output_design_id = null;
    protected ?Page $page = null;
    protected mixed $page_mode = false;
    protected string|bool $add_to_loc_attach_calcexp = false; // TODO: is this allowed already?

    protected ?int $loc_correspondence_type_id = null;
    protected ?float $loc_vat = null;
    protected ?int $loc_due_days = null;
    protected ?float $loc_discount = null;
    protected ?int $loc_discount_days = null;
    protected ?UsDate $loc_from_date = null;
    protected ?UsDate $loc_to_date = null;

    /**
     * @param int $category_rate_type_times Either Category_rates_model::T_TIMES or Category_rates_model::T_WORK_PACKAGE
     * @return Expenditure_bill_request_values
     * @throws Exception
     */
    public static function init_from_post(int $category_rate_type_times, bool $is_project=false, bool $is_woa=false, bool $is_staff=false): Expenditure_bill_request_values
    {
        $obj = new Expenditure_bill_request_values();
        $obj->CI = CI_Controller::get_instance();

        $obj->category_rate_type = $category_rate_type_times;

        $obj->is_project = $is_project;
        $obj->is_woa = $is_woa;
        $obj->is_staff = $is_staff;

        $obj->CI->form_validation->map_array($obj->CI->input->post('val1'));
        $obj->CI->form_validation->map_array($obj->CI->input->post('val10'));
        $_post_bool = function (string $key) use ($obj): bool // TODO: there must already be a function for this
        {
            return strtolower($obj->CI->input->post($key)) == 'true';
        };

        $filter_prefix = 'filter_projects_';
        if ($category_rate_type_times == Category_rates_model::T_WORK_PACKAGE) {
            $filter_prefix = 'filter_workareas_';
        }

        $obj->datetime = new UsDateTime($obj->CI->input->post($filter_prefix . 'date__date')
            . ' '
            . $obj->CI->input->post($filter_prefix . 'date__time')
        );
        $obj->date = new UsDateTime($obj->CI->input->post($filter_prefix . 'date__date'));

        if (!empty($obj->CI->input->post($filter_prefix . 'project__project_yn'))) {
            $obj->project_id = intval($obj->CI->input->post($filter_prefix . 'project__project'));
        }

        if (!empty($obj->CI->input->post($filter_prefix . 'project__project_group_yn'))) {
            $obj->project_group_id = intval($obj->CI->input->post($filter_prefix . 'project__project_group'));
        }

        $obj->invoice_inactive_yes = $_post_bool('val2');
        $obj->add_to_offers = $_post_bool('val3');
        $obj->add_to_fees = $_post_bool('val4');
        $obj->add_to_loc = $_post_bool('val5');
        $obj->replace_offers_estimation = is_array($obj->CI->input->post('val6')) ? $obj->CI->input->post('val6') : [];
        $obj->add_existing_fees_to_offer = is_array($obj->CI->input->post('val7')) ? $obj->CI->input->post('val7') : [];
        $obj->round_commercial_row = $_post_bool('val8');;
        $obj->description = $obj->CI->input->post('val9');

        $obj->add_to_loc_attach_calcexp = $_post_bool('calcexp_add_to_loc_attach_calcexp');
        $obj->output_template = $obj->CI->input->post('calcexp_add_to_loc_attach_template_type');
        $obj->output_design_id = $obj->CI->input->post('calcexp_add_to_loc_attach_design_type') ?? false;

        if (!empty($obj->CI->input->post('calcexp_add_to_loc_select_type'))) {
            $obj->loc_correspondence_type_id = intval($obj->CI->input->post('calcexp_add_to_loc_select_type'));
        }
        $obj->loc_vat = usnumber_to_float($obj->CI->input->post('calcexp_add_to_loc_vat'));
        $obj->loc_due_days = $obj->CI->input->post('calcexp_add_to_loc_due_days');
        $obj->loc_discount = usnumber_to_float($obj->CI->input->post('calcexp_add_to_loc_discount'));
        $obj->loc_discount_days = empty($obj->CI->input->post('calcexp_add_to_loc_discount_days')) ? null : $obj->CI->input->post('calcexp_add_to_loc_discount_days');
        $obj->loc_from_date = new UsDate($obj->CI->input->post('calcexp_add_to_loc_from_date'), null, true, null);
        $obj->loc_to_date = new UsDate($obj->CI->input->post('calcexp_add_to_loc_to_date'), null, true, null);

        return $obj;
    }

    public static function init_from_graphql_args(array $args): self
    {
        $object = new Expenditure_bill_request_values();

        /**
         * Keep default values; map graphql args to object (&referenced)
         *
         * @param string        $obj_key    The object key to set the value to
         * @param string|null   $args_key   If the key in $args is different from $obj_key, use this value from $args
         * @return void
         */
        $_set_object_value = function (string $obj_key, ?string $args_key = null) use ($args, &$object) {
            $args_key = $args_key ?? $obj_key;
            if (isset($args[$args_key])) {
                $object->$obj_key = $args[$args_key];
            }
        };

        if(isset($args['expenditure_type'])) {
            switch ($args['expenditure_type']) {
                case Calcexp_model::TYPE_IS_PROJECT:
                    $object->is_project = true;
                    break;
                case Calcexp_model::TYPE_IS_WORKAREA:
                    $object->is_woa = true;
                    break;
                case Calcexp_model::TYPE_IS_STAFF:
                    $object->is_staff = true;
                    break;
            }
        }

        $object->datetime = $args['date'];
        $_set_object_value('date');
        $_set_object_value('category_rate_type');
        $_set_object_value('project_id');
        $_set_object_value('project_group_id');
        $_set_object_value('description');
        $_set_object_value('replace_offers_estimation');
        $_set_object_value('add_existing_fees_to_offer');
        $_set_object_value('invoice_inactive_yes');
        $_set_object_value('add_to_offers');
        $_set_object_value('add_to_fees');
        $_set_object_value('round_commercial_row');
        $_set_object_value('add_to_loc', 'add_to_payment');
        $_set_object_value('output_template');
        $_set_object_value('output_design_id');
        $_set_object_value('page');
        $_set_object_value('page_mode');
        $_set_object_value('loc_correspondence_type_id', 'payment_correspondence_type_id');
        $_set_object_value('loc_vat', 'payment_vat');
        $_set_object_value('loc_due_days', 'payment_due_days');
        $_set_object_value('loc_discount', 'payment_discount');
        
        if (isset($args['payment_from_date'])) {
            $object->loc_from_date = new UsDate($args['payment_from_date']->us_date(), null, true, null);
        }
        if (isset($args['payment_to_date'])) {
            $object->loc_to_date = new UsDate($args['payment_to_date']->us_date(), null, true, null);
        }

        return $object;
    }

    public function validate(): string|false
    {
        $this->CI->load->model('Category_rates_model', 'category_rates');
        
        if (!in_array($this->category_rate_type, [
            Category_rates_model::T_TIMES,
            Category_rates_model::T_WORK_PACKAGE
        ], true)) {
            return 'Category Rate Type must be either Category_rates_model::T_TIMES or Category_rates_model::T_WORK_PACKAGE';
        }

        if ($this->project_id === null && $this->project_group_id === null) {
            return 'Project ID or Project Group ID is required';
        }

        if(empty($this->is_project) && empty($this->is_woa) && empty($this->is_staff)) {
            return 'Calcexp must be either project, workarea, or staff';
        }

        return false;
    }

    public function get_calcexp_request_vars(): stdClass
    {
        $request_vars = new stdClass();

        $request_vars->project = $this->get_project_id();
        $request_vars->project_group = $this->get_project_group_id();
        $request_vars->date_from = $this->get_datetime();
        $request_vars->date_to = $this->get_datetime();
        $request_vars->date = $this->get_date();
        $request_vars->is_project = $this->get_is_project();
        $request_vars->is_woa = $this->get_is_woa();
        $request_vars->is_staff = $this->get_is_staff();

        return $request_vars;
    }

    public function get_calcexp_values_new_or_existing(?object $calcexp_existing = null): stdClass
    {
        $obj = new stdClass();
        if ($calcexp_existing === null) {
            $this->CI->load->model('System_settings_model', 'system_settings', true);

            $obj->id = 'add';
            $obj->project_id = $this->get_project_group_id() ?? $this->get_project_id();
            $obj->date = $this->get_datetime()->mysql();
            $obj->staff_id = 0;
            $obj->is_project = $this->get_is_project();
            $obj->is_woa = $this->get_is_woa();
            $obj->is_staff = $this->get_is_staff();
        } else {
            $obj->id = $calcexp_existing->id;
            $obj->project_id = $calcexp_existing->project_id;
            $obj->date = $calcexp_existing->date->mysql();
            $obj->staff_id = $calcexp_existing->staff_id;
            $obj->is_project = $calcexp_existing->is_project;
            $obj->is_woa = $calcexp_existing->is_woa;
            $obj->is_staff = $calcexp_existing->is_staff;
        }
        $obj->description = $this->get_description();
        return $obj;
    }

    public function get_report_vars(int $calcexp_id): array
    {
        return [
            [
                'filter_file_id' => 'calcexp_projects_former',
                'filter_projects_date__date' => $this->get_datetime()->us_date(),
                'filter_projects_date__time' => $this->get_datetime()->us_stime(),
                'filter_projects_project__project_yn' => 1,
                'filter_projects_project__project' => $this->project_id,
                'filter_calcexp_projects_calcexp_id' => $calcexp_id,
            ],
            'date',
            2,
            false,
            true        //Ignore Progressbar
        ];
    }

    public function get_report_id(): string
    {
        if ($this->get_category_rate_type() == Category_rates_model::T_WORK_PACKAGE) {
            return self::REPORT_ID_WORKAREAS;
        } else {
            return self::REPORT_ID_PROJECTS;
        }
    }

    public function get_correspondence_values(int $correspondence_type_id): Correspondence_values
    {
        return Correspondence_values::init_from_values(
            'add',
            $this->get_project_id(),
            false,
            $correspondence_type_id,
            false,
            null,
            'Rechnungsanlage', // TODO: implement md_translations (sic!)
            $this->get_datetime(),
            new UsDate(''),
            new UsDate(''),
            '',
            '',
            [] // $copies
        );
    }

    public function get_loc_values(): stdClass
    {
        $loc_values = new stdClass();

        $loc_values->correspondence_type_id = $this->loc_correspondence_type_id;
        $loc_values->vat = $this->loc_vat;
        $loc_values->due_days = $this->loc_due_days;
        $loc_values->discount = $this->loc_discount;
        $loc_values->discount_days = $this->loc_discount_days;
        $loc_values->from_date = $this->loc_from_date;
        $loc_values->to_date = $this->loc_to_date;

        return $loc_values;
    }

    public function get_category_rate_type(): int
    {
        return $this->category_rate_type;
    }

    public function get_is_project(): bool
    {
        return $this->is_project;
    }

    public function get_is_woa(): bool
    {
        return $this->is_woa;
    }

    public function get_is_staff(): bool
    {
        return $this->is_staff;
    }

    public function get_datetime(): UsDateTime
    {
        return $this->datetime;
    }

    public function get_date(): UsDateTime
    {
        return $this->date;
    }

    public function get_project_id(): ?int
    {
        return $this->project_id;
    }

    public function get_project_group_id(): ?int
    {
        return $this->project_group_id;
    }

    public function get_description(): string
    {
        return $this->description;
    }

    public function get_replace_offers_estimation(): array
    {
        return $this->replace_offers_estimation;
    }

    public function get_add_existing_fees_to_offer(): array
    {
        return $this->add_existing_fees_to_offer;
    }

    public function get_invoice_inactive_yes(): bool
    {
        return $this->invoice_inactive_yes;
    }

    public function get_add_to_offers(): bool
    {
        return $this->add_to_offers;
    }

    public function get_add_to_fees(): bool
    {
        return $this->add_to_fees;
    }

    public function get_round_commercial_row(): bool
    {
        return $this->round_commercial_row;
    }

    public function get_add_to_loc(): bool
    {
        return $this->add_to_loc;
    }

    public function get_output_template(): string
    {
        return $this->output_template;
    }

    public function get_output_design_id(): ?int
    {
        return $this->output_design_id;
    }

    public function get_page(): ?Page
    {
        return $this->page;
    }

    public function get_page_mode(): mixed
    {
        return $this->page_mode;
    }

    public function get_add_to_loc_attach_calcexp(): bool
    {
        return $this->add_to_loc_attach_calcexp;
    }
}