<?php

declare(strict_types=1);

namespace App\Framework\WebPush\Controllers;

use App\Framework\Attributes\Route;
use App\Framework\Http\Method;
use App\Framework\Http\Request;
use App\Framework\Http\Session\Session;
use App\Framework\Http\Status;
use App\Framework\Meta\MetaData;
use App\Framework\Router\Result\JsonResult;
use App\Framework\Router\Result\ViewResult;
use App\Framework\WebPush\Repository\SubscriptionRepository;
use App\Framework\WebPush\Services\WebPushService;
use App\Framework\WebPush\ValueObjects\PushMessage;
use App\Framework\WebPush\ValueObjects\PushSubscription;

/**
 * Web Push Notification Controller.
 */
final readonly class WebPushController
{
    public function __construct(
        private SubscriptionRepository $subscriptionRepository,
        private WebPushService $webPushService,
        private Session $session,
    ) {
    }

    /**
     * Speichert eine neue Push Subscription.
     */
    #[Route(path: '/api/push/subscribe', method: Method::POST)]
    public function subscribe(Request $request): JsonResult
    {
        $data = $request->parsedBody->toArray();

        if ($data === null) {
            return new JsonResult(
                data: ['error' => 'Invalid request body'],
                status: Status::BAD_REQUEST
            );
        }

        try {
            // Extract user ID from session or request (if authenticated)
            $userId = $this->session->get('user_id') ?? null;

            // Create subscription from browser data
            $subscription = PushSubscription::fromBrowserData(
                $data,
                $userId
            );

            // Save subscription
            $saved = $this->subscriptionRepository->save($subscription);

            if (! $saved) {
                return new JsonResult(
                    data: ['error' => 'Failed to save subscription'],
                    status: Status::INTERNAL_SERVER_ERROR
                );
            }

            return new JsonResult(
                data: [
                    'success' => true,
                    'message' => 'Subscription saved successfully',
                    'hash' => $subscription->getHash(),
                ],
                status: Status::CREATED
            );
        } catch (\Exception $e) {
            return new JsonResult(
                data: ['error' => $e->getMessage()],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Löscht eine Push Subscription.
     */
    #[Route(path: '/api/push/unsubscribe', method: Method::POST)]
    public function unsubscribe(Request $request): JsonResult
    {
        $data = $request->parsedBody;

        if ($data === null || ! isset($data['endpoint'])) {
            return new JsonResult(
                data: ['error' => 'Endpoint required'],
                status: Status::BAD_REQUEST
            );
        }

        try {
            // Generate hash from endpoint
            $hash = hash('sha256', $data['endpoint']);

            // Delete subscription
            $deleted = $this->subscriptionRepository->delete($hash);

            if (! $deleted) {
                return new JsonResult(
                    data: ['error' => 'Subscription not found'],
                    status: Status::NOT_FOUND
                );
            }

            return new JsonResult(
                data: [
                    'success' => true,
                    'message' => 'Subscription deleted successfully',
                ]
            );
        } catch (\Exception $e) {
            return new JsonResult(
                data: ['error' => $e->getMessage()],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Sendet Test-Notification an eigene Subscription.
     */
    #[Route(path: '/api/push/test', method: Method::POST)]
    public function test(Request $request): JsonResult
    {
        $data = $request->parsedBody;

        if ($data === null || ! isset($data['endpoint'])) {
            return new JsonResult(
                data: ['error' => 'Endpoint required'],
                status: Status::BAD_REQUEST
            );
        }

        try {
            // Find subscription
            $hash = hash('sha256', $data['endpoint']);
            $subscription = $this->subscriptionRepository->findByHash($hash);

            if ($subscription === null) {
                return new JsonResult(
                    data: ['error' => 'Subscription not found'],
                    status: Status::NOT_FOUND
                );
            }

            // Create test message
            $message = PushMessage::simple(
                title: $data['title'] ?? 'Test Notification',
                body: $data['body'] ?? 'This is a test notification from your application!'
            );

            // Send notification
            $result = $this->webPushService->sendTo($subscription, $message);

            if ($result->success) {
                return new JsonResult(
                    data: [
                        'success' => true,
                        'message' => 'Test notification sent successfully',
                        'result' => $result->toArray(),
                    ]
                );
            }

            // Delete subscription if expired
            if ($result->shouldDeleteSubscription()) {
                $this->subscriptionRepository->delete($hash);
            }

            return new JsonResult(
                data: [
                    'success' => false,
                    'error' => $result->errorMessage,
                    'result' => $result->toArray(),
                ],
                status: Status::BAD_REQUEST
            );
        } catch (\Exception $e) {
            return new JsonResult(
                data: ['error' => $e->getMessage()],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Listet alle Subscriptions auf (Admin).
     */
    #[Route(path: '/api/push/subscriptions', method: Method::GET)]
    public function subscriptions(Request $request): JsonResult
    {
        try {
            $subscriptions = $this->subscriptionRepository->findAll();

            return new JsonResult(
                data: [
                    'total' => count($subscriptions),
                    'subscriptions' => array_map(
                        fn (PushSubscription $sub) => [
                            'hash' => $sub->getHash(),
                            'user_id' => $sub->userId,
                            'created_at' => $sub->createdAt?->format('Y-m-d H:i:s'),
                            'expires_at' => $sub->expiresAt?->format('Y-m-d H:i:s'),
                            'is_expired' => $sub->isExpired(),
                            'is_valid' => $sub->isValid(),
                        ],
                        $subscriptions
                    ),
                ]
            );
        } catch (\Exception $e) {
            return new JsonResult(
                data: ['error' => $e->getMessage()],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Broadcast-Notification an alle Subscriptions.
     */
    #[Route(path: '/api/push/broadcast', method: Method::POST)]
    public function broadcast(Request $request): JsonResult
    {
        $data = $request->parsedBody;

        if ($data === null || ! isset($data['title']) || ! isset($data['body'])) {
            return new JsonResult(
                data: ['error' => 'Title and body required'],
                status: Status::BAD_REQUEST
            );
        }

        try {
            // Create message
            $message = PushMessage::simple(
                title: $data['title'],
                body: $data['body']
            );

            // Get all subscriptions
            $subscriptions = $this->subscriptionRepository->findAll();

            if (empty($subscriptions)) {
                return new JsonResult(
                    data: [
                        'success' => true,
                        'message' => 'No subscriptions to send to',
                        'sent' => 0,
                        'failed' => 0,
                    ]
                );
            }

            // Send to all
            $results = $this->webPushService->sendToMany($subscriptions, $message);

            // Count successes and failures
            $sent = 0;
            $failed = 0;
            $expired = [];

            foreach ($results as $index => $result) {
                if ($result->success) {
                    $sent++;
                } else {
                    $failed++;

                    // Delete expired subscriptions
                    if ($result->shouldDeleteSubscription()) {
                        $expired[] = $subscriptions[$index]->getHash();
                        $this->subscriptionRepository->delete($subscriptions[$index]->getHash());
                    }
                }
            }

            return new JsonResult(
                data: [
                    'success' => true,
                    'message' => 'Broadcast completed',
                    'total' => count($subscriptions),
                    'sent' => $sent,
                    'failed' => $failed,
                    'expired_deleted' => count($expired),
                ]
            );
        } catch (\Exception $e) {
            return new JsonResult(
                data: ['error' => $e->getMessage()],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * VAPID Public Key für Frontend.
     */
    #[Route(path: '/api/push/vapid-key', method: Method::GET)]
    public function vapidKey(Request $request): JsonResult
    {
        // This should be injected from config
        $publicKey = $_ENV['VAPID_PUBLIC_KEY'] ?? null;

        if ($publicKey === null) {
            return new JsonResult(
                data: ['error' => 'VAPID public key not configured'],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }

        return new JsonResult(
            data: ['public_key' => $publicKey]
        );
    }

    /**
     * WebPush Demo Page.
     */
    #[Route(path: '/webpush/demo', method: Method::GET)]
    public function demo(Request $request): ViewResult
    {
        return new ViewResult(
            template: 'webpush-demo',
            metaData: MetaData::create('WebPush Demo'),
            data: []
        );
    }
}
