<?php

namespace App\Controller\OgRepository\Image;

use App\Controller\OgRepository\AbstractOgRepositoryController;
use App\Dto\Input\BackupImageInput;
use App\Dto\Input\DeleteImageInput;
use App\Entity\Image;
use App\Entity\ImageImageRepository;
use App\Entity\ImageRepository;
use App\Model\CommandTypes;
use App\Model\ImageStatus;
use App\Model\TraceStatus;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;

#[AsController]
class BackupImageAction extends AbstractOgRepositoryController
{
    /**
     * @throws TransportExceptionInterface
     * @throws ServerExceptionInterface
     * @throws RedirectionExceptionInterface
     * @throws ClientExceptionInterface
     */
    public function __invoke(BackupImageInput $input, ImageImageRepository $imageImageRepository): JsonResponse
    {
        $image = $imageImageRepository->getImage();
        $repository = $imageImageRepository->getRepository();

        $this->validateImageName($image);
        $this->logger->info('Create backup image', ['image' => $image->getName()]);

        $content = $this->createBackupRequest($input, $imageImageRepository, $repository);
        $this->handleBackupResponse($content);

        $this->processBackupSuccess($input, $image, $repository, $imageImageRepository, $content);

        return new JsonResponse(data: $content, status: Response::HTTP_OK);
    }

    private function validateImageName(Image $image): void
    {
        if (!$image->getName()) {
            throw new BadRequestHttpException('Name is required');
        }
    }

    private function createBackupRequest(BackupImageInput $input, ImageImageRepository $imageImageRepository, ImageRepository $repository): array
    {
        $params = [
            'json' => [
                'ID_img' => $imageImageRepository->getImageFullsum(),
                'repo_ip' => $input->repoIp,
                'ssh_port' => $repository->getSshPort() ?? '22',
                'remote_path' => $input->remotePath,
                'user' => $repository->getUser() ?? 'opengnsys',
            ]
        ];

        return $this->createRequest('PUT', 'http://'.$repository->getIp().':8006/ogrepository/v1/repo/images', $params);
    }

    private function handleBackupResponse(array $content): void
    {
        if (!isset($content['error'])) {
            return;
        }

        $errorMessage = $this->extractErrorMessage($content);
        $errorCode = $content['code'] ?? Response::HTTP_BAD_REQUEST;

        $this->throwAppropriateException($errorCode, $errorMessage);
    }

    private function extractErrorMessage(array $content): string
    {
        $errorMessage = $content['error'];
        $errorDetails = $content['details'] ?? null;

        if (!$errorDetails) {
            return $errorMessage;
        }

        $extractedMessage = $this->extractMessageFromDetails($errorDetails);
        
        return $extractedMessage ?: $errorMessage;
    }

    private function extractMessageFromDetails($errorDetails): ?string
    {
        if (is_array($errorDetails)) {
            return $errorDetails['details'] ?? $errorDetails['message'] ?? $errorDetails['error'] ?? null;
        }

        if (is_string($errorDetails)) {
            $parsed = $this->parseJsonString($errorDetails);
            if ($parsed !== null) {
                return $parsed['details'] ?? $parsed['message'] ?? $parsed['error'] ?? null;
            }
            
            return !empty($errorDetails) ? $errorDetails : null;
        }

        return null;
    }

    private function parseJsonString(string $jsonString): ?array
    {
        $cleanJson = trim($jsonString, " \t\n\r\0\x0B");
        $decoded = json_decode($cleanJson, true);
    
        return (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) ? $decoded : null;
    }

    private function throwAppropriateException(int $errorCode, string $errorMessage): void
    {
        if ($errorCode === Response::HTTP_INTERNAL_SERVER_ERROR) {
            throw new BadRequestHttpException('Error backing up image: ' . $errorMessage);
        }

        if ($errorCode >= 400 && $errorCode < 500) {
            throw new BadRequestHttpException($errorMessage);
        }

        if ($errorCode >= 500) {
            throw new BadRequestHttpException('Error backing up image: ' . $errorMessage);
        }

        throw new BadRequestHttpException($errorMessage);
    }

    private function processBackupSuccess(
        BackupImageInput $input, 
        Image $image, 
        ImageRepository $repository, 
        ImageImageRepository $imageImageRepository, 
        array $content
    ): void {
        $inputData = [
            'imageName' => $image->getName(),
            'repositoryUuid' => $repository->getUuid(),
            'imageImageRepositoryUuid' => $imageImageRepository->getUuid(),
            'ID_img' => $imageImageRepository->getImageFullsum(),
            'repo_ip' => $input->repoIp,
            'remote_path' => $input->remotePath
        ];

        $this->createService->__invoke(
            $image->getClient(), 
            CommandTypes::BACKUP_IMAGE, 
            TraceStatus::IN_PROGRESS, 
            $content['job_id'], 
            $inputData
        );

        $imageImageRepository->setStatus(ImageStatus::BACKUP);
        $this->entityManager->persist($imageImageRepository);
        $this->entityManager->flush();
    }
}