<?php

namespace HelpGent\App\Support\Element;

defined( 'ABSPATH' ) || exit;

use HelpGent\App\DTO\ElementAnswerDTO;
use HelpGent\App\Exceptions\RequestValidatorException;
use HelpGent\App\Repositories\ElementAnswerRepository;
use HelpGent\WpMVC\RequestValidator\Validator;
use stdClass;
use WP_REST_Request;

abstract class Element {
    abstract public static function get_key(): string;

    public bool $has_nested_answer = false;

    public bool $skip_full_submission = false;

    public ElementAnswerRepository $element_answer_repository;

    public function __construct( ElementAnswerRepository $element_answer_repository ) {
        $this->element_answer_repository = $element_answer_repository;
    }

    abstract public function validate( WP_REST_Request $wp_rest_request, Validator $validator, array $element );

    abstract public function get_summary( stdClass $form, array $element, int $page = 0, int $per_page = 0 );

    public function get_answer_dto( array $values, array $element, int $response_id, int $form_id ): ElementAnswerDTO {
        return ( new ElementAnswerDTO() )
            ->set_response_id( $response_id )
            ->set_form_id( $form_id )
            ->set_element_id( $element['id'] )
            ->set_element_type( $element['element_type'] )
            ->set_value( $values[static::get_key()] );
    }

    public function save_answer( WP_REST_Request $wp_rest_request, array $element, int $response_id, stdClass $form ): bool {
        $value = $wp_rest_request->get_param( static::get_key() );

        $element_id = $wp_rest_request->get_param( 'element_id' );
        $answer     = $this->element_answer_repository->get_by_element_first( $response_id, $element_id, $element['element_type'] );

        if ( ! $this->has_nested_answer && 0 !== $value && empty( $value ) ) {
            return $this->delete_answer( $this->element_answer_repository, $answer );
        }

        $element_answer_dto = $this->get_answer_dto( $wp_rest_request->get_params(), $element, $response_id, $form->id );

        if ( $answer ) {
            $element_answer_dto->set_id( intval( $answer->id ) );
            $update_answer      = ( bool ) $this->element_answer_repository->update( $element_answer_dto );
            $save_nested_answer = true;

            if ( $this->has_nested_answer ) {
                $save_nested_answer = $this->save_nested_answer( $answer->id, $wp_rest_request->get_params(), $form );
            }

            return $update_answer && $save_nested_answer;
        }

        $answer_id          = $this->element_answer_repository->create( $element_answer_dto );
        $save_nested_answer = true;

        if ( $this->has_nested_answer ) {
            $save_nested_answer = $this->save_nested_answer( $answer_id, $wp_rest_request->get_params(), $form );
        }

        return ( bool ) $answer_id && $save_nested_answer;
    }

    public function save_nested_answer( int $answer_id, array $values, stdClass $form ): bool {
        // Save the nested answer.

        return true;
    }

    protected static function delete_answer( ElementAnswerRepository $element_answer_repository, $answer ) {
        if ( empty( $answer ) ) {
            return false;
        }

        return $element_answer_repository->delete( $answer->id );
    }

    public static function throw_validator_errors( Validator $validator ) {
        if ( $validator->is_fail() ) {
            static::throw_errors( $validator->errors );
        }
    }

    protected static function sanitize_error_messages( array $errors ): array {
        $sanitized = [];

        foreach ( $errors as $key => $messages ) {
            if ( ! is_array( $messages ) ) {
                $sanitized[ $key ] = [ esc_html( (string) $messages ) ];
                continue;
            }

            $flattened_messages = [];

            array_walk_recursive(
                $messages,
                function( $message ) use ( &$flattened_messages ) {
                    $flattened_messages[] = esc_html( (string) $message );
                }
            );

            $sanitized[ $key ] = $flattened_messages;
        }

        return $sanitized;
    }

    public static function throw_errors( array $errors ) {
        throw new RequestValidatorException(
            "",
            422,
            null,
            static::sanitize_error_messages( $errors )
        );
    }
}
