<?php

declare(strict_types=1);

/**
 * 本文件属于 DBErp 进销存系统。
 *
 * 版权所有 © 2026 北京珑大钜商科技有限公司。保留所有权利。
 *
 * 未经官方授权，不得删除、篡改或隐藏本文件中的版权声明、产品标识与来源信息。
 * 商业授权、官方更新与技术支持服务，请以珑大官方支持中心记录为准。
 *
 * @link https://support.loongdom.cn
 * @see  https://www.dberp.com.cn
 */

namespace App\Support;

use App\Support\AuditLogDisplay\DberpAuditLogFieldLabelResolver;
use Illuminate\Database\Eloquent\Builder;

/**
 * 审计日志列表等界面：将动作码、快照键值格式化为中文可读文案。
 */
final class DberpAuditDisplay
{
    public static function actionLabel(?string $action): string
    {
        if ($action === null || $action === '') {
            return '—';
        }

        $map = __('dberp.options.audit_action');

        if (is_array($map) && isset($map[$action])) {
            return $map[$action];
        }

        if (preg_match('/^dberp\\.([a-z0-9_]+)\\.(created|updated|deleted)$/i', $action, $matches) === 1) {
            $resource = $matches[1];
            $verb = strtolower($matches[2]);
            $subject = self::resourceLabelFromAuditSegment($resource);

            return match ($verb) {
                'created' => sprintf('%s · %s', $subject, (string) __('dberp.audit.trail_created')),
                'updated' => sprintf('%s · %s', $subject, (string) __('dberp.audit.trail_updated')),
                'deleted' => sprintf('%s · %s', $subject, (string) __('dberp.audit.trail_deleted')),
                default => sprintf('%s · %s', $subject, $verb),
            };
        }

        return (string) __('dberp.audit.unknown_action');
    }

    public static function applyActionKeywordToQuery(Builder $query, string $keyword): Builder
    {
        $keyword = trim($keyword);

        if ($keyword === '') {
            return $query;
        }

        $matchedCodes = self::matchingActionCodesForKeyword($keyword);
        $likePattern = '%'.self::escapeLikeWildcards($keyword).'%';

        return $query->where(function (Builder $scoped) use ($likePattern, $matchedCodes): void {
            // ESCAPE 保证 %/_ 按字面匹配，MySQL 与 SQLite 测试库行为一致。
            $scoped->whereRaw('action like ? escape ?', [$likePattern, '\\']);

            if ($matchedCodes !== []) {
                $scoped->orWhereIn('action', $matchedCodes);
            }
        });
    }

    /**
     * @return list<string>
     */
    public static function matchingActionCodesForKeyword(string $keyword): array
    {
        $needle = mb_strtolower(trim($keyword));

        // 单字中文（如「已」）会命中大量标签，过宽；动作码 LIKE 仍可单独匹配英文片段。
        if ($needle === '' || mb_strlen($needle) < 2) {
            return [];
        }

        $matches = [];

        $map = __('dberp.options.audit_action');

        if (is_array($map)) {
            foreach ($map as $code => $label) {
                if (! is_string($code)) {
                    continue;
                }

                if (self::keywordMatchesHaystack($needle, $code, is_string($label) ? $label : (string) $label)) {
                    $matches[] = $code;
                }
            }
        }

        foreach (self::patternActionCodesForKeyword($needle) as $code) {
            $matches[] = $code;
        }

        return array_values(array_unique($matches));
    }

    public static function snapshotKeyLabel(string $key, ?string $auditableType = null): string
    {
        return app(DberpAuditLogFieldLabelResolver::class)->resolve($key, $auditableType);
    }

    public static function snapshotValueSummary(mixed $value): string
    {
        if ($value === null || $value === '') {
            return '—';
        }

        if (is_bool($value)) {
            return $value ? '是' : '否';
        }

        if (is_array($value)) {
            if ($value === []) {
                return '—';
            }

            $keys = array_keys($value);

            if ($keys === range(0, count($value) - 1)) {
                $parts = array_map(static fn (mixed $item): string => (string) $item, array_slice($value, 0, 6));

                $suffix = count($value) > 6 ? '…' : '';

                return implode('、', $parts).$suffix;
            }

            $pairs = [];
            $i = 0;

            foreach ($value as $nestedKey => $nestedValue) {
                if ($i >= 3) {
                    $pairs[] = '…';

                    break;
                }

                if (is_array($nestedValue)) {
                    $nestedValue = (string) __('dberp.audit.nested_value_omitted');
                }

                $pairs[] = self::snapshotKeyLabel((string) $nestedKey).'：'.self::snapshotValueSummary($nestedValue);
                $i++;
            }

            return implode('；', $pairs);
        }

        $string = (string) $value;

        if (mb_strlen($string) > 160) {
            return mb_substr($string, 0, 160).'…';
        }

        return $string;
    }

    public static function auditableSummary(?string $auditableType, mixed $auditableId): string
    {
        if (($auditableType === null || $auditableType === '') && ($auditableId === null || $auditableId === '')) {
            return '—';
        }

        $typeLabel = self::auditableTypeLabel($auditableType);
        $idPart = ($auditableId !== null && $auditableId !== '') ? ' #'.$auditableId : '';

        return $typeLabel.$idPart;
    }

    public static function auditableTypeLabel(?string $auditableType): string
    {
        if ($auditableType === null || $auditableType === '') {
            return (string) __('dberp.audit.auditable_type_none');
        }

        $short = class_basename($auditableType);
        $key = 'dberp.audit.entity.'.$short;
        $label = __($key);

        return $label !== $key ? $label : (string) __('dberp.audit_display.entity_type_unmapped');
    }

    private static function resourceLabelFromAuditSegment(string $segment): string
    {
        $segment = self::normalizeAuditResourceSegment($segment);
        $resourceKey = 'dberp.resources.'.$segment.'.label';
        $label = __($resourceKey);

        if ($label !== $resourceKey) {
            return $label;
        }

        $fallbackKey = 'dberp.audit.resource_fallback.'.$segment;
        $fallback = __($fallbackKey);

        return $fallback !== $fallbackKey ? $fallback : (string) __('dberp.audit_display.resource_unmapped');
    }

    /**
     * 审计动作里资源段命名与 Filament 资源键不完全一致时，映射到已有翻译键。
     */
    private static function normalizeAuditResourceSegment(string $segment): string
    {
        return match ($segment) {
            'number_rule' => 'number_sequence',
            default => $segment,
        };
    }

    /**
     * @return list<string>
     */
    private static function patternActionCodesForKeyword(string $needle): array
    {
        $codes = [];
        $verbs = [
            'created' => (string) __('dberp.audit.trail_created'),
            'updated' => (string) __('dberp.audit.trail_updated'),
            'deleted' => (string) __('dberp.audit.trail_deleted'),
        ];

        foreach (self::auditResourceSegmentLabels() as $segment => $label) {
            foreach ($verbs as $verb => $verbLabel) {
                $code = 'dberp.'.$segment.'.'.$verb;
                $compositeLabel = $label.' · '.$verbLabel;

                if (self::keywordMatchesHaystack($needle, $code, $compositeLabel)) {
                    $codes[] = $code;
                }
            }
        }

        return $codes;
    }

    /**
     * @return array<string, string>
     */
    private static function auditResourceSegmentLabels(): array
    {
        $segments = [];

        $resources = __('dberp.resources');

        if (is_array($resources)) {
            foreach ($resources as $resourceKey => $config) {
                if (! is_string($resourceKey) || ! is_array($config)) {
                    continue;
                }

                $label = (string) ($config['label'] ?? '');

                if ($label === '') {
                    continue;
                }

                $segments[self::auditSegmentForResourceKey($resourceKey)] = $label;
            }
        }

        $fallback = __('dberp.audit.resource_fallback');

        if (is_array($fallback)) {
            foreach ($fallback as $segment => $label) {
                if (is_string($segment) && is_string($label) && $label !== '') {
                    $segments[$segment] = $label;
                }
            }
        }

        return $segments;
    }

    private static function auditSegmentForResourceKey(string $resourceKey): string
    {
        return match ($resourceKey) {
            'number_sequence' => 'number_rule',
            default => $resourceKey,
        };
    }

    private static function keywordMatchesHaystack(string $needle, string ...$haystacks): bool
    {
        foreach ($haystacks as $haystack) {
            if ($haystack !== '' && str_contains(mb_strtolower($haystack), $needle)) {
                return true;
            }
        }

        return false;
    }

    private static function escapeLikeWildcards(string $value): string
    {
        return str_replace(
            ['\\', '%', '_'],
            ['\\\\', '\\%', '\\_'],
            $value,
        );
    }
}
