<?php

namespace app\admin\controller\order;

use app\common\controller\Backend;
use app\common\library\finance\DepositSettlement;
use app\common\library\finance\MockPay;
use RuntimeException;
use support\think\Db;
use support\Response;
use Throwable;
use Webman\Http\Request as WebmanRequest;

/**
 * 充值订单
 *
 * 模拟支付流程：用户确认支付后 status=3（待审核），管理员 approve 后由 DepositSettlement 入账。
 * 编辑入口用于查看详情；审核通过/驳回走 approve/reject。
 */
class DepositOrder extends Backend
{
    protected ?object $model = null;

    protected bool $modelValidate = true;

    protected bool $modelSceneValidate = true;

    protected string|array $quickSearchField = ['id', 'order_no', 'pay_channel', 'remark', 'deposit_tier_id', 'idempotency_key'];

    protected string|array $defaultSortField = ['id' => 'desc'];

    protected string|array $orderGuarantee = ['id' => 'desc'];

    protected array $withJoinTable = ['user', 'channel', 'reviewAdmin'];

    protected function initController(WebmanRequest $request): ?Response
    {
        $this->model = new \app\common\model\DepositOrder();
        return null;
    }

    protected function _index(): Response
    {
        if ($this->request && $this->request->get('select')) {
            return $this->select($this->request);
        }

        list($where, $alias, $limit, $order) = $this->queryBuilder();
        $table = strtolower($this->model->getTable());
        $mainShort = $alias[$table] ?? '';
        if ($mainShort !== '' && $this->auth && !$this->auth->isSuperAdmin()) {
            $where[] = ['user.admin_id', 'in', $this->scopedAdminIds()];
        }
        $this->appendDepositOrderIndexWhere($where, $mainShort);

        $res = $this->model
            ->withJoin($this->withJoinTable, $this->withJoinType)
            ->with($this->withJoinTable)
            ->visible([
                'user' => ['username', 'phone'],
                'channel' => ['name'],
                'reviewAdmin' => ['username'],
            ])
            ->alias($alias)
            ->where($where)
            ->order($order)
            ->paginate($limit);

        return $this->success('', [
            'list'   => $res->items(),
            'total'  => $res->total(),
            'remark' => get_route_remark(),
        ]);
    }

    /**
     * 子类可追加列表过滤条件（例如仅展示已注册充值渠道的订单）
     *
     * @param list<array<mixed>> $where
     */
    protected function appendDepositOrderIndexWhere(array &$where, string $mainShort): void
    {
    }

    /**
     * GET 时返回关联信息，便于前端详情弹窗直接渲染 user.username / channel.name；
     * POST 一律拒绝，保证充值订单的金额/状态只能由结算服务变更。
     */
    protected function _edit(): Response
    {
        $pk = $this->model->getPk();
        $id = $this->request ? ($this->request->post($pk) ?? $this->request->get($pk)) : null;
        if ($id === null || $id === '') {
            return $this->error(__('Parameter error'));
        }

        if ($this->request && $this->request->method() === 'POST') {
            return $this->error(__('Please use approve/reject buttons to complete the review'));
        }

        $row = $this->loadWithRelations(intval(strval($id)));
        if (!$row) {
            return $this->error(__('Record not found'));
        }
        if (!$this->checkChannelScoped($row)) {
            return $this->error(__('You have no permission'));
        }

        return $this->success('', ['row' => $row]);
    }

    private function loadWithRelations(int $id): ?array
    {
        $row = $this->model
            ->withJoin($this->withJoinTable, $this->withJoinType)
            ->with($this->withJoinTable)
            ->visible([
                'user'        => ['username', 'phone', 'admin_id'],
                'channel'     => ['name'],
                'reviewAdmin' => ['username'],
            ])
            ->where($this->model->getTable() . '.id', $id)
            ->find();
        if (!$row) {
            return null;
        }
        return $row->toArray();
    }

    private function checkChannelScoped(array $row): bool
    {
        if (!$this->auth || $this->auth->isSuperAdmin()) {
            return true;
        }
        $userRow = $row['user'] ?? null;
        if (!is_array($userRow)) {
            return false;
        }
        $adminIdRaw = $userRow['admin_id'] ?? null;
        if ($adminIdRaw === null || $adminIdRaw === '') {
            return false;
        }
        if (!is_numeric(strval($adminIdRaw))) {
            return false;
        }
        return in_array(intval(strval($adminIdRaw)), $this->scopedAdminIds(), true);
    }

    /**
     * 当前管理员可见的管理员ID集合（本人 + 下级角色组内管理员）
     *
     * @return int[]
     */
    private function scopedAdminIds(): array
    {
        if (!$this->auth) {
            return [0];
        }
        if ($this->auth->isSuperAdmin()) {
            return [];
        }
        $groupIds = $this->auth->getAdminChildGroups();
        $adminIds = $groupIds ? $this->auth->getGroupAdmins($groupIds) : [];
        $adminIds[] = $this->auth->id;
        $adminIds = array_map(static fn($id) => intval(strval($id)), $adminIds);
        $adminIds = array_values(array_unique(array_filter($adminIds, static fn($id) => $id > 0)));
        return $adminIds === [] ? [0] : $adminIds;
    }

    /**
     * 审核通过：将待审核订单结算入账（status 3 -> 1）
     */
    public function approve(WebmanRequest $request): Response
    {
        $response = $this->initializeBackend($request);
        if ($response !== null) {
            return $response;
        }
        if ($request->method() !== 'POST') {
            return $this->error(__('Parameter error'));
        }

        $id = $this->intParam($request->post('id'));
        if ($id <= 0) {
            return $this->error(__('Parameter error'));
        }

        $scoped = $this->loadWithRelations($id);
        if (!$scoped) {
            return $this->error(__('Record not found'));
        }
        if (!$this->checkChannelScoped($scoped)) {
            return $this->error(__('You have no permission'));
        }

        $order = Db::name('deposit_order')->where('id', $id)->find();
        if (!$order) {
            return $this->error(__('Record not found'));
        }

        $currentStatus = $this->intParam($order['status'] ?? 0);
        if ($currentStatus !== MockPay::DEPOSIT_STATUS_PENDING_REVIEW) {
            return $this->error(__('This order has already been reviewed'));
        }

        $now = time();
        $adminId = $this->intParam($this->auth->id ?? 0);
        $adminName = $this->adminDisplayName();
        $extraRemark = '管理员（' . $adminName . '）审核通过并入账';

        try {
            DepositSettlement::settle(
                $id,
                DepositSettlement::SOURCE_ADMIN_APPROVE,
                'admin approve mock deposit',
                $adminId > 0 ? $adminId : null,
                $extraRemark
            );
        } catch (RuntimeException $e) {
            return $this->error($e->getMessage());
        } catch (Throwable $e) {
            return $this->error($e->getMessage());
        }

        $patch = [
            'update_time' => $now,
        ];
        if ($this->depositOrderHasColumn('review_admin_id')) {
            $patch['review_admin_id'] = $adminId > 0 ? $adminId : null;
        }
        if ($this->depositOrderHasColumn('review_time')) {
            $patch['review_time'] = $now;
        }
        Db::name('deposit_order')->where('id', $id)->where('status', 1)->update($patch);

        return $this->success(__('Approved'));
    }

    /**
     * 审核驳回：必须填写备注（reject_reason）
     */
    public function reject(WebmanRequest $request): Response
    {
        $response = $this->initializeBackend($request);
        if ($response !== null) {
            return $response;
        }
        if ($request->method() !== 'POST') {
            return $this->error(__('Parameter error'));
        }

        $id = $this->intParam($request->post('id'));
        if ($id <= 0) {
            return $this->error(__('Parameter error'));
        }

        $remarkRaw = $request->post('remark');
        $remark = is_string($remarkRaw) ? trim($remarkRaw) : '';
        if ($remark === '') {
            return $this->error(__('Please provide reject reason'));
        }

        $scoped = $this->loadWithRelations($id);
        if (!$scoped) {
            return $this->error(__('Record not found'));
        }
        if (!$this->checkChannelScoped($scoped)) {
            return $this->error(__('You have no permission'));
        }

        $order = Db::name('deposit_order')->where('id', $id)->find();
        if (!$order) {
            return $this->error(__('Record not found'));
        }

        $currentStatus = $this->intParam($order['status'] ?? 0);
        if ($currentStatus !== MockPay::DEPOSIT_STATUS_PENDING_REVIEW) {
            return $this->error(__('This order has already been reviewed'));
        }

        $now = time();
        $adminId = $this->intParam($this->auth->id ?? 0);
        $adminName = $this->adminDisplayName();
        $rejectReason = mb_substr($remark, 0, 255);
        $baseRemark = is_string($order['remark'] ?? null) ? trim($order['remark']) : '';
        $note = '管理员（' . $adminName . '）驳回：' . $rejectReason;
        $combined = $baseRemark === '' ? $note : mb_substr($baseRemark . ' | ' . $note, 0, 255);

        $update = [
            'status'      => 2,
            'remark'      => $combined,
            'update_time' => $now,
        ];
        if ($this->depositOrderHasColumn('reject_reason')) {
            $update['reject_reason'] = $rejectReason;
        }
        if ($this->depositOrderHasColumn('review_admin_id')) {
            $update['review_admin_id'] = $adminId > 0 ? $adminId : null;
        }
        if ($this->depositOrderHasColumn('review_time')) {
            $update['review_time'] = $now;
        }

        $affected = Db::name('deposit_order')
            ->where('id', $id)
            ->where('status', MockPay::DEPOSIT_STATUS_PENDING_REVIEW)
            ->update($update);
        if (!is_numeric($affected) || intval($affected) <= 0) {
            return $this->error(__('This order has already been reviewed'));
        }

        return $this->success(__('Rejected'));
    }

    private function depositOrderHasColumn(string $column): bool
    {
        static $cache = [];
        if (array_key_exists($column, $cache)) {
            return $cache[$column];
        }
        try {
            $rows = Db::query('SHOW COLUMNS FROM `deposit_order` LIKE ?', [$column]);
            $cache[$column] = is_array($rows) && $rows !== [];
        } catch (Throwable $e) {
            $cache[$column] = false;
        }

        return $cache[$column];
    }

    private function intParam($raw): int
    {
        if ($raw === null || $raw === '') {
            return 0;
        }
        if (is_numeric(strval($raw))) {
            return intval(strval($raw));
        }

        return 0;
    }

    private function adminDisplayName(): string
    {
        if (!$this->auth) {
            return 'admin';
        }
        $username = $this->auth->username ?? '';
        if (is_string($username) && trim($username) !== '') {
            return trim($username);
        }

        return 'admin#' . strval($this->auth->id ?? 0);
    }

}
