<?php
/**
 * Created by Glee Ltd.
 * User: Hieu
 * Date: 20-Aug-19
 * Time: 11:16 AM
 * Description:
 */

namespace Hura8\System\Controller;

use Hura8\Interfaces\iExcelDownload;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;


abstract class aExcelDownloadController implements iExcelDownload
{
    protected static $column_names = [
        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
        'AA','AB','AC','AD','AE','AF','AG','AH','AI','AJ','AK','AL','AM',
        'AN','AO','AP','AQ','AR','AS','AT','AU','AV','AW','AX','AY','AZ',
        //...
    ];

    protected $work_sheet_title = "Danh sách"; //
    protected $export_file_name = '';

    /* @var $objExcel Spreadsheet */
    protected $objExcel;

    /* @var $currentActiveSheet Worksheet */
    protected $currentActiveSheet;

    protected $client_excel_col_config = [
        /*"A" => array(
            'name'  => 'ID Sản phẩm Web',
            'width' => '10',
            'data_field_name'   => 'id',
        ),
        "B" => array(
            'name'  => 'Mã kho (SKU)',
            'width' => '10',
            'data_field_name'   => 'storeSKU',
        ),*/
        //...
    ];

    protected $field_column_mappings = [
        //'name' => "A",
        //'price' => "B",
    ];

    private $header_row_index = 2;

    // hold cach for some operations
    protected $cache = [];

    protected $format_item_middlewares = []; // list of middleware object to format item


    public function __construct($client_config_file_name='', $export_file_name='', $work_sheet_title = '')
    {
        if($client_config_file_name) {
            $this->setColumnConfigUseConfigFile($client_config_file_name);
        }

        $this->export_file_name = ($export_file_name) ?: "file_".CURRENT_TIME;
        $this->work_sheet_title = ($work_sheet_title) ?: "Danh sách";
    }


    protected function setColumnConfigUseConfigFile($client_config_file_name) {
        //this from a config file for each client
        $client_config_file = "config/client/excel/".$client_config_file_name;
        if( ! file_exists(ROOT_DIR .'/'. $client_config_file)) {
            die("Please create config file: ".$client_config_file);
        }
        $client_fields_config = include ROOT_DIR .'/'. $client_config_file;

        // auto add excel column names based on fields' index
        $this->client_excel_col_config = $this->_make_columns(array_values($client_fields_config));

        // create field-col map
        $this->createFieldColumnMappings();
    }


    private function createFieldColumnMappings() {
        $field_column_mappings = [];
        foreach ($this->client_excel_col_config as $column_name => $_prop) {
            $field_column_mappings[$_prop['data_field_name']] = $column_name;
        }
        $this->field_column_mappings = $field_column_mappings;
    }


    public function setColumnConfigManually(array $col_config) {
        $this->client_excel_col_config = $this->_make_columns($col_config);
        // create field-col map
        $this->createFieldColumnMappings();
    }


    protected function _make_columns($fields_config) {
        $new_array = [];
        $total_names = sizeof(static::$column_names);
        foreach ($fields_config as $index => $config) {
            if($index >= $total_names) break;
            $new_array[static::$column_names[$index]] = $config;
        }

        return $new_array;
    }


    public function start(array $options = [
        "debug_mode" => '', // show-item-list
        "excelOption" => [],
        "sheetOption" => [],
        "sheetStartRowNumber" => 3,
        "sheetHeaderRowNumber" => 2,
        "getItemListOption" => [
            "brand"     => [],
            "category"  => [],
            "page"      => 1,
            "limit"     => 100,
        ],
        "exportFileOption" => [],
    ]) {

        $debug_mode = $options['debug_mode'] ?? false;
        // debug mode
        if($debug_mode) {
            // show item list
            if($debug_mode == 'show-item-list') {
                $item_list = $this->getItemList($options['getItemListOption']);
                print_r($item_list);
            }

            // show formatted list
            if($debug_mode == 'show-formatted-list') {
                $item_list = $this->formatItemList( $this->getItemList($options['getItemListOption']) );
                print_r($item_list);
            }

            return true;
        }

        // setup
        if(isset($options['sheetHeaderRowNumber']) && $options['sheetHeaderRowNumber']) {
            $this->header_row_index = $options['sheetHeaderRowNumber'];
        }

        $this->createExcelObject($options['excelOption'] ?? []);
        $this->createActiveSheet(0, $options['sheetOption'] ?? []);

        // num beforeWriteList
        $this->beforeWriteList();

        // fetch all items and write till the end
        $has_fetch_all_items = false;
        $start_row = (isset($options["sheetStartRowNumber"])) ? $options["sheetStartRowNumber"] : 3;

        $item_list = $this->formatItemList( $this->getItemList($options['getItemListOption']) );

        //debug_var($item_list);
        //exit;

        $has_fetch_all_items = true;
        $this->writeItemsToExcel($start_row, $item_list);

        /*$getItemListOption = $options['getItemListOption'];
        $pageIndex = 0;

        while (!$has_fetch_all_items) {
            // run from page 1->end
            $pageIndex += 1;
            $getItemListOption["page"] = $pageIndex;
            $item_list = $this->getItemList($getItemListOption);

            // flag
            if(!sizeof($item_list)) {
                $has_fetch_all_items = true;
            }

            // else, start write
            $last_row = $this->writeItemsToExcel($start_row, $item_list);

            // update $start_row
            $start_row = $last_row;// + 1
        }*/

        // export
        if($has_fetch_all_items) {
            $this->getExcelFile($options['exportFileOption']);
        }

        return true;
    }

    // abstract methods
    protected function beforeWriteList() { }
    abstract protected function getItemList(array $options);
    abstract protected function defaultFormatItemInfo(array $item_info, $index =0);

    protected function registerFormatItemInfoMiddleware($middleware_ojb) {
        $this->format_item_middlewares[] = $middleware_ojb;
    }

    protected function formatItemInfo(array $item_info, $index = 0) {

        // apply middleware
        if(sizeof($this->format_item_middlewares)) {
            foreach ($this->format_item_middlewares as $_middleware) {
                $item_info = call_user_func($_middleware, $item_info);
            }
        } else {
            $item_info = $this->defaultFormatItemInfo($item_info, $index);
        }

        return $item_info;
    }


    protected function createExcelObject(array $options)
    {
        // Create new Spreadsheet object
        $this->objExcel = new Spreadsheet();

        // Set properties
        $this->objExcel->getProperties()->setCreator("Hurasoft");
        $this->objExcel->getProperties()->setLastModifiedBy("Hurasoft");
        $this->objExcel->getProperties()->setTitle("Excel Document");
        $this->objExcel->getProperties()->setSubject("Excel Document");
        $this->objExcel->getProperties()->setDescription("Tao file excel");
    }


    protected function createActiveSheet($sheet_index = 0, array $options=[]) {
        // Create a first sheet, representing sales data
        $this->objExcel->setActiveSheetIndex($sheet_index);
        $this->currentActiveSheet = $this->objExcel->getActiveSheet();
        $this->currentActiveSheet->setCellValueExplicit('A1', $this->work_sheet_title, DataType::TYPE_STRING);
        $this->currentActiveSheet->getStyle('A1')->getFont()->setSize(16);
        $this->currentActiveSheet->getStyle('A1')->getFont()->setBold(true);

        // Set header row
        $row_index = $this->header_row_index;
        foreach ($this->client_excel_col_config as $col_name => $_prop) {
            $col_width = (isset($_prop['width']) && intval($_prop['width']) > 0) ? intval($_prop['width']) : 15;
            $this->currentActiveSheet->getColumnDimension($col_name)->setWidth($col_width);
            $this->currentActiveSheet->setCellValueExplicit($col_name. $row_index, $_prop["name"], DataType::TYPE_STRING);
            $this->currentActiveSheet->getStyle($col_name . $row_index)->getFont()->setBold(true);
        }
    }

    protected function formatItemList(array $item_list)
    {
        $new_list = [];
        foreach ( $item_list as $index => $item_info ) {
            $new_list[$index] = $this->formatItemInfo($item_info, $index);
        }

        return $new_list;
    }


    protected function writeItemsToExcel($start_row = 1, array $item_list=[])
    {
        $write_row = $start_row;
        foreach ( $item_list as $index => $item_info ) {

            // write each field to its corresponding columns
            foreach ($item_info as $_field => $_value) {
                if( !isset($this->field_column_mappings[$_field])) continue;

                if(is_array($_value)) $_value = serialize($_value);

                $write_column = $this->field_column_mappings[$_field];

                $this->currentActiveSheet->setCellValueExplicit($write_column . $write_row, $_value, DataType::TYPE_STRING);
                $this->currentActiveSheet->getStyle($write_column . $write_row)->getAlignment()->setWrapText(true);
            }

            // next rows
            $write_row += 1;
        }

        // get the last row
        return $write_row;
    }


    protected function getExcelFile(array $options)
    {
        // write to a local file
        $local_file = $options['local_file'] ?? '';
        if($local_file) {
            $this->_save_to_file($local_file);
            return true;
        }

        // default: export to browser to download
        $this->_export_to_browser();

        return true;
    }

    protected function cleanUp(){
        // clean up
        $this->objExcel->disconnectWorksheets();
        unset($this->objExcel);
    }

    protected function _save_to_file($file_path){

        // delete old file if exist
        if(file_exists($file_path)) {
            @unlink($file_path);
        }

        $writer = new Xlsx($this->objExcel);
        $writer->save($file_path);
        $this->cleanUp();
    }

    protected function _export_to_browser(){
        // Rename sheet
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition: attachment;filename="'.$this->export_file_name.'.xlsx"');
        header('Cache-Control: max-age=0');

        $writer = new Xlsx($this->objExcel);
        ob_end_clean();
        $writer->save('php://output');

        $this->cleanUp();
        exit();
    }

}
