<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\Controllers;

use App\Framework\Attributes\Route;
use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Http\Enums\Method;
use App\Framework\Http\HttpRequest;
use App\Framework\LiveComponents\Services\ChunkedUploadManager;
use App\Framework\LiveComponents\Services\UploadProgressTracker;
use App\Framework\LiveComponents\ValueObjects\ChunkHash;
use App\Framework\LiveComponents\ValueObjects\UploadSessionId;
use App\Framework\Router\Result\JsonResponse;
use App\Framework\Router\Result\Status;

/**
 * Chunked Upload Controller
 *
 * REST API für Chunked/Resumable File Uploads:
 * - POST /live-component/upload/init - Initialize upload session
 * - POST /live-component/upload/chunk - Upload single chunk
 * - POST /live-component/upload/complete - Finalize upload
 * - POST /live-component/upload/abort - Abort upload
 * - GET /live-component/upload/status/{sessionId} - Get upload status
 */
final readonly class ChunkedUploadController
{
    public function __construct(
        private ChunkedUploadManager $uploadManager,
        private UploadProgressTracker $progressTracker
    ) {
    }

    /**
     * Initialize new chunked upload session
     *
     * Request Body:
     * {
     *   "componentId": "file-uploader",
     *   "fileName": "large-file.zip",
     *   "totalSize": 104857600,
     *   "chunkSize": 524288,
     *   "expectedFileHash": "sha256_hash_optional"
     * }
     *
     * Response:
     * {
     *   "success": true,
     *   "sessionId": "abc123...",
     *   "totalChunks": 200,
     *   "expiresAt": "2024-01-15T12:00:00Z"
     * }
     */
    #[Route('/live-component/upload/init', method: Method::POST)]
    public function initialize(HttpRequest $request): JsonResponse
    {
        try {
            $data = $request->parsedBody->toArray();

            // Validate required fields
            if (empty($data['componentId']) || empty($data['fileName']) || empty($data['totalSize']) || empty($data['chunkSize'])) {
                return new JsonResponse(
                    data: [
                        'success' => false,
                        'error' => 'Missing required fields: componentId, fileName, totalSize, chunkSize',
                    ],
                    status: Status::BAD_REQUEST
                );
            }

            // Parse parameters
            $componentId = (string) $data['componentId'];
            $fileName = (string) $data['fileName'];
            $totalSize = Byte::fromBytes((int) $data['totalSize']);
            $chunkSize = Byte::fromBytes((int) $data['chunkSize']);
            $expectedFileHash = !empty($data['expectedFileHash'])
                ? ChunkHash::fromString((string) $data['expectedFileHash'])
                : null;

            // Get user ID from session/auth (simplified - should use proper auth)
            $userId = $this->getUserIdFromRequest($request);

            // Initialize upload session
            $session = $this->uploadManager->initializeUpload(
                componentId: $componentId,
                fileName: $fileName,
                totalSize: $totalSize,
                chunkSize: $chunkSize,
                expectedFileHash: $expectedFileHash,
                userId: $userId
            );

            return new JsonResponse(data: [
                'success' => true,
                'session_id' => $session->sessionId->toString(),
                'total_chunks' => $session->totalChunks,
                'expires_at' => $session->expiresAt->format('c'),
            ]);
        } catch (\InvalidArgumentException $e) {
            return new JsonResponse(
                data: [
                    'success' => false,
                    'error' => $e->getMessage(),
                ],
                status: Status::BAD_REQUEST
            );
        } catch (\Exception $e) {
            return new JsonResponse(
                data: [
                    'success' => false,
                    'error' => 'Failed to initialize upload session',
                ],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Upload single chunk
     *
     * Request Body (multipart/form-data):
     * - sessionId: string
     * - chunkIndex: int
     * - chunkHash: string (sha256)
     * - chunk: binary data
     *
     * Response:
     * {
     *   "success": true,
     *   "progress": 45.5,
     *   "uploadedChunks": 91,
     *   "totalChunks": 200
     * }
     */
    #[Route('/live-component/upload/chunk', method: Method::POST)]
    public function uploadChunk(HttpRequest $request): JsonResponse
    {
        try {
            // Parse form data
            $sessionId = $request->parsedBody->get('sessionId');
            $chunkIndex = $request->parsedBody->get('chunkIndex');
            $chunkHashString = $request->parsedBody->get('chunkHash');

            if (empty($sessionId) || $chunkIndex === null || empty($chunkHashString)) {
                return new JsonResponse(
                    data: [
                        'success' => false,
                        'error' => 'Missing required fields: sessionId, chunkIndex, chunkHash',
                    ],
                    status: Status::BAD_REQUEST
                );
            }

            // Get chunk data from uploaded file
            $uploadedFiles = $request->uploadedFiles ?? [];
            if (empty($uploadedFiles['chunk'])) {
                return new JsonResponse(
                    data: [
                        'success' => false,
                        'error' => 'No chunk file uploaded',
                    ],
                    status: Status::BAD_REQUEST
                );
            }

            $chunkFile = $uploadedFiles['chunk'];
            $chunkData = file_get_contents($chunkFile['tmp_name']);

            if ($chunkData === false) {
                return new JsonResponse(
                    data: [
                        'success' => false,
                        'error' => 'Failed to read chunk data',
                    ],
                    status: Status::INTERNAL_SERVER_ERROR
                );
            }

            // Parse parameters
            $sessionIdObj = UploadSessionId::fromString((string) $sessionId);
            $chunkIndexInt = (int) $chunkIndex;
            $chunkHash = ChunkHash::fromString((string) $chunkHashString);

            // Get user ID
            $userId = $this->getUserIdFromRequest($request);

            // Upload chunk
            $session = $this->uploadManager->uploadChunk(
                sessionId: $sessionIdObj,
                chunkIndex: $chunkIndexInt,
                chunkData: $chunkData,
                providedHash: $chunkHash,
                userId: $userId
            );

            return new JsonResponse(data: [
                'success' => true,
                'progress' => $session->getProgress(),
                'uploaded_chunks' => count($session->getUploadedChunks()),
                'total_chunks' => $session->totalChunks,
            ]);
        } catch (\InvalidArgumentException $e) {
            return new JsonResponse(
                data: [
                    'success' => false,
                    'error' => $e->getMessage(),
                ],
                status: Status::BAD_REQUEST
            );
        } catch (\Exception $e) {
            return new JsonResponse(
                data: [
                    'success' => false,
                    'error' => 'Failed to upload chunk',
                ],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Complete upload and assemble file
     *
     * Request Body:
     * {
     *   "sessionId": "abc123...",
     *   "targetPath": "/uploads/final-file.zip"
     * }
     *
     * Response:
     * {
     *   "success": true,
     *   "filePath": "/uploads/final-file.zip",
     *   "completedAt": "2024-01-15T12:30:00Z"
     * }
     */
    #[Route('/live-component/upload/complete', method: Method::POST)]
    public function complete(HttpRequest $request): JsonResponse
    {
        try {
            $data = $request->parsedBody->toArray();

            if (empty($data['sessionId']) || empty($data['targetPath'])) {
                return new JsonResponse(
                    data: [
                        'success' => false,
                        'error' => 'Missing required fields: sessionId, targetPath',
                    ],
                    status: Status::BAD_REQUEST
                );
            }

            $sessionId = UploadSessionId::fromString((string) $data['sessionId']);
            $targetPath = (string) $data['targetPath'];

            // Get user ID
            $userId = $this->getUserIdFromRequest($request);

            // Complete upload
            $session = $this->uploadManager->completeUpload(
                sessionId: $sessionId,
                targetPath: $targetPath,
                userId: $userId
            );

            return new JsonResponse(data: [
                'success' => true,
                'file_path' => $targetPath,
                'completed_at' => $session->completedAt?->format('c'),
            ]);
        } catch (\InvalidArgumentException $e) {
            return new JsonResponse(
                data: [
                    'success' => false,
                    'error' => $e->getMessage(),
                ],
                status: Status::BAD_REQUEST
            );
        } catch (\Exception $e) {
            return new JsonResponse(
                data: [
                    'success' => false,
                    'error' => 'Failed to complete upload',
                ],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Abort upload and cleanup
     *
     * Request Body:
     * {
     *   "sessionId": "abc123...",
     *   "reason": "User cancelled"
     * }
     *
     * Response:
     * {
     *   "success": true
     * }
     */
    #[Route('/live-component/upload/abort', method: Method::POST)]
    public function abort(HttpRequest $request): JsonResponse
    {
        try {
            $data = $request->parsedBody->toArray();

            if (empty($data['sessionId'])) {
                return new JsonResponse(
                    data: [
                        'success' => false,
                        'error' => 'Missing required field: sessionId',
                    ],
                    status: Status::BAD_REQUEST
                );
            }

            $sessionId = UploadSessionId::fromString((string) $data['sessionId']);
            $reason = $data['reason'] ?? 'User cancelled';

            // Get user ID
            $userId = $this->getUserIdFromRequest($request);

            // Abort upload
            $this->uploadManager->abortUpload(
                sessionId: $sessionId,
                userId: $userId,
                reason: (string) $reason
            );

            return new JsonResponse(data: [
                'success' => true,
            ]);
        } catch (\Exception $e) {
            return new JsonResponse(
                data: [
                    'success' => false,
                    'error' => 'Failed to abort upload',
                ],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Get upload status
     *
     * Response:
     * {
     *   "success": true,
     *   "progress": 45.5,
     *   "uploadedChunks": 91,
     *   "totalChunks": 200,
     *   "phase": "uploading",
     *   "quarantineStatus": "pending"
     * }
     */
    #[Route('/live-component/upload/status/{sessionId}', method: Method::GET)]
    public function status(string $sessionId): JsonResponse
    {
        try {
            $sessionIdObj = UploadSessionId::fromString($sessionId);

            $progress = $this->progressTracker->getProgress($sessionIdObj);

            if ($progress === null) {
                return new JsonResponse(
                    data: [
                        'success' => false,
                        'error' => 'Upload session not found',
                    ],
                    status: Status::NOT_FOUND
                );
            }

            return new JsonResponse(data: [
                'success' => true,
                'progress' => $progress['progress'],
                'uploaded_chunks' => $progress['uploaded_chunks'],
                'total_chunks' => $progress['total_chunks'],
                'uploaded_bytes' => $progress['uploaded_bytes'],
                'total_bytes' => $progress['total_bytes'],
                'phase' => $progress['phase'],
                'quarantine_status' => $progress['quarantine_status'],
            ]);
        } catch (\Exception $e) {
            return new JsonResponse(
                data: [
                    'success' => false,
                    'error' => 'Failed to get upload status',
                ],
                status: Status::INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * Get user ID from request
     *
     * Simplified implementation - should use proper authentication
     */
    private function getUserIdFromRequest(HttpRequest $request): ?string
    {
        // TODO: Implement proper authentication
        // For now, return null to disable SSE tracking
        // In production, extract from session/JWT token
        return null;
    }
}
