<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\Polling;

use App\Framework\Attributes\Route;
use App\Framework\Http\Method;
use App\Framework\Http\JsonResult;
use App\Framework\Http\Status;
use App\Framework\Logging\Logger;

/**
 * Controller handling polling requests for both attribute-based and closure-based polls.
 *
 * Unified endpoint: /poll/{pollId}
 * - Handles PollableClosure polls (pollId starts with "closure.")
 * - Handles #[Poll] attribute polls (pollId starts with "attribute.")
 */
final readonly class PollController
{
    public function __construct(
        private PollableClosureRegistry $closureRegistry,
        private PollExecutor $pollExecutor,
        private Logger $logger
    ) {}

    /**
     * Unified polling endpoint for both poll types.
     */
    #[Route(path: '/poll/{pollId}', method: Method::GET)]
    public function poll(string $pollId): JsonResult
    {
        $pollIdVO = PollId::fromString($pollId);

        try {
            // Route to appropriate handler based on poll ID type
            if ($pollIdVO->isClosure()) {
                return $this->handleClosurePoll($pollIdVO);
            }

            if ($pollIdVO->isAttribute()) {
                return $this->handleAttributePoll($pollIdVO);
            }

            // Unknown poll ID format
            $this->logger->warning('Unknown poll ID format', [
                'poll_id' => $pollId
            ]);

            return new JsonResult(
                data: ['error' => 'Unknown poll type'],
                status: Status::NOT_FOUND
            );

        } catch (\Throwable $e) {
            $this->logger->error('Poll execution failed', [
                'poll_id' => $pollId,
                'error' => $e->getMessage()
            ]);

            return new JsonResult(
                data: ['error' => 'Poll execution failed'],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Handle closure-based poll execution.
     */
    private function handleClosurePoll(PollId $pollId): JsonResult
    {
        $registration = $this->closureRegistry->get($pollId);

        if ($registration === null) {
            $this->logger->warning('Closure poll not found', [
                'poll_id' => (string) $pollId
            ]);

            return new JsonResult(
                data: ['error' => 'Poll not found'],
                status: Status::NOT_FOUND
            );
        }

        if (!$registration->shouldPoll()) {
            return new JsonResult(
                data: ['error' => 'Poll is disabled'],
                status: Status::FORBIDDEN
            );
        }

        try {
            $result = $this->closureRegistry->execute($pollId);

            return new JsonResult(
                data: $result,
                status: Status::OK
            );

        } catch (\Throwable $e) {
            $this->logger->error('Closure poll execution failed', [
                'poll_id' => (string) $pollId,
                'template_key' => $registration->templateKey,
                'error' => $e->getMessage()
            ]);

            if ($registration->closure->stopOnError) {
                // Unregister poll if stopOnError is enabled
                $this->closureRegistry->unregister($pollId);
            }

            return new JsonResult(
                data: ['error' => 'Execution failed'],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Handle attribute-based poll execution.
     */
    private function handleAttributePoll(PollId $pollId): JsonResult
    {
        // Parse poll ID format: "attribute.ClassName::methodName"
        $pollIdString = (string) $pollId;
        $parts = explode('attribute.', $pollIdString, 2);

        if (count($parts) !== 2) {
            return new JsonResult(
                data: ['error' => 'Invalid attribute poll ID format'],
                status: Status::BAD_REQUEST
            );
        }

        $classMethod = $parts[1];
        $classMethodParts = explode('::', $classMethod, 2);

        if (count($classMethodParts) !== 2) {
            return new JsonResult(
                data: ['error' => 'Invalid class::method format'],
                status: Status::BAD_REQUEST
            );
        }

        [$className, $methodName] = $classMethodParts;

        // Check if poll exists
        if (!$this->pollExecutor->isPollable($className, $methodName)) {
            $this->logger->warning('Attribute poll not found', [
                'poll_id' => $pollIdString,
                'class' => $className,
                'method' => $methodName
            ]);

            return new JsonResult(
                data: ['error' => 'Poll not found'],
                status: Status::NOT_FOUND
            );
        }

        // Execute poll
        $result = $this->pollExecutor->executePoll($className, $methodName);

        if ($result->isFailed()) {
            $this->logger->error('Attribute poll execution failed', [
                'poll_id' => $pollIdString,
                'class' => $className,
                'method' => $methodName,
                'error' => $result->error?->getMessage(),
                'reason' => $result->reason
            ]);

            return new JsonResult(
                data: ['error' => $result->reason ?? 'Execution failed'],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }

        return new JsonResult(
            data: $result->data,
            status: Status::OK
        );
    }

    /**
     * Get polling metadata for debugging.
     */
    #[Route(path: '/poll/metadata', method: Method::GET)]
    public function metadata(): JsonResult
    {
        return new JsonResult([
            'closure_polls' => $this->closureRegistry->getPollingMetadata(),
            'attribute_polls' => $this->pollExecutor->getExecutionStats()
        ]);
    }
}
