<?php

declare(strict_types=1);

namespace App\Modules\Administration\Services;

use App\Modules\Administration\Repositories\DynamicFormSubmissionRepository;
use RuntimeException;
use Throwable;

final class DynamicFormSubmissionService
{
    public function __construct(
        private DynamicFormSubmissionRepository $submissions,
        private DynamicFormFieldService $fieldService
    ) {
    }


    public function submit(
        int $formId,
        array $data,
        ?int $submittedBy = null,
        ?int $employeeId = null
    ): int {
        if ($formId <= 0) {
            throw new RuntimeException(
                'Invalid dynamic form ID.'
            );
        }

        $fields = $this->fieldService
            ->byForm($formId);

        if (empty($fields)) {
            throw new RuntimeException(
                'This form does not contain any fields.'
            );
        }

        $this->submissions
            ->beginTransaction();

        try {
            $now = date('Y-m-d H:i:s');

            $submissionId =
                $this->submissions->create([
                    'form_id' =>
                        $formId,

                    'employee_id' =>
                        $employeeId,

                    'submitted_by' =>
                        $submittedBy,

                    'submission_status' =>
                        'submitted',

                    'submitted_at' =>
                        $now,

                    'status' =>
                        1,

                    'created_at' =>
                        $now,

                    'updated_at' =>
                        $now
                ]);

            if ($submissionId <= 0) {
                throw new RuntimeException(
                    'Submission creation failed.'
                );
            }


            foreach ($fields as $field) {
                $fieldId = (int) (
                    $field['id']
                    ?? 0
                );

                if ($fieldId <= 0) {
                    continue;
                }


                $fieldName = (string) (
                    $field['field_name']
                    ?? ''
                );

                if ($fieldName === '') {
                    continue;
                }


                $fieldType = strtolower(
                    (string) (
                        $field['field_type']
                        ?? 'text'
                    )
                );


                /*
                 * File upload will be implemented
                 * in the next stage.
                 */

                if ($fieldType === 'file') {
                    continue;
                }


                $value =
                    $data[$fieldName]
                    ?? null;


                /*
                 * Validate required fields.
                 */

                $required =
                    (int) (
                        $field['is_required']
                        ?? 0
                    ) === 1;


                if (
                    $required
                    &&
                    $this->isEmptyValue($value)
                ) {
                    throw new RuntimeException(
                        (
                            $field['field_label']
                            ?? 'Required field'
                        )
                        . ' is required.'
                    );
                }


                /*
                 * Checkbox values arrive as arrays.
                 * Store them as JSON.
                 */

                if (
                    $fieldType === 'checkbox'
                ) {
                    $value = is_array($value)
                        ? array_values(
                            array_filter(
                                $value,
                                static fn (
                                    mixed $item
                                ): bool =>
                                    trim(
                                        (string) $item
                                    ) !== ''
                            )
                        )
                        : [];


                    $valueText =
                        json_encode(
                            $value,
                            JSON_UNESCAPED_UNICODE
                            | JSON_UNESCAPED_SLASHES
                        );


                    if ($valueText === false) {
                        throw new RuntimeException(
                            'Checkbox value encoding failed.'
                        );
                    }

                } else {

                    if (is_array($value)) {
                        throw new RuntimeException(
                            'Invalid value format for '
                            . (
                                $field['field_label']
                                ?? 'field'
                            )
                            . '.'
                        );
                    }


                    $valueText =
                        $value !== null
                        ? trim(
                            (string) $value
                        )
                        : null;
                }


                /*
                 * Save field value.
                 */

                $created =
                    $this->submissions
                        ->createValue([
                            'submission_id' =>
                                $submissionId,

                            'field_id' =>
                                $fieldId,

                            'value_text' =>
                                $valueText,

                            'file_path' =>
                                null,

                            'file_original_name' =>
                                null,

                            'file_mime_type' =>
                                null,

                            'file_size' =>
                                null,

                            'created_at' =>
                                $now,

                            'updated_at' =>
                                $now
                        ]);


                if (!$created) {
                    throw new RuntimeException(
                        'Failed to save field: '
                        . (
                            $field['field_label']
                            ?? $fieldName
                        )
                    );
                }
            }


            $this->submissions
                ->commit();


            return $submissionId;

        } catch (Throwable $exception) {

            $this->submissions
                ->rollBack();


            throw $exception;
        }
    }


    public function find(
        int $id
    ): ?array {
        return $this->submissions
            ->find($id);
    }


    public function values(
        int $submissionId
    ): array {
        return $this->submissions
            ->values($submissionId);
    }


    public function byForm(
        int $formId
    ): array {
        return $this->submissions
            ->byForm($formId);
    }


    public function byEmployee(
        int $employeeId
    ): array {
        return $this->submissions
            ->byEmployee($employeeId);
    }


    private function isEmptyValue(
        mixed $value
    ): bool {
        if ($value === null) {
            return true;
        }

        if (is_array($value)) {
            return count(
                array_filter(
                    $value,
                    static fn (
                        mixed $item
                    ): bool =>
                        trim(
                            (string) $item
                        ) !== ''
                )
            ) === 0;
        }

        return trim(
            (string) $value
        ) === '';
    }
}