<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;

function Mirai_pointsTable($name) {
    $db = \Typecho\Db::get();
    return $db->getPrefix() . $name;
}

function Mirai_pointsEnabled() {
    $options = Mirai_opt();
    return isset($options->pointsEnable) && $options->pointsEnable === '1';
}

function Mirai_pointsName() {
    $options = Mirai_opt();
    return isset($options->pointsName) && $options->pointsName !== '' ? $options->pointsName : '积分';
}

function Mirai_pointsGetBalance($uid = 0) {
    if (!$uid) {
        $user = Mirai_user();
        if (!$user->hasLogin()) return 0;
        $uid = $user->uid;
    }
    $db = \Typecho\Db::get();
    $row = $db->fetchRow($db->select('points')->from('table.users')->where('uid = ?', $uid));
    return (int)($row['points'] ?? 0);
}

function Mirai_pointsGetAccount($uid) {
    $db = \Typecho\Db::get();
    $walletTable = Mirai_pointsTable('mirai_pay_wallets');
    $row = $db->fetchRow($db->select()->from($walletTable)->where('uid = ?', $uid));
    if (!$row) {
        $now = time();
        try {
            $db->query($db->insert($walletTable)->rows([
                'uid' => $uid,
                'balance' => 0,
                'points' => 0,
                'points_total_earned' => 0,
                'points_total_spent' => 0,
                'updated' => $now,
            ]));
        } catch (\Exception $e) {}
        $row = ['uid' => $uid, 'balance' => 0, 'points' => 0, 'points_total_earned' => 0, 'points_total_spent' => 0, 'updated' => $now];
    }
    return $row;
}

function Mirai_pointsAdjust($uid, $amount, $action, $refType = null, $refId = null, $remark = '', $orderNo = '') {
    if (!$uid || $amount == 0) return ['success' => false, 'msg' => '参数无效'];

    if ($refType !== null || $refId !== null) {
        $dedup = Mirai_pointsCheckDedup($uid, $refType ?: '', $refId ?: 0, $action);
        if (!$dedup['allowed']) {
            return ['success' => false, 'msg' => '已获取过该奖励'];
        }
    }

    $db = \Typecho\Db::get();
    $lockKey = 'mirai_points_' . $uid;
    $locked = false;
    if (function_exists('Mirai_payAcquireLock')) {
        $locked = Mirai_payAcquireLock($lockKey);
    }

    try {
        $account = Mirai_pointsGetAccount($uid);
        $balanceBefore = (int)$account['points'];
        $balanceAfter = $balanceBefore + $amount;

        if ($balanceAfter < 0) {
            if ($locked && function_exists('Mirai_payReleaseLock')) Mirai_payReleaseLock($lockKey);
            return ['success' => false, 'msg' => Mirai_pointsName() . '不足'];
        }

        $now = time();
        $walletTable = Mirai_pointsTable('mirai_pay_wallets');

        $totalEarned = (int)$account['points_total_earned'];
        $totalSpent = (int)$account['points_total_spent'];
        if ($amount > 0) {
            $totalEarned += $amount;
        } else {
            $totalSpent += abs($amount);
        }

        $db->query($db->update($walletTable)->rows([
            'points' => $balanceAfter,
            'points_total_earned' => $totalEarned,
            'points_total_spent' => $totalSpent,
            'updated' => $now,
        ])->where('uid = ?', $uid));

        $db->query($db->update('table.users')->rows(['points' => $balanceAfter])->where('uid = ?', $uid));

        $logTable = Mirai_pointsTable('mirai_points_logs');
        $db->query($db->insert($logTable)->rows([
            'uid' => $uid,
            'action' => $action,
            'amount' => $amount,
            'balance_before' => $balanceBefore,
            'balance_after' => $balanceAfter,
            'ref_type' => $refType,
            'ref_id' => $refId,
            'remark' => $remark,
            'created' => $now,
        ]));

        if (function_exists('Mirai_payAdjustBalance')) {
            $walletLogType = $amount > 0 ? 'points_earn' : 'points_spend';
            Mirai_payAdjustBalance($uid, 0, $walletLogType, $remark ?: $action, $orderNo);
        }

        Mirai_pointsUpdateDaily($uid, $amount, $action);

        if ($locked && function_exists('Mirai_payReleaseLock')) Mirai_payReleaseLock($lockKey);

        return ['success' => true, 'balance_before' => $balanceBefore, 'balance_after' => $balanceAfter, 'amount' => $amount];
    } catch (\Exception $e) {
        if ($locked && function_exists('Mirai_payReleaseLock')) Mirai_payReleaseLock($lockKey);
        return ['success' => false, 'msg' => $e->getMessage()];
    }
}

function Mirai_pointsUpdateDaily($uid, $amount, $action) {
    $db = \Typecho\Db::get();
    $dailyTable = Mirai_pointsTable('mirai_points_daily');
    $today = date('Y-m-d');
    $now = time();

    $isFree = !in_array($action, ['purchase', 'points_pay', 'exchange_vip']);

    $row = $db->fetchRow($db->select()->from($dailyTable)->where('uid = ? AND date = ?', $uid, $today));
    if ($row) {
        $updateData = ['updated' => $now];
        if ($amount > 0) {
            $updateData['points_total'] = (int)$row['points_total'] + $amount;
            if ($isFree) {
                $updateData['points_free'] = (int)$row['points_free'] + $amount;
            }
        } else {
            $updateData['points_spent'] = (int)$row['points_spent'] + abs($amount);
        }
        $db->query($db->update($dailyTable)->rows($updateData)->where('id = ?', $row['id']));
    } else {
        $data = [
            'uid' => $uid,
            'date' => $today,
            'points_free' => ($amount > 0 && $isFree) ? $amount : 0,
            'points_total' => $amount > 0 ? $amount : 0,
            'points_spent' => $amount < 0 ? abs($amount) : 0,
            'exp_free' => 0,
            'exp_total' => 0,
            'created' => $now,
            'updated' => $now,
        ];
        $db->query($db->insert($dailyTable)->rows($data));
    }
}

function Mirai_pointsIsAllowFree($uid) {
    $options = Mirai_opt();
    $dailyLimit = isset($options->pointsDailyLimit) ? (int)$options->pointsDailyLimit : 100;
    if ($dailyLimit <= 0) return true;

    $db = \Typecho\Db::get();
    $dailyTable = Mirai_pointsTable('mirai_points_daily');
    $today = date('Y-m-d');
    $row = $db->fetchRow($db->select('points_free')->from($dailyTable)->where('uid = ? AND date = ?', $uid, $today));
    $todayFree = (int)($row['points_free'] ?? 0);

    return $todayFree < $dailyLimit;
}

function Mirai_pointsGetDaily($uid, $date = null) {
    $db = \Typecho\Db::get();
    $dailyTable = Mirai_pointsTable('mirai_points_daily');
    if (!$date) $date = date('Y-m-d');
    $row = $db->fetchRow($db->select()->from($dailyTable)->where('uid = ? AND date = ?', $uid, $date));
    return $row ?: null;
}

function Mirai_pointsGetLogs($uid, $page = 1, $pageSize = 20) {
    $db = \Typecho\Db::get();
    $logTable = Mirai_pointsTable('mirai_points_logs');
    $offset = ($page - 1) * $pageSize;

    $count = $db->fetchRow($db->select(['COUNT(*)' => 'cnt'])->from($logTable)->where('uid = ?', $uid));
    $total = (int)($count['cnt'] ?? 0);

    $rows = $db->fetchAll($db->select()->from($logTable)
        ->where('uid = ?', $uid)
        ->order('created', \Typecho\Db::SORT_DESC)
        ->limit($pageSize)
        ->offset($offset));

    return ['list' => $rows, 'total' => $total, 'page' => $page, 'pageSize' => $pageSize];
}

function Mirai_pointsCheckDedup($uid, $targetType, $targetId, $action, $maxCount = 0) {
    $db = \Typecho\Db::get();
    $dedupTable = Mirai_pointsTable('mirai_points_dedup');

    $row = $db->fetchRow($db->select()->from($dedupTable)
        ->where('uid = ? AND target_type = ? AND target_id = ? AND action = ?', $uid, $targetType, $targetId, $action));

    if ($row) {
        if ($maxCount > 0 && (int)$row['count'] < $maxCount) {
            $db->query($db->update($dedupTable)->rows([
                'count' => (int)$row['count'] + 1,
            ])->where('id = ?', $row['id']));
            return ['allowed' => true, 'count' => (int)$row['count'] + 1];
        }
        return ['allowed' => false, 'count' => (int)$row['count']];
    }

    $db->query($db->insert($dedupTable)->rows([
        'uid' => $uid,
        'target_type' => $targetType,
        'target_id' => $targetId,
        'action' => $action,
        'count' => 1,
        'created' => time(),
    ]));
    return ['allowed' => true, 'count' => 1];
}

function Mirai_pointsGetTaskValue($taskKey) {
    $options = Mirai_opt();
    $field = 'pointsTask_' . $taskKey;
    return isset($options->$field) ? (int)$options->$field : 0;
}
