<?php

namespace app\common\arw\adjfut\src;

use app\common\arw\adjfut\src\Exception\ErrorMsg;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use think\helper\Arr;
use think\helper\Str;
use think\Validate;
use app\common\arw\adjfut\src\Traits\Event;
use think\facade\Env;

class Excel
{
    use Event;
    /**
     * Spreadsheet
     *
     * @var Spreadsheet
     */
    private $spreadsheet = null;
    /**
     * 工作表样式
     *
     * @var array
     */
    private $worksheetStyle = [];
    /**
     * 文件路径
     *
     * @var string
     * @date 2022-04-15
     * @example
     * @author arw
     * @since 1.0.0
     */
    private $path = '';

    /**
     * 初始化
     *
     * @param string|UploadFile $path
     * @date 2022-04-15
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function __construct($path = '')
    {
        if ($path) {
            if ($path instanceof UploadFile) {
                $path = $path->getRealPath();
            }
            $this->spreadsheet = self::loadExcel($path);
            $this->path = $path;
        } else {
            $this->spreadsheet = new Spreadsheet();
        }
    }

    /**
     * 设置工作表样式
     *
     * @param array $options
     * @param boolean $options[autoBorder] 是否自动添加边框
     * @param boolean $options[freezeLine] 是否自动冻结首行
     * @return self
     */
    public function setWorksheetStyle(array $options): self
    {
        $this->worksheetStyle = $options;
        return $this;
    }

    /**
     * 获取当前Spreadsheet
     *
     * @return Spreadsheet
     * @date 2020-11-23
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function getSpreadsheet(): Spreadsheet
    {
        return $this->spreadsheet;
    }

    /**
     * 读取excel
     *
     * @param string $path excel路径
     * @return Spreadsheet
     */
    public static function loadExcel(string $path): Spreadsheet
    {
        if (!is_file($path)) {
            throw new ErrorMsg("文件不存在 $path", 1);
        }
        $ext = pathinfo($path, PATHINFO_EXTENSION);
        return IOFactory::createReader(ucfirst($ext))->load($path);
    }

    /**
     * 获取工作表
     * 默认获取当前激活的
     *
     * @param string $sheet
     * @return Worksheet
     */
    public function getWorksheet(string $sheet = 'active'): Worksheet
    {
        $Spreadsheet = $this->getSpreadsheet();
        if ($sheet === 'active') {
            $Worksheet = $Spreadsheet->getActiveSheet();
        } else {
            $Worksheet = $Spreadsheet->getSheet($sheet);
        }
        return $Worksheet;
    }

    /**
     * 获取excel数据
     *
     * @param string $sheet
     * @return array
     */
    public function getExcelData(string $sheet = 'active'): array
    {
        return $this->getWorksheet($sheet)->toArray();
    }

    /**
     * 解析excel
     *
     * @param array $rules
     * @param string $rules[][title] 表头
     * @param string $rules[][validate] 验证器
     * @param string $rules[][field] 字段名
     * @param string $rules[][type] 数据类型 date:日期格式(Y-m-d H:i:s)
     * @param array $options
     * @param array $options[titleLine] 表头行
     * @param boolean $options[autoRemove] 解析后移除文件
     * @param boolean $options[ignoreNullRow] 忽略空行
     * @return array
     * @date 2022-04-11
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function parseExcel(array $rules, array $options = []): array
    {
        $titleLine = Arr::get($options, 'titleLine', [1]);
        $autoRemove = Arr::get($options, 'autoRemove', true);
        $ignoreNullRow = Arr::get($options, 'ignoreNullRow', true);

        if ($autoRemove) {
            if (is_file($this->path)) {
                unlink($this->path);
            }
        }

        $title = [];
        $_title = [];
        $_field = [];
        $_type = [];
        $_validate = [];
        foreach ($rules as $rule) {
            $_title[] = $rule['title'];
            if (isset($rule['field'])) {
                $_field[] = $rule['field'];
                if (isset($rule['validate'])) {
                    $_validate[join('|', [
                        $rule['field'],
                        $rule['title']
                    ])] = $rule['validate'];
                }
            } else {
                $_field[] = '';
            }
            if (isset($rule['type'])) {
                $_type[] = $rule['type'];
            } else {
                $_type[] = '';
            }
        }
        $Worksheet = $this->getWorksheet();
        $data = $Worksheet->toArray();
        // 获取表头 合并复杂表头
        foreach ($titleLine as $line) {
            $values = $data[$line - 1];
            foreach ($values as $key => $value) {
                if ($value) {
                    $title[$key] = $value;
                }
            }
        }
        // 验证表头
        if ($title != $_title) {
            throw new ErrorMsg("表头不一致 请确认excel是否正确", 1);
        }
        // 去除非数据字段
        $i = max($titleLine) - 1;
        do {
            unset($data[$i]);
            $i--;
        } while ($i >= 0);

        $validate = new Validate;
        $validate->rule($_validate)->batch(true)->failException(true);

        $result = [];
        $error = [];
        foreach ($data as $row => $value) {
            $index = $row + 1;
            $line = "第${index}行";
            $_value = [];
            if ($ignoreNullRow && self::isNullRow($value)) {
                continue;
            }
            foreach ($_field as $column => $field) {
                if ($field) {
                    if ($_type[$column]) {
                        $type = $_type[$column];
                        if (Str::startsWith($type, 'date') && $value[$column]) {
                            $format = 'Y-m-d H:i:s';
                            $types = explode(':', $type);
                            if (isset($types[1])) {
                                $format = $types[1];
                            }
                            $value[$column] = Tool::conversionDateTime($value[$column], $format);
                        }
                    }
                    $_value[$field] = $value[$column];
                }
            }
            // 验证数据
            if ($_validate) {
                try {
                    $validate->check($_value);
                } catch (\Throwable $th) {
                    $error[] = $line . '：' . $th->getMessage();
                }
            }

            $result[$line] = $_value;
        }

        if ($error) {
            throw new ErrorMsg(join(PHP_EOL, $error), 1);
        }

        return $result;
    }

    /**
     * 导出excel
     *
     * @param array $data 数据
     * @param array $options
     * @param boolean $options[autoBorder] 是否自动添加边框
     * @param string $options[freezeLine] 是否自动冻结首行
     * @date 2022-04-11
     * @example
     * @author arw
     * @since 1.0.0
     */
    public static function exportSheet(array $data, array $options = [])
    {
        $excel = new self;
        $excel->setWorksheetStyle($options);
        $spreadsheet = $excel->getSpreadsheet();
        $worksheet = $spreadsheet->getActiveSheet();
        $worksheet->fromArray($data);
        return $excel;
    }

    /**
     * 导出处理图片字段
     */
    public static function ExportImgFiled($value)
    {
        if(strpos($value,"http") !== false){
            $base_url = "";
        }else{
            $base_url = Env::get('APP.DEFAULT_IMG_URL');
        }
        return $base_url . $value;
    }

    /**
     * 导出excel 多sheet
     *
     * @param array $data 数据
     * @param array $options
     * @param boolean $options[autoBorder] 是否自动添加边框
     * @param string $options[freezeLine] 是否自动冻结首行
     * @date 2022-04-11
     * @example
     * @author arw
     * @since 1.0.0
     */
    public static function exportMoreSheet(array $data, array $options = [])
    {
        $excel = new self;
        $excel->setWorksheetStyle($options);
        $spreadsheet = $excel->getSpreadsheet();
        $spreadsheet->removeSheetByIndex(0);
        foreach ($data as $key => $value) {
            $worksheet = new Worksheet($spreadsheet, $key);
            $worksheet->fromArray($value);
            $spreadsheet->addSheet($worksheet);
        }
        $spreadsheet->setActiveSheetIndex(0);
        return $excel;
    }

    /**
     * 保存excel
     *
     * @param string $name 文件名
     * @param string $save 保存路径
     * @date 2022-04-11
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function save(string $name, string $save = 'php://output')
    {
        $spreadsheet = $this->getSpreadsheet();
        $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
        $_name = $name;
        if (!preg_match("/\./", $name)) {
            $_name .= '.Xlsx';
        }
        $worksheets = $spreadsheet->getAllSheets();
        foreach ($worksheets as $worksheet) {
            $this->_setWorksheetStyle($worksheet);
        }
        // 保存前
        $this->trigger('beforeSave', [$spreadsheet]);
        // 浏览器下载
        if ($save === 'php://output') {
            Tool::obEndClean();
            header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
            header('Cache-Control: max-age=0');
            header('Content-Disposition:inline;filename="' . $_name . '"');
            $writer->save($save);
        } else {
            Tool::mkdir($save);
            // 保存到本地目录
            $writer->save(join(DIRECTORY_SEPARATOR, [
                $save, $_name,
            ]));
        }
        $spreadsheet->disconnectWorksheets();
        unset($spreadsheet);
        // 保存后
        $this->trigger('AfterSave');
    }

    /**
     * 保存前
     *
     * @param callable $cb
     * @date 2022-04-11
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function onBeforeSave(callable $cb): self
    {
        return $this->event('beforeSave', $cb);
    }

    /**
     * 保存后
     *
     * @param callable $cb
     * @date 2022-04-11
     * @example
     * @author arw
     * @since 1.0.0
     */
    public function onAfterSave(callable $cb): self
    {
        return $this->event('afterSave', $cb);
    }

    /**
     * 判断是否空行
     *
     * @param array $rows
     * @return boolean
     * @date 2021-11-27
     * @example
     * @author arw
     * @since 1.0.0
     */
    public static function isNullRow(array $rows)
    {
        $rows = array_unique($rows);
        $rows = array_filter($rows, function ($v) {
            return !is_null($v);
        });
        return count($rows) == 0;
    }

    /**
     * 快捷设置excel样式
     * 首行冻结 自动添加边框
     *
     * @param Worksheet $worksheet
     * @return Worksheet $worksheet
     * @date 2021-01-25
     * @author arw
     * @since 1.0.0
     */
    private function _setWorksheetStyle(Worksheet $worksheet): Worksheet
    {
        $options = $this->worksheetStyle;
        $autoBorder = Arr::get($options, 'autoBorder', true);
        $freezeLine = Arr::get($options, 'freezeLine', 'A2');
        $autoBackground = Arr::get($options, 'autoBackground', true);
        if ($freezeLine) {
            $worksheet->freezePane($freezeLine);
        }
        if ($autoBorder) {
            $worksheet->getStyle(join(':', [
                'A1',
                $worksheet->getHighestDataColumn() . $worksheet->getHighestDataRow()
            ]))->applyFromArray([
                'borders' => [
                    'allBorders' => [
                        'borderStyle' => Border::BORDER_THIN, //细边框
                    ],
                ],
            ]);
        }
        if($autoBackground){
            $style = join(':', [
                'A1',
                $worksheet->getHighestDataColumn() . "1"
            ]);
            
            // 列宽
            $worksheet -> getDefaultColumnDimension() -> setWidth(25);
            // $worksheet -> getColumnDimension('A') -> setWidth(30); //设置A列宽度为30
            //行高
            // $worksheet -> getRowDimension(1) -> setRowHeight(25);
            $worksheet -> getDefaultRowDimension() -> setRowHeight(25);

            //样式设置 - 字体
            $worksheet -> getStyle($style) -> getFont() 
            // -> setBold(true)
            ->setName('微软雅黑')
            -> setSize(13);

            //样式设置 - 字体颜色
        //     $worksheet -> getStyle('A1') -> getFont() 
        //    -> getColor() -> setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_RED); //设置单元格A1的字体颜色

            // $font = $worksheet -> getCell('B1') -> getColor() -> setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_RED);

            // if(substr($font,0,1) == "*"){
                
            // }

            // $worksheet -> setCellValue('A2', substr($font,0,1) );
           
            //样式设置 - 水平、垂直居中
            $styleArray = [
                'alignment' => [
                    'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
                    'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER
                ],
            ];
            $worksheet -> getStyle($style) -> applyFromArray($styleArray);

            //样式设置 - 单元格背景颜色
            $worksheet->getStyle($style)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('FFFFFF00');
        }
        return $worksheet;
    }

    /**
     * 数字转字母
     * 1 => A
     *
     * @param integer $index
     * @return string
     * @date 2021-01-23
     * @author arw
     * @since 1.0.0
     */
    public static function stringToIndex(int $index)
    {
        return Coordinate::stringFromColumnIndex($index);
    }

    /**
     * 字母转数字
     * A => 1
     *
     * @param string $string
     * @return int
     * @date 2021-01-23
     * @author arw
     * @since 1.0.0
     */
    public static function indexToString(string $string)
    {
        return Coordinate::columnIndexFromString($string);
    }
}
