<?php

declare(strict_types=1);

namespace app\common\service;

use think\Config;

/**
 * 工单云控同步日志
 * - logCron：定时任务汇总，app_debug 关闭时也写入 runtime/log/split_sync.log
 * - log：详细调试日志，仅 app_debug=true 时写入
 */
class SplitTicketSyncLogger
{
    private const LOG_FILE = 'split_sync.log';

    private static ?string $ticketTag = null;

    public static function isEnabled(): bool
    {
        return (bool) Config::get('app_debug');
    }

    public static function setTicketContext(?int $ticketId, ?string $ticketType = null): void
    {
        if ($ticketId === null || $ticketId <= 0) {
            self::$ticketTag = null;
            return;
        }
        $type = $ticketType !== null && $ticketType !== '' ? $ticketType : '-';
        self::$ticketTag = sprintf('#%d(%s)', $ticketId, $type);
    }

    public static function clearTicketContext(): void
    {
        self::$ticketTag = null;
    }

    /**
     * 定时任务汇总日志（始终写入）
     *
     * @param array<string, mixed> $context
     */
    public static function logCron(string $message, array $context = []): void
    {
        self::writeLine('cron', $message, $context);
    }

    /**
     * 详细调试日志（仅 app_debug=true）
     *
     * @param array<string, mixed> $context
     */
    public static function log(string $stage, string $message, array $context = []): void
    {
        if (!self::isEnabled()) {
            return;
        }
        self::writeLine($stage, $message, $context);
    }

    /**
     * @param array<string, mixed> $context
     */
    private static function writeLine(string $stage, string $message, array $context = []): void
    {
        $context = self::sanitize($context);
        $ctxJson = $context !== [] ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE) : '';
        $line = sprintf(
            "[%s] %s [%s] %s%s\n",
            date('Y-m-d H:i:s'),
            self::$ticketTag ?? '[global]',
            $stage,
            $message,
            $ctxJson
        );

        $runtime = defined('RUNTIME_PATH') ? RUNTIME_PATH : (ROOT_PATH . 'runtime/');
        $dir = $runtime . 'log/';
        if (!is_dir($dir)) {
            @mkdir($dir, 0755, true);
        }
        @file_put_contents($dir . self::LOG_FILE, $line, FILE_APPEND | LOCK_EX);
    }

    /**
     * @param array<string, mixed> $context
     * @return array<string, mixed>
     */
    private static function sanitize(array $context): array
    {
        $out = [];
        foreach ($context as $key => $value) {
            $lower = strtolower((string) $key);
            if (in_array($lower, ['password', 'passwd', 'pwd', 'token', 'secret'], true)) {
                continue;
            }
            if ($key === 'authActions' && is_array($value)) {
                $out[$key] = self::sanitizeAuthActions($value);
                continue;
            }
            if (is_array($value)) {
                $out[$key] = self::sanitize($value);
                continue;
            }
            if (is_string($value) && mb_strlen($value) > 2000) {
                $out[$key] = mb_substr($value, 0, 2000, 'UTF-8') . '...(truncated)';
                continue;
            }
            $out[$key] = $value;
        }
        return $out;
    }

    /**
     * @param array<int, mixed> $actions
     * @return array<int, mixed>
     */
    private static function sanitizeAuthActions(array $actions): array
    {
        $sanitized = [];
        foreach ($actions as $action) {
            if (!is_array($action)) {
                $sanitized[] = $action;
                continue;
            }
            if (array_key_exists('value', $action)) {
                $action['value'] = '***';
            }
            $sanitized[] = $action;
        }
        return $sanitized;
    }
}
