<?php

namespace app\service;

use app\model\Account;
use app\model\ClockLog;
use app\model\Log;
use app\model\OvertimeLog;
use app\model\PayLog;
use app\model\PayMonthLog;
use think\facade\Db;
use think\Model;

/**
 * 薪资计算
 *
 * Class Pay
 * @package app\service
 */
class Pay
{
    // 基本逻辑 工人工资=基本工资+加班工资 一个月的工资=有效打卡的天数*基本工资+加班工资(=天工资/6*加班小时)
    //基本工资在员工入职就要填入
    //基本工资按天计算,打卡确认一天算一天
    //加班工资按6小时为一天:天工资÷6小时=每小时工资

    /*
        基本工资按天计算,打卡确认一天算一天,可能会出现的漏洞(本期暂不考虑):
        晚上加班,如23:10打上班卡,第二天03:10打下班卡,这样就会认为上班2天。
        若正常一天,必须至少要有上班和下班两次打卡的话,上述则两天都不算上班
     */


    //            $sql = "
    //            insert into bee_pay_log(`account_id`, `worksite_id`,`time`, `year`, `month`, `day`)
    //            select ".$item['account_id'].",".$item['worksite_id'].",".$item['day'].",".$year.",".$month.",".$day." from DUAL
    //            where not exists (select `account_id`, `worksite_id`,`time` from bee_pay_log where `account_id` = ".$item['account_id']." AND `worksite_id` = ".$item['worksite_id']." AND `time` = ".$item['day'].")
    //";
    //            Db::execute($sql);

    // 生成工人每个工地月薪资汇总


    /**
     * 汇总指定日期间的工资
     * 汇总到pay_log表
     *
     * @param  int  $begin  开始时间 为0则不限制 格式为Ymd 如20221103
     * @param  int  $end  结束时间 为0则不限制 格式为Ymd 如20221103
     */
    public static function statistic(int $begin = 0, int $end = 0)
    {
        set_time_limit(0);
        $where = [];
        if ($begin) {
            $where[] = ['l.day', '>=', $begin];
        }

        if ($end) {
            $where[] = ['l.day', '<=', $end];
        }

        // 打卡记录表 每300条处理一次 打卡记录表中同一个indexs只计算一次
        ClockLog::alias('l')
            ->leftJoin('account a', 'a.id = l.account_id')
            ->leftJoin('pay_log pl', 'pl.indexs = l.indexs')
            ->where('l.is_statistic', ClockLog::COMMON_OFF)
            ->where('l.status', ClockLog::COMMON_ON)
            ->where('l.need_statistic', ClockLog::COMMON_ON)
            ->where('l.role', ClockLog::COMMON_ON)//工人
            ->where('a.pay', '>', 0)
            ->where('l.handle_count', '<=', 10) //查询处理次数10次以下
            ->where($where)
            ->group('l.indexs')
            ->fieldRaw('count(l.id),l.id,l.account_id,l.worksite_id,l.created_at,l.day,l.indexs,a.pay,pl.id as pay_log_id,pl.base_amount_count')
            ->chunk(300, function ($items) {
                $update     = [];
                $indexsList = [];
                $ids = $items->column('id');
                \think\facade\Log::write($ids);
                // 每查询一次就增加一次handle_count 避免未处理成功后 死循环一直查询
                (new ClockLog)->whereIn('id', $ids)->save([
                    'handle_count' => Db::raw('`handle_count` + 1')
                ]);
                foreach ($items as $item) {
                    if ($item['pay_log_id'] && $item['base_amount_count'] == 0) {
                        $update[]     = [
                            'id'                => $item['pay_log_id'],
                            'amount'            => Db::raw('`amount` + '.$item['pay']),
                            'base_amount'       => Db::raw('`base_amount` + '.$item['pay']),
                            'remarks'           => Db::raw('CONCAT_WS(char(10), `remarks`,"基本工资入账 '.$item['pay'].'元")'),
                            'base_amount_count' => 1,
                        ];
                        $indexsList[] = $item['indexs'];
                    }
                }

                if (!empty($update)) {
                    //                    \think\facade\Log::write($update);
                    // 启动事务
                    Db::startTrans();
                    try {
                        (new PayLog())->saveAll($update);
                        ClockLog::whereIn('indexs', $indexsList)->update(['is_statistic' => ClockLog::COMMON_ON]);
                        Db::commit();
                    } catch (\Exception $e) {
                        Db::rollback();
                        \think\facade\Log::error('批量统计基本工资失败'.$e->getMessage().' file:'.$e->getFile().' line:'.$e->getLine());
                    }

                }
            }, 'pl.id', 'desc');
        // 加班记录表 每300条处理一次
        OvertimeLog::alias('l')
            ->leftJoin('account a', 'a.id = l.account_id')
            ->leftJoin('pay_log pl', 'pl.indexs = l.indexs')
            ->where('l.is_statistic', ClockLog::COMMON_OFF)
            ->where('l.status', ClockLog::COMMON_ON)
            ->where('a.pay', '>', 0)
            ->where('l.handle_count', '<=', 10) //查询处理次数10次以下
            ->where($where)
            ->fieldRaw('l.id,l.account_id,l.worksite_id,l.created_at,l.`day`,l.time,l.indexs,a.pay,pl.id as pay_log_id,pl.overtime_amount_count')
            ->chunk(300, function ($items) {
                $update = [];
                $ids    = [];

                $handleIds = $items->column('id');
                // 每查询一次就增加一次handle_count 避免未处理成功后 死循环一直查询
                (new OvertimeLog)->whereIn('id', $handleIds)->save([
                    'handle_count' => Db::raw('`handle_count` + 1')
                ]);
                foreach ($items as $item) {
                    if ($item['pay_log_id']) {
                        // 加班工资=基本工资/6小时 * 加班小时
                        $overtimeAmount = round(Math::mul(Math::div($item['pay'], 6), $item['time']), 2);
                        $update[]       = [
                            'id'                    => $item['pay_log_id'],
                            'amount'                => Db::raw('`amount` + '.$overtimeAmount),
                            'overtime_amount'       => Db::raw('`overtime_amount` + '.$overtimeAmount),
                            'remarks'               => Db::raw('CONCAT_WS(char(10), `remarks`,"加班工资入账 '.$overtimeAmount.'元")'),
                            'overtime_amount_count' => Db::raw('`overtime_amount_count` + 1'),
                        ];
                        $ids[]          = $item['id'];
                    }
                }

                //                \think\facade\Log::write($update);
                if (!empty($update)) {
                    Db::startTrans();
                    try {
                        (new PayLog())->saveAll($update);
                        OvertimeLog::whereIn('id', $ids)->update(['is_statistic' => OvertimeLog::COMMON_ON]);
                        Db::commit();
                    } catch (\Exception $e) {
                        Db::rollback();
                        \think\facade\Log::error('批量统计加班工资失败'.$e->getMessage().' file:'.$e->getFile().' line:'.$e->getLine());
                    }
                }
            }, 'l.id', 'desc');
    }

    /**
     * 生成月份工资记录
     *
     * @param  int  $time  生成时间 按月份 格式为年月 如202209 date('Ym')
     * @return bool
     */
    public static function generateMonthLog(int $time = 0): bool
    {
        if ($time == 0) {
            // 默认生成上个月
            $time = date('Ym', strtotime('last month'));
        }
        $timestamp = strtotime($time.'01');
        $year      = date('Y', $timestamp);
        $month     = date('m', $timestamp);

        $where = [
            ['year', '=', $year],
            ['month', '=', $month],
            ['role', '=', Account::ROLE_WORKER],
        ];

        // 每次统计 都讲之前的结果清理,插入新计算结果
        $insert = [];

        // 每日工资记录(基本工资和加班工资)
        PayLog::where($where)
            ->chunk(300, function ($items) use (&$insert, $time, $year, $month) {
                foreach ($items as $item) {
                    $indexs = $item['account_id'].'-'.$item['worksite_id'].'-'.$time;
                    if (!isset($insert[$indexs])) {
                        $insert[$indexs] = [
                            'account_id'  => $item['account_id'],
                            'worksite_id' => $item['worksite_id'],
                            'indexs'      => $indexs,
                            'time'        => $time,
                            'year'        => $year,
                            'month'       => $month,
                            'created_at'  => date('Y-m-d H:i:s'),

                            'amount'          => $item['amount'],
                            'base_amount'     => $item['base_amount'],
                            'overtime_amount' => $item['overtime_amount'],
                        ];
                    } else {
                        $insert[$indexs]['amount']          = $insert[$indexs]['amount'] + $item['amount'];
                        $insert[$indexs]['base_amount']     = $insert[$indexs]['base_amount'] + $item['base_amount'];
                        $insert[$indexs]['overtime_amount'] = $insert[$indexs]['overtime_amount'] + $item['overtime_amount'];
                    }
                }
            });

        if (!empty($insert)) {
            //                    \think\facade\Log::write($insert);
            // 启动事务
            Db::startTrans();
            try {
                PayMonthLog::where('time', $time)->delete();
                (new PayMonthLog())->insertAll($insert);
                Db::commit();
            } catch (\Exception $e) {
                Db::rollback();
                \think\facade\Log::error('统计['.$time.']工资失败'.$e->getMessage().' file:'.$e->getFile().' line:'.$e->getLine());
            }
        }

        return true;
    }
}