<?php

namespace FSPoster\App\Pages\Settings\Workflow\Services;

use FSPoster\App\Models\WorkflowLog;
use FSPoster\App\Pages\Settings\Workflow\DTOs\Request\WorkflowLogRequest;
use FSPoster\App\Pages\Settings\Workflow\Mappers\WorkflowLogMapper;
use FSPoster\App\Pages\Settings\Workflow\Repositories\WorkflowLogRepository;
use FSPoster\App\Providers\DB\Collection;
use FSPoster\App\Providers\Exceptions\HttpException;
use FSPoster\App\Providers\Helpers\Date;
use RuntimeException;

class WorkflowLogService
{
    public const STATUS_SUCCESS = 'success';
    public const STATUS_ERROR = 'error';

    private WorkflowLogRepository $repository;
    private WorkflowLogMapper $mapper;
    private WorkflowQueueService $queueService;

    public function __construct(WorkflowLogRepository $repository, WorkflowLogMapper $mapper, WorkflowQueueService $queueService)
    {
        $this->repository = $repository;
        $this->mapper = $mapper;
        $this->queueService = $queueService;
    }

    public function getAll(int $page): array
    {
        $logs = $this->repository->getAll($page);
        return $this->mapper->toListResponse($logs);
    }

    /**
     * @param int $id
     * @return WorkflowLog|Collection|null
     */
    public function get(int $id)
    {
        return $this->repository->get($id);
    }

    public function clear(): void
    {
        $this->repository->clear();
    }

    public function getLogsCount(): int
    {
        return $this->repository->getLogsCount();
    }

    public function create(WorkflowLogRequest $request): void
    {
        $this->repository->create([
            'workflow_id' => $request->getWorkflowId(),
            'workflow_action_id' => $request->getWorkflowActionId(),
            'when' => $request->getWhen(),
            'driver' => $request->getDriver(),
            'request_data' => $request->getRequestData(),
            'response_data' => $request->getResponseData(),
            'data' => $request->getData(),
            'event_data' => $request->getEventData(),
            'planned_at' => $request->getPlannedAt() ?? Date::dateTimeSQL(),
            'executed_at' => $request->getExecutedAt(),
            'attempt_count' => $request->getAttemptCount(),
            'status' => $request->getStatus(),
            'error_msg' => $request->getErrorMsg(),
        ]);
    }

    public function delete(int $id): void
    {
        $this->repository->delete($id);
    }

    public function retry(int $id): void
    {
        $log = $this->repository->get($id);

        if ($log === null) {
            throw new RuntimeException(fsp__('Log not found'));
        }

        if (!in_array((string)($log->status ?? ''), ['error', 'canceled', 'success'], true)) {
            throw new HttpException(fsp__('Only completed queue entries can be retried.'));
        }

        $hasDirectPayload = !empty($log->data) && !empty($log->event_data);

        if ($hasDirectPayload) {
            $decodedActionData = json_decode((string)$log->data, true);
            $decodedEventData = json_decode((string)$log->event_data, true);

            if (!is_array($decodedActionData) || !is_array($decodedEventData)) {
                throw new HttpException(fsp__('Log data is invalid'));
            }

            $actionSettingsData = $decodedActionData;
            $eventData = $decodedEventData;
        } else {
            $requestData = !empty($log->request_data) ? json_decode((string)$log->request_data, true) : null;

            if (!is_array($requestData)) {
                throw new HttpException(fsp__('Log data not found'));
            }

            $actionSettingsData = $requestData['actionSettingsData'] ?? null;
            $eventData = $requestData['eventData'] ?? null;
        }

        if (!is_array($actionSettingsData) || empty($actionSettingsData) || !is_array($eventData)) {
            throw new HttpException(fsp__('Log data not found'));
        }

        $data = wp_json_encode($actionSettingsData);
        $eventDataJson = wp_json_encode($eventData);

        if (empty($data) || empty($eventDataJson)) {
            throw new HttpException(fsp__('Log data is invalid'));
        }

        $this->queueService->enqueueRetry([
            'workflow_id' => (int)$log->workflow_id,
            'workflow_action_id' => isset($log->workflow_action_id) ? (int)$log->workflow_action_id : null,
            'when' => (string)$log->when,
            'driver' => (string)$log->driver,
            'data' => $data,
            'event_data' => $eventDataJson,
            'attempt_count' => (int)($log->attempt_count ?? 0),
            'error_msg' => (string)($log->error_msg ?? ''),
            'blog_id' => (int)($log->blog_id ?? 0),
            'schedule_id' => isset($log->schedule_id) ? (int)$log->schedule_id : null,
            'user_id' => isset($log->user_id) ? (int)$log->user_id : null,
            'origin_schedule_id' => isset($log->origin_schedule_id) ? (int)$log->origin_schedule_id : null,
            'chain_depth' => isset($log->chain_depth) ? (int)$log->chain_depth : 0,
        ]);
    }
}
