<?php

namespace App\Services;

use App\Models\PushDeviceToken;
use App\Models\PushNotificationSetting;
use App\Services\Fcm\FcmSender;
use Illuminate\Support\Facades\Log;

/**
 * 푸시 알림 서비스
 * - 토큰 관리 (등록/해제/조회)
 * - 알림 설정 관리
 * - 비즈니스 이벤트 기반 푸시 발송
 */
class PushNotificationService extends Service
{
    public function __construct(
        private readonly FcmSender $fcmSender
    ) {}

    // ============================================================
    // 토큰 관리
    // ============================================================

    /**
     * FCM 토큰 등록/갱신
     */
    public function registerToken(array $data): PushDeviceToken
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        // 동일 토큰이 있으면 업데이트, 없으면 생성
        $token = PushDeviceToken::withoutGlobalScopes()
            ->where('token', $data['token'])
            ->first();

        if ($token) {
            // 기존 토큰 업데이트 (다른 사용자의 토큰이면 이전 것은 비활성화)
            if ($token->user_id !== $userId || $token->tenant_id !== $tenantId) {
                $token->update([
                    'tenant_id' => $tenantId,
                    'user_id' => $userId,
                    'platform' => $data['platform'],
                    'device_name' => $data['device_name'] ?? null,
                    'app_version' => $data['app_version'] ?? null,
                    'is_active' => true,
                    'last_used_at' => now(),
                    'deleted_at' => null,
                ]);
            } else {
                $token->update([
                    'platform' => $data['platform'],
                    'device_name' => $data['device_name'] ?? null,
                    'app_version' => $data['app_version'] ?? null,
                    'is_active' => true,
                    'last_used_at' => now(),
                ]);
            }
        } else {
            // 새 토큰 생성
            $token = PushDeviceToken::create([
                'tenant_id' => $tenantId,
                'user_id' => $userId,
                'token' => $data['token'],
                'platform' => $data['platform'],
                'device_name' => $data['device_name'] ?? null,
                'app_version' => $data['app_version'] ?? null,
                'is_active' => true,
                'last_used_at' => now(),
            ]);
        }

        // 사용자 기본 알림 설정 초기화 (없는 경우)
        $this->initializeDefaultSettings($tenantId, $userId);

        Log::info('[PushNotificationService] FCM token registered', [
            'user_id' => $userId,
            'platform' => $data['platform'],
            'token_id' => $token->id,
        ]);

        return $token;
    }

    /**
     * FCM 토큰 비활성화
     */
    public function unregisterToken(string $tokenValue): bool
    {
        $token = PushDeviceToken::withoutGlobalScopes()
            ->where('token', $tokenValue)
            ->first();

        if ($token) {
            $token->update(['is_active' => false]);

            Log::info('[PushNotificationService] FCM token unregistered', [
                'token_id' => $token->id,
            ]);

            return true;
        }

        return false;
    }

    /**
     * 사용자의 활성 토큰 목록 조회
     */
    public function getUserTokens(?int $userId = null): array
    {
        $userId = $userId ?? $this->apiUserId();

        return PushDeviceToken::forUser($userId)
            ->active()
            ->get()
            ->toArray();
    }

    // ============================================================
    // 알림 설정 관리
    // ============================================================

    /**
     * 알림 설정 조회
     */
    public function getSettings(?int $userId = null): array
    {
        $tenantId = $this->tenantId();
        $userId = $userId ?? $this->apiUserId();

        $settings = PushNotificationSetting::where('tenant_id', $tenantId)
            ->forUser($userId)
            ->get()
            ->keyBy('notification_type');

        // 모든 알림 유형에 대한 설정 반환 (없으면 기본값)
        $result = [];
        foreach (PushNotificationSetting::getAllTypes() as $type) {
            if ($settings->has($type)) {
                $result[$type] = $settings->get($type)->toArray();
            } else {
                $result[$type] = [
                    'notification_type' => $type,
                    'is_enabled' => true,
                    'sound' => $this->getDefaultSound($type),
                    'vibrate' => true,
                    'show_preview' => true,
                ];
            }
        }

        return $result;
    }

    /**
     * 알림 설정 업데이트
     */
    public function updateSettings(array $settings): array
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        $updated = [];
        foreach ($settings as $setting) {
            $record = PushNotificationSetting::updateOrCreate(
                [
                    'tenant_id' => $tenantId,
                    'user_id' => $userId,
                    'notification_type' => $setting['notification_type'],
                ],
                [
                    'is_enabled' => $setting['is_enabled'],
                    'sound' => $setting['sound'] ?? $this->getDefaultSound($setting['notification_type']),
                    'vibrate' => $setting['vibrate'] ?? true,
                    'show_preview' => $setting['show_preview'] ?? true,
                ]
            );
            $updated[] = $record->toArray();
        }

        return $updated;
    }

    /**
     * 기본 알림 설정 초기화
     */
    protected function initializeDefaultSettings(int $tenantId, int $userId): void
    {
        foreach (PushNotificationSetting::getAllTypes() as $type) {
            PushNotificationSetting::firstOrCreate(
                [
                    'tenant_id' => $tenantId,
                    'user_id' => $userId,
                    'notification_type' => $type,
                ],
                [
                    'is_enabled' => true,
                    'sound' => $this->getDefaultSound($type),
                    'vibrate' => true,
                    'show_preview' => true,
                ]
            );
        }
    }

    /**
     * 알림 유형별 기본 알림음
     */
    protected function getDefaultSound(string $type): string
    {
        return match ($type) {
            PushNotificationSetting::TYPE_DEPOSIT => PushNotificationSetting::SOUND_DEPOSIT,
            PushNotificationSetting::TYPE_WITHDRAWAL => PushNotificationSetting::SOUND_WITHDRAWAL,
            PushNotificationSetting::TYPE_ORDER => PushNotificationSetting::SOUND_ORDER,
            PushNotificationSetting::TYPE_APPROVAL => PushNotificationSetting::SOUND_APPROVAL,
            default => PushNotificationSetting::SOUND_DEFAULT,
        };
    }

    // ============================================================
    // 비즈니스 이벤트 기반 푸시 발송
    // ============================================================

    /**
     * 비즈니스 이벤트에 따른 푸시 발송
     *
     * @param  string  $event  이벤트 타입 (new_client, payment, sales_order 등)
     * @param  string  $title  알림 제목
     * @param  string  $body  알림 내용
     * @param  array  $data  추가 데이터 (딥링크, ID 등)
     * @param  int|null  $tenantId  특정 테넌트 (null이면 현재 컨텍스트)
     * @param  int|null  $userId  특정 사용자에게만 발송 (null이면 테넌트 전체)
     */
    public function sendByEvent(
        string $event,
        string $title,
        string $body,
        array $data = [],
        ?int $tenantId = null,
        ?int $userId = null
    ): void {
        $tenantId = $tenantId ?? $this->tenantIdOrNull();

        if (! $tenantId) {
            Log::warning('[PushNotificationService] No tenant_id provided');

            return;
        }

        $channelId = $this->getChannelForEvent($event);

        // 해당 테넌트의 활성 토큰 조회 (global scope 무시)
        $query = PushDeviceToken::withoutGlobalScopes()
            ->forTenant($tenantId)
            ->active();

        // 특정 사용자에게만 발송
        if ($userId) {
            $query->forUser($userId);
        }

        $tokens = $query->pluck('token')->toArray();

        if (empty($tokens)) {
            Log::info('[PushNotificationService] No active tokens found', [
                'tenant_id' => $tenantId,
                'user_id' => $userId,
                'event' => $event,
            ]);

            return;
        }

        // 이벤트 타입을 data에 추가
        $data['event'] = $event;

        try {
            $result = $this->fcmSender->sendToMany(
                $tokens,
                $title,
                $body,
                $channelId,
                $data
            );

            Log::info('[PushNotificationService] Push sent', [
                'tenant_id' => $tenantId,
                'event' => $event,
                'channel_id' => $channelId,
                'token_count' => count($tokens),
                'success_count' => $result->getSuccessCount(),
                'failure_count' => $result->getFailureCount(),
            ]);

        } catch (\Exception $e) {
            Log::error('[PushNotificationService] Push failed', [
                'tenant_id' => $tenantId,
                'event' => $event,
                'error' => $e->getMessage(),
            ]);
        }
    }

    /**
     * 신규 거래처 등록 알림
     */
    public function notifyNewClient(int $clientId, string $clientName, ?int $tenantId = null): void
    {
        $this->sendByEvent(
            'new_client',
            '신규 거래처 등록',
            "새로운 거래처 '{$clientName}'이(가) 등록되었습니다.",
            ['client_id' => $clientId, 'type' => 'client'],
            $tenantId
        );
    }

    /**
     * 결제 알림
     */
    public function notifyPayment(int $paymentId, string $message, ?int $tenantId = null, ?int $userId = null): void
    {
        $this->sendByEvent(
            'payment',
            '결제 알림',
            $message,
            ['payment_id' => $paymentId, 'type' => 'payment'],
            $tenantId,
            $userId
        );
    }

    /**
     * 수주 알림
     */
    public function notifySalesOrder(int $orderId, string $message, ?int $tenantId = null): void
    {
        $this->sendByEvent(
            'sales_order',
            '수주 알림',
            $message,
            ['order_id' => $orderId, 'type' => 'sales_order'],
            $tenantId
        );
    }

    /**
     * 발주 알림
     */
    public function notifyPurchaseOrder(int $orderId, string $message, ?int $tenantId = null): void
    {
        $this->sendByEvent(
            'purchase_order',
            '발주 알림',
            $message,
            ['order_id' => $orderId, 'type' => 'purchase_order'],
            $tenantId
        );
    }

    /**
     * 계약 알림
     */
    public function notifyContract(int $contractId, string $message, ?int $tenantId = null): void
    {
        $this->sendByEvent(
            'contract',
            '계약 알림',
            $message,
            ['contract_id' => $contractId, 'type' => 'contract'],
            $tenantId
        );
    }

    /**
     * 일반 알림
     */
    public function notifyGeneral(string $title, string $body, array $data = [], ?int $tenantId = null, ?int $userId = null): void
    {
        $this->sendByEvent(
            'general',
            $title,
            $body,
            $data,
            $tenantId,
            $userId
        );
    }

    /**
     * 이벤트 타입에 따른 채널 ID 반환
     */
    private function getChannelForEvent(string $event): string
    {
        return match ($event) {
            'new_client' => config('fcm.channels.vendor_register'),
            'payment' => config('fcm.channels.approval_request'),
            'income' => config('fcm.channels.income'),
            'sales_order' => config('fcm.channels.sales_order'),
            'purchase_order' => config('fcm.channels.purchase_order'),
            'contract' => config('fcm.channels.contract'),
            default => config('fcm.channels.default'),
        };
    }
}
