<?php
// +----------------------------------------------------------------------
// | onezan-admin 快速开发的管理平台
// +----------------------------------------------------------------------
// | 官方网址：https://www.onezan.cn
// +----------------------------------------------------------------------
// | onezan团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: 543885307@qq.com
// +----------------------------------------------------------------------
namespace app\service\domain\notification;

use Core\service\registry\ApplicationRegistry;
use Core\service\registry\MessageRegistry;
use app\repository\channel\ChannelRepository;
use app\service\domain\channel\CoreChannelCatalogService;
use think\App;


/**
 * Core Notification Center Service
 * @package app\service\domain\notification
 */
class CoreNotificationCenterService
{
    private App $app;
    private MessageRegistry $messageRegistry;
    private ApplicationRegistry $ApplicationRegistry;

    private const EMAIL_FIELDS = [
        ['key' => 'subject_template', 'label' => '邮件标题模板'],
        ['key' => 'body_template', 'label' => '邮件正文HTML模板'],
    ];

    private const CHANNEL_DRIVER_MAP = [
        'weapp_subscribe' => ['weapp'],
        'wechat_template' => ['wechat'],
        'alipay_template' => ['aliapp'],
        'sms' => ['weapp', 'wechat', 'aliapp', 'h5', 'app'],
        'email' => ['weapp', 'wechat', 'aliapp', 'h5', 'app'],
    ];

    /**
     * CoreNotificationCenterService constructor
     * @param App $app Application instance
     * @param MessageRegistry $messageRegistry Message registry
     * @param ApplicationRegistry $ApplicationRegistry Application registry
     */
    public function __construct(App $app, MessageRegistry $messageRegistry, ApplicationRegistry $ApplicationRegistry)
    {
        $this->app = $app;
        $this->messageRegistry = $messageRegistry;
        $this->ApplicationRegistry = $ApplicationRegistry;
    }


    /**
     * Get notification configuration grouped by message group
     * @return array
     */
    public function config(): array
    {
        $messages = $this->messageRegistry->all();
        if (empty($messages)) {
            return ['groups' => []];
        }

        $groups = [];
        $index = [];
        foreach ($messages as $item) {
            $group = (string)($item['group'] ?? '');
            $code = (string)($item['code'] ?? '');
            if ($code === '') continue;

            if (!isset($index[$group])) {
                $index[$group] = count($groups);
                $groups[] = ['group' => $group, 'items' => []];
            }

            $methods = $this->buildMethods($item);
            $groups[$index[$group]]['items'][] = [
                'code' => $code,
                'title' => (string)($item['title'] ?? $code),
                'scene' => (string)($item['scene'] ?? ''),
                'source' => (string)($item['source'] ?? ''),
                'source_label' => $this->resolveSourceName((string)($item['source'] ?? '')),
                'methods' => $methods,
            ];
        }

        return ['groups' => $groups];
    }


    /**
     * 解析消息来源名称
     * @param string $source 来源标识（core或应用code）
     * @return string 来源显示名称
     */
    private function resolveSourceName(string $source): string
    {
        if ($source === 'core') {
            return '';
        }

        $manifest = $this->ApplicationRegistry->manifest($source);
        $name = (string)($manifest['name'] ?? '');
        if ($name !== '') {
            return $name;
        }

        return $source;
    }


    /**
     * Save notification settings for multiple messages
     * @param array $data Notification configuration data
     * @return array
     */
    public function save(array $data): array
    {
        $repo = $this->channelRepository();
        $notifyDrivers = $this->catalog()->notifyDrivers();
        $failures = [];

        foreach ($data as $messageItem) {
            $code = (string)($messageItem['code'] ?? '');
            $methods = $messageItem['methods'] ?? [];
            if ($code === '' || !is_array($methods)) continue;

            $messageDef = $this->findMessageDef($code);
            if (!$messageDef) continue;

            $scene = (string)($messageDef['scene'] ?? '');
            if ($scene === '') continue;

            foreach ($methods as $method) {
                $driverType = (string)($method['driver_type'] ?? '');
                $status = isset($method['status']) ? (int)$method['status'] : 0;
                $config = is_array($method['config'] ?? null) ? $method['config'] : [];
                $varMapping = is_array($method['var_mapping'] ?? null) ? $method['var_mapping'] : [];

                if (!empty($varMapping)) {
                    $config['_var_mapping'] = $varMapping;
                }

                $driverKey = $this->driverKey($scene, $driverType);
                if ($driverKey === '') continue;

                $channels = self::CHANNEL_DRIVER_MAP[$driverType] ?? [];
                if (empty($channels) && isset($notifyDrivers[$driverKey])) {
                    $channels = $this->resolveChannelsForDriver($driverKey);
                }

                foreach ($channels as $channel) {
                    try {
                        $repo->saveNotifyConfig($channel, $driverKey, [
                            'status' => $status,
                            'sort' => 0,
                            'config' => $config,
                        ]);
                    } catch (\Throwable $e) {
                        $failures[] = ['code' => $code, 'driver' => $driverType, 'channel' => $channel, 'error' => $e->getMessage()];
                    }
                }
            }
        }

        if (count($failures) > 0) {
            return ['code' => 1, 'msg' => '', 'failures' => $failures];
        }

        return ['code' => 0, 'msg' => ''];
    }

    /**
     * 根据消息定义构建通知方式（驱动）列表
     * @param array $messageDef 消息定义数组
     * @return array 通知方式列表
     */
    private function buildMethods(array $messageDef): array
    {
        $scene = (string)($messageDef['scene'] ?? '');
        $driverTypes = $messageDef['driver_types'] ?? [];
        if ($scene === '' || !is_array($driverTypes) || empty($driverTypes)) {
            return [];
        }

        $notifyDrivers = $this->catalog()->notifyDrivers();
        $existingConfigs = $this->loadAllNotifyConfigs($scene, $driverTypes);

        $methods = [];
        foreach ($driverTypes as $driverType) {
            $driverType = (string)$driverType;
            $driverKey = $this->driverKey($scene, $driverType);
            $driverDef = $notifyDrivers[$driverKey] ?? null;

            $fields = [];
            $exampleLines = [];
            $driverName = '';
            $templateSync = false;
            $syncTip = '';
            $formatDesc = '';
            $varMapping = [];

            if ($driverDef) {
                $fields = $driverDef['fields'] ?? [];
                $exampleLines = $driverDef['example_lines'] ?? [];
                $driverName = (string)($driverDef['driver_name'] ?? '');
                $templateSync = (bool)($driverDef['template_sync'] ?? false);
                $syncTip = (string)($driverDef['sync_tip'] ?? '');
                $formatDesc = (string)($driverDef['format_desc'] ?? '');
                $varMapping = is_array($driverDef['var_mapping'] ?? null) ? $driverDef['var_mapping'] : [];
            } elseif ($driverType === 'email') {
                $fields = self::EMAIL_FIELDS;
                $driverName = '';
                $exampleLines = [
                    '',
                    '',
                ];
            } else {
                $driverName = $driverType;
                $fields = [['key' => 'template_id', 'label' => '模板ID / Code']];
            }

            $saved = $existingConfigs[$driverType] ?? null;
            $status = 0;
            $config = [];
            $savedVarMapping = [];
            if ($saved) {
                $status = (int)($saved['status'] ?? 0);
                $config = $saved['config'] ?? [];
                $savedVarMapping = is_array($saved['var_mapping'] ?? null) ? $saved['var_mapping'] : [];
            }

            $resolvedVarMapping = !empty($savedVarMapping) ? $savedVarMapping : $varMapping;

            $methods[] = [
                'driver_type' => $driverType,
                'driver_key' => $driverKey,
                'driver_name' => $driverName,
                'status' => $status,
                'config' => $config,
                'fields' => $fields,
                'channels' => self::CHANNEL_DRIVER_MAP[$driverType] ?? $this->resolveChannelsForDriver($driverKey),
                'template_sync' => $templateSync,
                'sync_tip' => $syncTip,
                'example_lines' => $exampleLines,
                'format_desc' => $formatDesc,
                'var_mapping' => $resolvedVarMapping,
            ];
        }

        return $methods;
    }

    /**
     * 加载指定场景所有驱动类型的已保存通知配置
     * @param string $scene 消息场景
     * @param array $driverTypes 驱动类型列表
     * @return array 通知配置映射
     */
    private function loadAllNotifyConfigs(string $scene, array $driverTypes): array
    {
        $repo = $this->channelRepository();
        $result = [];

        foreach ($driverTypes as $driverType) {
            $driverType = (string)$driverType;
            $driverKey = $this->driverKey($scene, $driverType);
            $channels = self::CHANNEL_DRIVER_MAP[$driverType] ?? [];
            if (empty($channels)) {
                $channels = $this->resolveChannelsForDriver($driverKey);
            }

            foreach ($channels as $channel) {
                $rows = $repo->listNotifyConfigs($channel);
                foreach ($rows as $row) {
                    if ((string)($row['driver'] ?? '') === $driverKey) {
                        $parsed = json_decode((string)($row['config_value'] ?? '{}'), true);
                        $parsed = is_array($parsed) ? $parsed : [];
                        $varMapping = $parsed['_var_mapping'] ?? null;
                        unset($parsed['_var_mapping']);
                        $result[$driverType] = [
                            'status' => (int)($row['status'] ?? 0),
                            'config' => $parsed,
                            'var_mapping' => is_array($varMapping) ? $varMapping : [],
                        ];
                        break 2;
                    }
                }
            }
        }

        return $result;
    }

    /**
     * 解析使用了指定驱动Key的所有渠道列表
     * @param string $driverKey 驱动Key
     * @return array 渠道标识列表
     */
    private function resolveChannelsForDriver(string $driverKey): array
    {
        $channels = [];
        $all = $this->catalog()->channels();
        foreach ($all as $channelKey => $def) {
            $drivers = (array)($def['notify_drivers'] ?? []);
            if (in_array($driverKey, $drivers, true)) {
                $channels[] = $channelKey;
            }
        }
        return $channels;
    }

    /**
     * 拼接驱动Key（场景.驱动类型）
     * @param string $scene 消息场景
     * @param string $driverType 驱动类型
     * @return string 驱动Key
     */
    private function driverKey(string $scene, string $driverType): string
    {
        if ($scene === '' || $driverType === '') {
            return '';
        }
        return $scene . '.' . $driverType;
    }

    /**
     * 根据消息编码查找消息定义
     * @param string $code 消息编码
     * @return array|null 消息定义数组或null
     */
    private function findMessageDef(string $code): ?array
    {
        foreach ($this->messageRegistry->all() as $item) {
            if ((string)($item['code'] ?? '') === $code) {
                return $item;
            }
        }
        return null;
    }

    /**
     * 获取渠道目录服务实例
     * @return CoreChannelCatalogService
     */
    private function catalog(): CoreChannelCatalogService
    {
        return $this->app->make(CoreChannelCatalogService::class);
    }

    /**
     * 获取渠道仓储实例
     * @return ChannelRepository
     */
    private function channelRepository(): ChannelRepository
    {
        return $this->app->make(ChannelRepository::class);
    }
}