<?php

/**
 * migrates 3.1 attendee rows into 4.1 line items.
 * THis is done separately from migrating transaction and registrations, because
 * we wanted to use that completed 4.1 data in creating the line items
 * 4.1 Line Item table fields
 * $this->_tables = array(
 * 'Line_Item'=>new EE_Primary_Table('esp_line_item','LIN_ID')
 * );
 * $line_items_can_be_for = array('Ticket','Price');
 * $this->_fields = array(
 * 'Line_Item'=> array(
 * 'LIN_ID'=>new EE_Primary_Key_Int_Field('LIN_ID', esc_html__("ID", "event_espresso")),
 * 'LIN_code'=>new EE_Slug_Field('LIN_code', esc_html__("Code for index into Cart", "event_espresso"), true),
 * 'TXN_ID'=>new EE_Foreign_Key_Int_Field('TXN_ID', esc_html__("Transaction ID", "event_espresso"), true, null,
 * 'Transaction'),
 * 'LIN_name'=>new EE_Full_HTML_Field('LIN_name', esc_html__("Line Item Name", "event_espresso"), false, ''),
 * 'LIN_desc'=>new EE_Full_HTML_Field('LIN_desc', esc_html__("Line Item Description", "event_espresso"), true),
 * 'LIN_unit_price'=>new EE_Money_Field('LIN_unit_price',  esc_html__("Unit Price", "event_espresso"),false,0),
 * 'LIN_percent'=>new EE_Float_Field('LIN_percent', esc_html__("Percent", "event_espresso"), false, false),
 * 'LIN_is_taxable'=>new EE_Boolean_Field('LIN_is_taxable', esc_html__("Taxable", "event_espresso"), false, false),
 * 'LIN_order'=>new EE_Integer_Field('LIN_order', esc_html__("Order of Application towards total of parent",
 * "event_espresso"), false,1),
 * 'LIN_total'=>new EE_Money_Field('LIN_total', esc_html__("Total (unit price x quantity)", "event_espresso"), false,
 * 0),
 * 'LIN_quantity'=>new EE_Integer_Field('LIN_quantity', esc_html__("Quantity", "event_espresso"), true, null),
 * 'LIN_parent'=>new EE_Integer_Field('LIN_parent', esc_html__("Parent ID (this item goes towards that Line Item's
 * total)", "event_espresso"), true, null),
 * 'LIN_type'=>new EE_Enum_Text_Field('LIN_type', esc_html__("Type", "event_espresso"), false, 'line-item',
 * array(
 * self::type_line_item=>  esc_html__("Line Item", "event_espresso"),
 * self::type_sub_line_item=>  esc_html__("Sub-Item", "event_espresso"),
 * self::type_sub_total=>  esc_html__("Subtotal", "event_espresso"),
 * self::type_tax_sub_total => esc_html__("Tax Subtotal", "event_espresso"),
 * self::type_tax=>  esc_html__("Tax", "event_espresso"),
 * self::type_total=>  esc_html__("Total", "event_espresso"))),
 * 'OBJ_ID'=>new EE_Foreign_Key_Int_Field('OBJ_ID', esc_html__("ID of Item purchased.", "event_espresso"),
 * true,null,$line_items_can_be_for),
 * 'OBJ_type'=>new EE_Any_Foreign_Model_Name_Field('OBJ_type', esc_html__("Model Name this Line Item is for",
 * "event_espresso"), true,null,$line_items_can_be_for),
 * )
 * );
 */
class EE_DMS_4_1_0_line_items extends EE_Data_Migration_Script_Stage_Table
{
    private string $_new_line_table;

    private string $_new_transaction_table;

    private string $_new_reg_table;


    public function __construct()
    {
        global $wpdb;
        $this->_pretty_name           = esc_html__("Line Items", "event_espresso");
        $this->_old_table             = $wpdb->prefix . "events_attendee";
        $this->select_expression      = 'att.*, e.event_status';
        $this->_extra_where_sql       = ' AS att
            INNER JOIN ' . $wpdb->prefix . 'events_detail AS e ON att.event_id=e.id
            WHERE e.event_status!="D"';
        $this->_new_transaction_table = $wpdb->prefix . "esp_transaction";
        $this->_new_line_table        = $wpdb->prefix . "esp_line_item";
        $this->_new_reg_table         = $wpdb->prefix . "esp_registration";
        parent::__construct();
    }


    /**
     * @throws EE_Error
     */
    protected function _migrate_old_row($old_row)
    {
        // insert line items if its a primary id
        if ((int) $old_row['is_primary']) {
            $txn_id = (int) $this->get_migration_script()->get_mapping_new_pk(
                $this->_old_table,
                $old_row['id'],
                $this->_new_transaction_table
            );
            if (! $txn_id) {
                $this->add_error(
                    sprintf(
                        esc_html__(
                            "Could not find the transaction for the 3.1 attendee %d from row %s",
                            "event_espresso"
                        ),
                        $old_row['id'],
                        $this->_json_encode($old_row)
                    )
                );
                return;
            }
            $txn            = $this->_get_txn($txn_id);
            $new_line_items = $this->_insert_new_line_items($txn, $old_row);
            $this->get_migration_script()->set_mapping(
                $this->_old_table,
                $old_row['id'],
                $this->_new_line_table,
                $new_line_items
            );
        }
    }


    private function _get_txn(int $txn_id)
    {
        global $wpdb;
        return $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM $this->_new_transaction_table WHERE TXN_ID=%d",
                $txn_id
            ),
            ARRAY_A
        );
    }


    /**
     * In 4.1, we'd normally need more info than just the registrations to make the line items. Ie, we'd need
     * the transaction, and tax prices at the time of registration. (And probably promotions and other price factors).
     * But seeing how these are REGs created from 3.1 attendee data, which have
     *
     * @param array $transaction
     * @param array $old_attendee
     * @return array new line item ids
     */
    private function _insert_new_line_items(array $transaction, array $old_attendee): array
    {
        global $wpdb;
        $regs_on_this_transaction = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM $this->_new_reg_table WHERE TXN_ID=%d",
                $transaction['TXN_ID']
            ),
            ARRAY_A
        );
        $new_line_item_ids        = [];
        // create a total ine item
        $total_line_item_id  = $this->_insert_new_line_item(
            [
                'LIN_code'  => 'total',
                'TXN_ID'    => $transaction['TXN_ID'],
                'LIN_name'  => esc_html__(
                    "Total",
                    "event_espresso"
                ),
                'LIN_total' => $transaction['TXN_total'],
                'LIN_type'  => 'total',
                'OBJ_ID'    => $transaction['TXN_ID'],
                'OBJ_type'  => 'Transaction',
            ],
            $old_attendee
        );
        $new_line_item_ids[] = $total_line_item_id;
        // create a subtotal line item
        $reg_total = 0;
        foreach ($regs_on_this_transaction as $new_reg) {
            $reg_total += floatval($new_reg['REG_final_price']);
        }
        $subtotal_line_item_id = $this->_insert_new_line_item(
            [
                'LIN_code'   => 'sub-total',
                'TXN_ID'     => $transaction['TXN_ID'],
                'LIN_name'   => esc_html__(
                    "Subtotal",
                    "event_espresso"
                ),
                'LIN_total'  => $reg_total,
                'LIN_parent' => $total_line_item_id,
                'LIN_type'   => 'sub-total',
            ],
            $old_attendee
        );
        $new_line_item_ids[]   = $subtotal_line_item_id;
        // group REGs by TKT_ID
        $regs_by_tkt = [];
        foreach ($regs_on_this_transaction as $new_reg) {
            $regs_by_tkt[ $new_reg['TKT_ID'] ][] = $new_reg;
        }

        // create individual line items

        foreach ($regs_by_tkt as $ticket_id => $regs) {
            $count      = count($regs);
            $line_total = 0;
            foreach ($regs as $new_reg) {
                $line_total += $new_reg['REG_final_price'];
            }
            $a_reg               = reset($regs);
            $new_ticket          = $this->_get_new_ticket_row($a_reg['TKT_ID']);
            $reg_line_item_id    = $this->_insert_new_line_item(
                [
                    'LIN_code'       => md5(
                        'Ticket' . $ticket_id . time()
                    ),
                    'TXN_ID'         => $transaction['TXN_ID'],
                    'LIN_name'       => $new_ticket['TKT_name'],
                    'LIN_unit_price' => $a_reg['REG_final_price'],
                    'LIN_is_taxable' => false,
                    'LIN_total'      => $line_total,
                    'LIN_quantity'   => $count,
                    'LIN_parent'     => $subtotal_line_item_id,
                    'OBJ_ID'         => $ticket_id,
                    'OBJ_type'       => 'Ticket',
                ],
                $old_attendee
            );
            $new_line_item_ids[] = $reg_line_item_id;
        }


        return $new_line_item_ids;
    }


    /**
     * Gets the full ticket by ID
     *
     * @param int   $new_ticket_id
     * @return array
     * @global wpdb $wpdb
     */
    private function _get_new_ticket_row(int $new_ticket_id): array
    {
        global $wpdb;
        return $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$wpdb->prefix}esp_ticket WHERE TKT_ID=%d",
                $new_ticket_id
            ),
            ARRAY_A
        );
    }


    private function _insert_new_line_item(array $cols_n_values, array $old_attendee): int
    {
        global $wpdb;
        $default_cols_n_values = [
            'LIN_code'       => '',
            'TXN_ID'         => 0,
            'LIN_name'       => '',
            'LIN_desc'       => '',
            'LIN_unit_price' => 0,
            'LIN_percent'    => 0,
            'LIN_is_taxable' => false,
            'LIN_order'      => 0,
            'LIN_total'      => 0,
            'LIN_quantity'   => null,
            'LIN_parent'     => 0,
            'LIN_type'       => 'line-item',
            'OBJ_ID'         => null,
            'OBJ_type'       => null,
        ];
        $cols_n_values         = array_merge($default_cols_n_values, $cols_n_values);
        $data_types            = [
            '%s',// LIN_code
            '%d',// TXN_ID
            '%s',// LIN_name
            '%s',// LIN_desc
            '%f',// LIN_unit_price
            '%f',// LIN_percent
            '%d',// LIN_is_taxable
            '%d',// LIN_order
            '%f',// LIN_total
            '%d',// LIN_quantity
            '%d',// LIN_parent
            '%s',// LIN_type
            '%d',// OBJ_ID
            '%s',// OBJ_type
        ];

        $success = $wpdb->insert(
            $this->_new_line_table,
            $cols_n_values,
            $data_types
        );
        if (! $success) {
            $this->add_error(
                $this->get_migration_script()->_create_error_message_for_db_insertion(
                    $this->_old_table,
                    $old_attendee,
                    $this->_new_reg_table,
                    $cols_n_values,
                    $data_types
                )
            );
            return 0;
        }
        return $wpdb->insert_id;
    }
}
