<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者，助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件，未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace app\services\order;


use app\jobs\OrderInvoiceJob;
use app\services\BaseServices;
use app\services\serve\ServeServices;
use crmeb\exceptions\AdminException;
use crmeb\exceptions\ApiException;
use app\dao\order\StoreOrderInvoiceDao;
use app\services\user\UserInvoiceServices;
use think\facade\Log;


/**
 * Class StoreOrderInvoiceServices
 * @package app\services\order
 */
class StoreOrderInvoiceServices extends BaseServices
{
    /**
     * LiveAnchorServices constructor.
     * @param StoreOrderInvoiceDao $dao
     */
    public function __construct(StoreOrderInvoiceDao $dao)
    {
        $this->dao = $dao;
    }

    public function chart(array $where)
    {
        $where['is_pay'] = 1;
        $where['is_del'] = 0;
        //全部
        $data['all'] = (string)$this->dao->count($where);
        //待开
        $where['type'] = 1;
        $data['noOpened'] = (string)$this->dao->count($where);
        //已开
        $where['type'] = 2;
        $data['opened'] = (string)$this->dao->count($where);
        //退款
        $where['type'] = 3;
        $data['refund'] = (string)$this->dao->count($where);
        $data['elec_invoice'] = (int)sys_config('elec_invoice', 0);
        return $data;
    }

    /**
     * 后台获取开票列表
     * @param array $where
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getList(array $where)
    {
        [$page, $list] = $this->getPageValue();
        $field = 'id as invoice_id,order_id,header_type,type,name,duty_number,drawer_phone,email,tell,address,bank,card_number,is_invoice,invoice_number,remark as invoice_reamrk,invoice_time,add_time as invoice_add_time,unique_num,invoice_num,invoice_type,invoice_serial_number,red_invoice_num';
        $where['is_pay'] = 1;
        $where['is_del'] = 0;
        $list = $this->dao->getList($where, $field, ['order' => function ($query) {
            $query->field('id,order_id,pay_price,add_time,real_name,user_phone,status,refund_status');
        }], 'add_time desc', $page, $list);
        foreach ($list as &$item) {
            $item['id'] = $item['order']['id'] ?? 0;
            $item['order_id'] = $item['order']['order_id'] ?? '';
            $item['pay_price'] = $item['order']['pay_price'] ?? 0.00;
            $item['real_name'] = $item['order']['real_name'] ?? '';
            $item['user_phone'] = $item['order']['user_phone'] ?? '';
            $item['status'] = $item['order']['status'] ?? '';
            $item['refund_status'] = $item['order']['refund_status'] ?? 0;
            $item['add_time'] = date('Y-m-d H:i:s', $item['order']['add_time'] ?? $item['invoice_add_time'] ?? time());
            $item['invoice_add_time'] = date('Y-m-d H:i:s', $item['invoice_add_time']);
        }
        $count = $this->dao->count($where);
        return compact('list', 'count');
    }

    /**
     * 前端获取开票列表（带商品信息）
     * @param $where
     * @return array
     */
    public function getOrderInvoiceList(array $where)
    {
        [$page, $list] = $this->getPageValue();
        $where['is_pay'] = 1;
        $where['is_del'] = 0;
        $list = $this->dao->getList($where, '*', ['order'], 'add_time desc', $page, $list);
        /** @var StoreOrderServices $storeOrderServices */
        $storeOrderServices = app()->make(StoreOrderServices::class);
        foreach ($list as &$item) {
            if (isset($item['order']) && $item['order']) {
                $item['order'] = $storeOrderServices->tidyOrder($item['order'], true);
                if (isset($item['order']['_status']['_type']) && $item['order']['_status']['_type'] == 3) {
                    foreach ($item['order']['cartInfo'] ?: [] as $key => $product) {
                        $item['order']['cartInfo'][$key]['add_time'] = isset($product['add_time']) ? date('Y-m-d H:i', (int)$product['add_time']) : '时间错误';
                    }
                }
            }
        }
        return $list;
    }

    /**
     * 订单申请开票
     * @param int $uid
     * @param $order_id
     * @param int $invoice_id
     * @return mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function makeUp(int $uid, $order_id, int $invoice_id)
    {
        if (!$order_id) throw new AdminException(100100);
        if (!$invoice_id) throw new AdminException(410325);

        /** @var StoreOrderServices $storeOrderServices */
        $storeOrderServices = app()->make(StoreOrderServices::class);
        /** @var UserInvoiceServices $userInvoiceServices */
        $userInvoiceServices = app()->make(UserInvoiceServices::class);
        $order = $storeOrderServices->getOne(['order_id|id' => $order_id, 'is_del' => 0]);
        if (!$order) {
            throw new AdminException(410173);
        }
        //检测再带查询
        $invoice = $userInvoiceServices->checkInvoice($invoice_id, $uid);

        if ($this->dao->getOne(['order_id' => $order['id'], 'uid' => $uid])) {
            throw new AdminException(410249);
        }
        if ($order['refund_status'] == 2) {
            throw new AdminException(410226);
        }
        if ($order['refund_status'] == 1) {
            throw new AdminException(410250);
        }
        unset($invoice['id'], $invoice['add_time']);
        $data = [];
        $data['category'] = 'order';
        $data['order_id'] = $order['id'];
        $data['invoice_id'] = $invoice_id;
        $data['add_time'] = time();
        $data['is_pay'] = $order['paid'] == 1 ? 1 : 0;
        $data = array_merge($data, $invoice);
        if (!$re = $this->dao->save($data)) {
            throw new AdminException(410251);
        }
        if (sys_config('elec_invoice', 1) == 1 && sys_config('auto_invoice', 1) == 1 && $data['is_pay'] == 1) {
            //自动开票
            OrderInvoiceJob::dispatchSecs(10, 'autoInvoice', [$re->id]);
        }

        //自定义事件-申请开票
        event('CustomEventListener', ['order_invoice', [
            'uid' => $uid,
            'order_id' => $order_id,
            'phone' => $order['user_phone'],
            'invoice_id' => $re->id,
            'add_time' => date('Y-m-d H:i:s'),
        ]]);

        return ['id' => $re->id];
    }

    public function setInvoice(int $id, array $data)
    {
        $orderInvoice = $this->dao->get($id);
        if (!$orderInvoice) {
            throw new AdminException(100026);
        }
        if ($data['is_invoice'] == 1) {
            $data['invoice_time'] = time();
        }
        if (!$this->dao->update($id, $data, 'id')) {
            throw new AdminException(100015);
        }
        return true;
    }

    /**
     * 拆分订单同步拆分申请开票记录
     * @param int $oid
     * @return bool
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function splitOrderInvoice(int $oid)
    {
        /** @var StoreOrderServices $storeOrderServices */
        $storeOrderServices = app()->make(StoreOrderServices::class);
        $orderInfo = $storeOrderServices->getOne(['id' => $oid, 'is_del' => 0]);
        if (!$orderInfo) {
            throw new AdminException(410173);
        }
        $pid = $orderInfo['pid'] > 0 ? $orderInfo['pid'] : $orderInfo['id'];
        //查询开票记录
        $orderInvoice = $this->dao->get(['order_id' => $oid]);
        //查询子订单
        $spliteOrder = $storeOrderServices->getColumn(['pid' => $pid, 'is_system_del' => 0], 'id,order_id');
        if ($spliteOrder && $orderInvoice) {
            $data = $orderInvoice->toArray();
            unset($data['id']);
            $data['add_time'] = strtotime($data['add_time']);
            $data_all = [];
            foreach ($spliteOrder as $order) {
                if (!$this->dao->count(['order_id' => $order['id']])) {
                    $data['order_id'] = $order['id'];
                    $data_all[] = $data;
                }
            }
            if ($data_all) {
                $this->transaction(function () use ($data_all, $orderInvoice, $orderInfo) {
                    $this->dao->saveAll($data_all);
                    if ($orderInfo['pid'] <= 0) {
                        $this->dao->update(['id' => $orderInvoice['id']], ['is_del' => 1]);
                    }
                });
            }
        }
        return true;
    }

    /**
     * 开具发票
     * @param $id
     * @return bool
     * @throws \ReflectionException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @author wuhaotian
     * @email 442384644@qq.com
     * @date 2024/5/15
     */
    public function invoiceIssuance($id)
    {
        if (sys_config('elec_invoice', 1) != 1) {
            return app('json')->fail('电子发票功能未开启，请在一号通中开启并且在商城后台一号通配置中开启');
        }
        $info = $this->dao->getOne(['id' => $id]);
        $orderInfo = app()->make(StoreOrderServices::class)->get($info['order_id']);
        $cartInfo = app()->make(StoreOrderCartInfoServices::class)->getOrderCartInfo($orderInfo['id']);
        $data = [];
        $data['is_tax_inclusive'] = 1;
        $data['account_name'] = $info['name'];
        $data['bank_name'] = '';
        $data['bank_account'] = '';
        $data['telephone'] = $info['drawer_phone'];
        $data['company_address'] = '';
        $data['drawer'] = '';
        $data['amount'] = $orderInfo['pay_price'];
        if ($info['header_type'] == 1) {
            $data['invoice_type'] = 82;
            $data['tax_id'] = '';
            $data['is_enterprise'] = 0;
        } else {
            $data['invoice_type'] = 81;
            $data['tax_id'] = $info['duty_number'];
            $data['is_enterprise'] = 1;
        }
        $goods = [];
        foreach ($cartInfo as $item) {
            $goods[] = [
                'cate_id' => sys_config('elec_invoice_cate', 1799),
                'store_name' => $item['cart_info']['productInfo']['store_name'],
                'unit_price' => bcadd($item['cart_info']['sum_price'], $item['cart_info']['postage_price'], 2),
                'num' => $item['cart_info']['cart_num'],
                'tax_rate' => sys_config('elec_invoice_tax_rate', 13),
            ];
        }
        $data['goods'] = $goods;
        try {
            $invoice = app()->make(ServeServices::class)->invoice();
            $res = $invoice->invoiceIssuance($orderInfo['order_id'], $data);
            if ($res['status'] == 200) {
                $this->dao->update($id, [
                    'is_invoice' => 1,
                    'unique_num' => $orderInfo['order_id'],
                    'invoice_num' => $res['data']['invoice_num'],
                    'invoice_type' => $res['data']['invoice_type'],
                    'invoice_serial_number' => $res['data']['invoice_serial_number'],
                    'invoice_time' => time()
                ]);
            }
        } catch (\Exception $e) {
            Log::error('自动开具发票失败，失败原因：' . $e->getMessage());
        }
        return true;
    }

    /**
     * 未开发票自动开具电子发票
     * @return bool
     * @author wuhaotian
     * @email 442384644@qq.com
     * @date 2024/5/15
     */
    public function autoInvoice()
    {
        if (sys_config('elec_invoice', 1) != 1) {
            return true;
        }
        if (sys_config('auto_invoice', 1) == 1) {
            $list = $this->dao->getColumn([
                ['is_pay', '=', 1],
                ['is_invoice', '=', 1],
                ['unique_num', '=', ''],
                ['is_del', '=', 0],
                ['add_time', '<', time() - 60],
            ], 'id');
            if ($list) {
                foreach ($list as $item) {
                    //自动开票
                    OrderInvoiceJob::dispatchSecs(10, 'autoInvoice', [$item]);
                }
            }
        }
        return true;
    }

    /**
     * 退款订单自动冲红
     * @return bool
     * @author wuhaotian
     * @email 442384644@qq.com
     * @date 2024/5/15
     */
    public function autoInvoiceRed()
    {
        if (sys_config('elec_invoice', 1) != 1) {
            return true;
        }
        if (sys_config('auto_invoice', 1) == 1) {
            $list = $this->dao->getColumn([
                ['is_pay', '=', 1],
                ['is_invoice', '=', 1],
                ['unique_num', '<>', ''],
                ['red_invoice_num', '=', ''],
                ['is_del', '=', 1],
                ['add_time', '<', time() - 60],
            ], 'id');
            if ($list) {
                foreach ($list as $item) {
                    //自动冲红
                    OrderInvoiceJob::dispatchSecs(10, 'autoInvoiceRed', [$item]);
                }
            }
        }
        return true;
    }

    /**
     * 负数发票开具
     * @param $id
     * @return bool
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @author wuhaotian
     * @email 442384644@qq.com
     * @date 2024/5/16
     */
    public function redInvoiceIssuance($id)
    {
        $invoiceInfo = $this->dao->get($id);
        if ($invoiceInfo['is_pay'] == 0 || $invoiceInfo['is_invoice'] == 0 || $invoiceInfo['unique_num'] == '') {
            throw new AdminException('发票状态有误，请检查');
        }
        $invoice = app()->make(ServeServices::class)->invoice();
        $redInfo = $invoice->applyRedInvoice(['invoice_num' => $invoiceInfo['invoice_num'], 'apply_type' => '01']);
        if ($redInfo['status'] != 200) throw new AdminException('申请红字发票失败，请检查');
        $res = $invoice->redInvoiceIssuance(['invoice_num' => $invoiceInfo['invoice_num'], 'red_number' => $redInfo['data']['red_number']]);
        if ($res['status'] != 200) throw new AdminException('开具负数发票失败，请检查');
        $this->dao->update($id, ['red_invoice_num' => $redInfo['data']['red_number']]);
        return true;
    }
}
