<?php

namespace App\Support;

use App\Models\AuditLog;
use App\Models\AdminUser;
use App\Models\Player;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;

/**
 * 审计日志列表展示：模块 / 动作 / 目标中文标签（API 资源名 + 业务码映射）。
 */
final class AuditLogApiPresenter
{
    /** @var array<string, string> */
    private const MODULE_LABELS = [
        'agent' => '代理管理',
        'system' => '系统管理',
        'settings' => '系统设置',
        'integration' => '接入站点',
        'player_manage' => '玩家管理',
        'player_service' => '玩家服务',
        'risk_cap' => '限额规则',
        'odds' => '赔率配置',
        'play_config' => '玩法配置',
        'settlement' => '派彩结算',
        'report_jobs' => '报表导出',
        'reconcile_jobs' => '对账任务',
        'draw' => '开奖期号',
        'wallet' => '钱包资金',
        'reconcile' => '对账核对',
        'jackpot' => '奖池',
        'dashboard' => '工作台',
        'job' => '后台任务',
        'bootstrap' => '系统',
    ];

    /** @var array<string, string> */
    private const ACTION_LABELS = [
        'agent_role.sync_permissions' => '同步代理角色权限',
        'admin_role.sync_permissions' => '同步平台角色权限',
        'admin_role.create' => '创建平台角色',
        'admin_role.update' => '更新平台角色',
        'admin_role.delete' => '删除平台角色',
        'agent_role.create' => '创建代理角色',
        'agent_role.update' => '更新代理角色',
        'agent_role.destroy' => '删除代理角色',
        'agent_admin_user.create' => '创建代理账号',
        'agent_admin_user.sync_roles' => '同步代理账号角色',
        'agent_node.create' => '创建代理节点',
        'agent_node.update' => '更新代理节点',
        'agent_node.destroy' => '删除代理节点',
        'agent.delegation.sync' => '同步代理授权',
        'admin_user.create' => '创建管理员',
        'admin_user.update' => '更新管理员',
        'admin_user.delete' => '删除管理员',
        'sync_permissions' => '同步权限',
        'batch_update' => '批量更新设置',
        'payout_adjustment' => '派彩调整',
        'rotate_secrets' => '轮换密钥',
        'toggle_active' => '切换启用状态',
        'enqueue' => '提交报表导出',
        'freeze' => '冻结玩家',
        'unfreeze' => '解冻玩家',
        'publish' => '发布开奖结果',
        'reopen' => '重新开奖',
        'run' => '执行结算',
        'confirm' => '确认账单',
        'close' => '关账',
        'payment' => '登记回款',
        'write_off' => '坏账核销',
        'adjustment' => '调整账单',
    ];

    /** @var array<string, string> */
    private const TARGET_TYPE_LABELS = [
        'admin_role' => '角色',
        'admin_user' => '管理员',
        'agent_node' => '代理节点',
        'admin_site' => '对接站点',
        'player' => '玩家',
        'lottery_settings' => '系统设置',
        'lottery_setting' => '设置项',
        'risk_cap_version' => '风险池版本',
        'odds_version' => '赔率版本',
        'play_config_item' => '玩法',
        'play_config_version' => '玩法配置版本',
        'settlement_batch_adjustment' => '结算调整单',
        'report_job' => '报表任务',
        'reconcile_job' => '对账任务',
        'transfer_no' => '转账单',
        'draw' => '期次',
        'batch' => '批次',
        'agent_settlement_bill' => '代理结算单',
        'agent_settlement_period' => '代理结算期',
        'integration_site' => '接入站点',
        'jackpot_pool' => '奖池',
    ];

    /** @var array<string, string> */
    private const API_RESOURCE_ENTITY_LABELS = [
        'admin-users' => '管理员',
        'admin-roles' => '角色',
        'agent-nodes' => '代理',
        'agent-roles' => '代理角色',
        'agent-admin-users' => '代理账号',
        'agent-lines' => '代理线路',
        'agent-delegation-grants' => '代理授权',
        'draws' => '期次',
        'players' => '玩家',
        'settings' => '系统设置',
        'integration-sites' => '接入站点',
        'reconcile-jobs' => '对账任务',
        'report-jobs' => '报表任务',
        'settlement-batches' => '结算批次',
        'jackpot' => '奖池',
        'risk' => '风控',
    ];

    /** @var array<string, string> */
    private const VERB_LABELS = [
        'sync' => '同步',
        'store' => '创建',
        'update' => '更新',
        'destroy' => '删除',
        'delete' => '删除',
        'create' => '创建',
        'publish' => '发布',
        'reopen' => '重新开奖',
        'freeze' => '冻结',
        'unfreeze' => '解冻',
        'run' => '执行',
        'transfer' => '转账',
        'start' => '开始',
        'test' => '测试',
    ];

    /**
     * @param  LengthAwarePaginator<AuditLog>  $paginator
     * @return array{items: list<array<string, mixed>>, meta: array{current_page: int, per_page: int, total: int, last_page: int}}
     */
    public static function listPayload(LengthAwarePaginator $paginator): array
    {
        $items = collect($paginator->items());
        $resourceNames = self::loadResourceNames($items);
        $operatorLabels = self::loadOperatorLabels($items);

        return AdminApiList::payload(
            $paginator,
            fn (AuditLog $r) => self::row($r, $resourceNames, $operatorLabels),
        );
    }

    /**
     * @param  array<string, string>  $resourceNames
     * @param  array<string, string>  $operatorLabels
     * @return array<string, mixed>
     */
    public static function row(AuditLog $r, array $resourceNames = [], array $operatorLabels = []): array
    {
        $operatorDisplay = self::operatorDisplay($r, $operatorLabels);

        return [
            'id' => (int) $r->id,
            'operator_type' => $r->operator_type,
            'operator_id' => (int) $r->operator_id,
            'operator_label' => $operatorDisplay['label'],
            'operator_subtitle' => $operatorDisplay['subtitle'],
            'module_code' => $r->module_code,
            'action_code' => $r->action_code,
            'target_type' => $r->target_type,
            'target_id' => $r->target_id,
            'module_label' => self::moduleLabel($r->module_code),
            'action_label' => self::actionLabel($r, $resourceNames),
            'target_label' => self::targetLabel($r, $resourceNames),
            'summary_label' => self::summaryLabel($r, $resourceNames),
            'before_json' => $r->before_json,
            'after_json' => $r->after_json,
            'ip' => $r->ip,
            'user_agent' => $r->user_agent,
            'created_at' => $r->created_at?->toIso8601String(),
        ];
    }

    /**
     * @param  Collection<int, AuditLog>  $items
     * @return array<string, string>
     */
    private static function loadOperatorLabels(Collection $items): array
    {
        $labels = [];

        $adminIds = $items
            ->filter(fn (AuditLog $row) => $row->operator_type === 'admin' && (int) $row->operator_id > 0)
            ->pluck('operator_id')
            ->map(fn (mixed $id): int => (int) $id)
            ->unique()
            ->values()
            ->all();
        if ($adminIds !== []) {
            $admins = AdminUser::query()
                ->whereIn('id', $adminIds)
                ->get(['id', 'username', 'name']);

            foreach ($admins as $admin) {
                $labels['admin:'.$admin->id] = self::formatIdentityLabel(
                    (string) ($admin->name ?? ''),
                    (string) ($admin->username ?? ''),
                );
            }
        }

        $playerIds = $items
            ->filter(fn (AuditLog $row) => $row->operator_type === 'player' && (int) $row->operator_id > 0)
            ->pluck('operator_id')
            ->map(fn (mixed $id): int => (int) $id)
            ->unique()
            ->values()
            ->all();
        if ($playerIds !== []) {
            $players = Player::query()
                ->whereIn('id', $playerIds)
                ->get(['id', 'username', 'nickname']);

            foreach ($players as $player) {
                $labels['player:'.$player->id] = self::formatIdentityLabel(
                    (string) ($player->nickname ?? ''),
                    (string) ($player->username ?? ''),
                );
            }
        }

        return $labels;
    }

    /**
     * @param  Collection<int, AuditLog>  $items
     * @return array<string, string>
     */
    private static function loadResourceNames(Collection $items): array
    {
        $codes = $items
            ->pluck('target_type')
            ->filter(fn (?string $type) => self::isApiResourceCode($type))
            ->unique()
            ->values()
            ->all();

        if ($codes === []) {
            return [];
        }

        /** @var array<string, string> */
        return DB::table('admin_api_resources')
            ->whereIn('code', $codes)
            ->pluck('name', 'code')
            ->all();
    }

    private static function moduleLabel(?string $code): string
    {
        if ($code === null || $code === '') {
            return '—';
        }

        return self::MODULE_LABELS[$code] ?? $code;
    }

    /**
     * @param  array<string, string>  $operatorLabels
     */
    private static function operatorDisplay(AuditLog $r, array $operatorLabels): array
    {
        $type = (string) ($r->operator_type ?? '');
        $id = (int) ($r->operator_id ?? 0);

        if ($type === 'system') {
            return ['label' => '系统', 'subtitle' => null];
        }

        $key = $type.':'.$id;
        if (isset($operatorLabels[$key]) && $operatorLabels[$key] !== '') {
            return [
                'label' => $operatorLabels[$key],
                'subtitle' => $id > 0 ? sprintf('%s #%d', $type === 'admin' ? '管理员' : ($type === 'player' ? '玩家' : $type), $id) : null,
            ];
        }

        $fallbackLabel = match ($type) {
            'admin' => $id > 0 ? '管理员 #'.$id : '管理员',
            'player' => $id > 0 ? '玩家 #'.$id : '玩家',
            'system' => '系统',
            default => $id > 0 ? sprintf('%s #%d', $type !== '' ? $type : '未知', $id) : ($type !== '' ? $type : '未知'),
        };

        return ['label' => $fallbackLabel, 'subtitle' => null];
    }

    private static function formatIdentityLabel(string $primary, string $secondary): string
    {
        $primary = trim($primary);
        $secondary = trim($secondary);

        if ($primary !== '') {
            return $primary;
        }

        return $secondary;
    }

    /**
     * @param  array<string, string>  $resourceNames
     */
    private static function actionLabel(AuditLog $r, array $resourceNames): string
    {
        $action = (string) ($r->action_code ?? '');
        if ($action === '') {
            return '—';
        }

        if (isset(self::ACTION_LABELS[$action])) {
            return self::ACTION_LABELS[$action];
        }

        $targetType = (string) ($r->target_type ?? '');
        if (self::isApiResourceCode($targetType) && isset($resourceNames[$targetType])) {
            return $resourceNames[$targetType];
        }

        if (isset(self::VERB_LABELS[$action])) {
            return self::VERB_LABELS[$action];
        }

        return $action;
    }

    /**
     * @param  array<string, string>  $resourceNames
     */
    private static function targetLabel(AuditLog $r, array $resourceNames): string
    {
        $type = (string) ($r->target_type ?? '');
        $id = trim((string) ($r->target_id ?? ''));

        if ($type === '') {
            return $id !== '' ? '#'.$id : '—';
        }

        if (self::isApiResourceCode($type)) {
            $entity = self::entityLabelFromResourceCode($type, $resourceNames);

            return $id !== '' ? sprintf('%s #%s', $entity, $id) : $entity;
        }

        $typeLabel = self::TARGET_TYPE_LABELS[$type] ?? $type;

        return $id !== '' ? sprintf('%s #%s', $typeLabel, $id) : $typeLabel;
    }

    /**
     * @param  array<string, string>  $resourceNames
     */
    private static function summaryLabel(AuditLog $r, array $resourceNames): string
    {
        $module = self::moduleLabel($r->module_code);
        $action = self::actionLabel($r, $resourceNames);
        $target = self::targetLabel($r, $resourceNames);

        if ($action === '—') {
            return $module;
        }

        if ($target === '—') {
            return sprintf('%s · %s', $module, $action);
        }

        if ($target === $action || str_starts_with($target, $action.' #')) {
            return sprintf('%s · %s', $module, $action);
        }

        return sprintf('%s · %s（%s）', $module, $action, $target);
    }

    /**
     * @param  array<string, string>  $resourceNames
     */
    private static function entityLabelFromResourceCode(string $code, array $resourceNames): string
    {
        $segments = explode('.', $code);
        if (count($segments) < 2) {
            return $resourceNames[$code] ?? $code;
        }

        $resourceSegment = $segments[1] ?? '';
        if (isset(self::API_RESOURCE_ENTITY_LABELS[$resourceSegment])) {
            return self::API_RESOURCE_ENTITY_LABELS[$resourceSegment];
        }

        return $resourceNames[$code] ?? $resourceSegment;
    }

    private static function isApiResourceCode(?string $code): bool
    {
        return is_string($code) && str_starts_with($code, 'admin.');
    }
}
