<?php

namespace HelpGent\App\Http\Controllers;

defined( 'ABSPATH' ) || exit;

use WP_Error;
use WP_REST_Request;
use HelpGent\App\DTO\UserDTO;
use HelpGent\App\DTO\ContactInfoDTO;
use HelpGent\App\EnumeratedList\UserType;
use HelpGent\App\Utils\AttemptValidator;
use HelpGent\App\Http\Controllers\Controller;
use HelpGent\App\Mail\Login;
use HelpGent\App\Mail\GuestTokenRenew;
use HelpGent\App\Models\Response as ResponseModel;
use HelpGent\App\Repositories\UserRepository;
use HelpGent\App\Repositories\FormRepository;
use HelpGent\App\Repositories\ContactInfoRepository;
use HelpGent\App\Repositories\ResponseRepository;
use HelpGent\App\Repositories\SettingsRepository;
use HelpGent\WpMVC\RequestValidator\Validator;
use HelpGent\WpMVC\Routing\Response;
use HelpGent\WpMVC\Exceptions\Exception;

class UserController extends Controller {
    public UserRepository $repository;

    public FormRepository $form_repository;

    public ResponseRepository $response_repository;

    public ContactInfoRepository $contact_repository;

    public SettingsRepository $settings_repository;

    public function __construct(
        UserRepository $repository,
        FormRepository $form_repository,
        ResponseRepository $response_repository,
        ContactInfoRepository $contact_repository,
        SettingsRepository $settings_repository
    ) {
        $this->repository          = $repository;
        $this->form_repository     = $form_repository;
        $this->response_repository = $response_repository;
        $this->contact_repository  = $contact_repository;
        $this->settings_repository = $settings_repository;
    }

    public function get() {
        $current_user = helpgent_get_current_user();

        $totals = ResponseModel::query()->select( 'count(id) as response', 'count(distinct form_id) as form' )->where( 'created_by', $current_user->get_id() )->get();

        $user                         = $this->repository->get_by_id( $current_user->get_id() );
        $user->is_administrator       = $current_user->is_administrator();
        $user->is_agent               = $current_user->is_agent();
        $user->guest_user_auto_remove = intval( helpgent_get_setting( 'guest_user_auto_remove' ) );
        $user->totals                 = $totals;
        $user->avatar_url             = helpgent_get_user_avatar_url( $user );

        $registration_page_id = $this->settings_repository->get_by_key( 'user_registration_page' );

        if ( ! empty( $registration_page_id ) ) {
            $user->registration_page = get_permalink( intval( $registration_page_id ) );
        } else {
            $user->registration_page = null;
        }

        return Response::send(
            [
                'user' => $user
            ]
        );
    }

    public function logout() {
        helpgent_logout();
        wp_logout();

        return Response::send( ['message' => esc_html__( 'Logged out', 'helpgent' )] );
    }

    public function update( Validator $validator, WP_REST_Request $request ) {
        $validator->validate(
            [
                'name'     => 'required|string',
                'timezone' => 'required|string',
                'about'    => 'required|string',
                'avatar'   => 'file|mimes:jpg,jpeg,png|max:10'
            ]
        );

        $normalize_line_endings = static function( string $value ): string {
            return str_replace( [ "\r\n", "\r" ], "\n", $value );
        };

        $string_length = static function( string $value ) use ( $normalize_line_endings ): int {
            $value = $normalize_line_endings( $value );

            return function_exists( 'mb_strlen' ) ? mb_strlen( $value ) : strlen( $value );
        };

        $name     = $normalize_line_endings( (string) $request->get_param( 'name' ) );
        $timezone = $normalize_line_endings( (string) $request->get_param( 'timezone' ) );
        $about    = $normalize_line_endings( (string) $request->get_param( 'about' ) );

        $validation_errors = [];

        if ( $string_length( $name ) > 100 ) {
            $validation_errors['name'][] = esc_html__( 'The name must not be greater than 100 characters.', 'helpgent' );
        }

        if ( $string_length( $timezone ) > 50 ) {
            $validation_errors['timezone'][] = esc_html__( 'The timezone must not be greater than 50 characters.', 'helpgent' );
        }

        if ( $string_length( $about ) > 180 ) {
            $validation_errors['about'][] = esc_html__( 'The about must not be greater than 180 characters.', 'helpgent' );
        }

        if ( ! empty( $validation_errors ) ) {
            throw ( new Exception( '', 422 ) )->set_messages( $validation_errors );
        }

        $user = helpgent_get_current_user();
        $dto  = ( new UserDTO() )
            ->set_id( $user->get_id() )
            ->set_name( $name )
            ->set_timezone( $timezone )
            ->set_about( $about );

        /**
         * Upload custom avatar
         */
        $files = $request->get_file_params();

        if ( ! empty( $files['avatar'] ) ) {
            $upload_avatar = helpgent_upload_attachment( $files['avatar'] );

            if ( $upload_avatar ) {
                if ( ! empty( $user->get_avatar_id() ) ) {
                    wp_delete_attachment( $user->get_avatar_id(), true );
                }
                $dto->set_avatar_id( $upload_avatar['attachment_id'] );
            }
        }

        $this->repository->update( $dto );

        return Response::send(
            [
                'message' => esc_html__( "Profile information has been successfully updated.", "helpgent" )
            ]
        );
    }

    public function update_email( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'email' => 'required|string|email|max:100|confirmed'
            ]
        );

        $email = sanitize_email( $wp_rest_request->get_param( 'email' ) );

        if ( $wp_rest_request->get_param( 'email' ) !== $email ) {
            $error = new Exception( '', 422 );

            $error->set_messages(
                [
                    'email' => [
                        esc_html__( "Please enter a valid email", "helpgent" )
                    ]
                ]
            );

            throw $error;
        }

        $current_user = helpgent_get_current_user();

        if ( $current_user->get_registered_id() || $this->repository->get_by_email( $email ) ) {
            throw new Exception( "Email already exists" );
        }

        $this->repository->update_email( $current_user->get_id(), $email );

        return Response::send(
            [
                'message' => esc_html__( "The email has been successfully updated.", "helpgent" )
            ]
        );
    }

    public function register( Validator $validator, WP_REST_Request $request ) {
        $validator->validate(
            [
                'name'           => 'string|max:255',
                'email'          => 'required|string|email|max:255',
                'number'         => 'string|max:255',
                'phone_number'   => 'string|max:255',
                'company'        => 'string|max:255',
                'company_name'   => 'string|max:255',
                'form_id'        => 'required|numeric',
                'response_token' => 'required|string',
            ]
        );

        // Retrieve the form by its ID.
        $form_id = intval( $request->get_param( 'form_id' ) );
        $form    = $this->form_repository->get_by_id_publish( $form_id );

        // Return 404 if the form is not found.
        if ( ! $form ) {
            throw new Exception( esc_html__( 'Form not found', 'helpgent' ) );
        }

        $response_token = $request->get_param( 'response_token' );
        $response       = $response_token ? helpgent_get_response_by_token( $response_token, $form->id ) : null;

        if ( ! $response ) {
            throw new Exception( esc_html__( 'Response not found', 'helpgent' ) );
        }

        if ( '1' === $response->is_completed ) {
            throw new Exception( esc_html__( 'Response already completed', 'helpgent' ) );
        }

        if ( $form_id != $response->form_id ) {
            throw new Exception( esc_html__( "Oops, something went wrong. Please try again.", "helpgent" ) );
        }

        $email = $request->get_param( 'email' );
        $phone_number = $request->get_param( 'phone_number' );
        $company_name = $request->get_param( 'company_name' );

        if ( empty( $phone_number ) ) {
            $phone_number = $request->get_param( 'number' );
        }

        if ( empty( $company_name ) ) {
            $company_name = $request->get_param( 'company' );
        }

        $response_contact_info = $this->contact_repository->get_response_contact_info( $response->id );

        if ( ! $response_contact_info ) {
            $contact_dto = ( new ContactInfoDTO )->set_form_id( $form->id )
            ->set_response_id( $response->id )
            ->set_name( $request->get_param( 'name' ) )
            ->set_email( $email )
            ->set_mobile_number( $phone_number )
            ->set_company( $company_name )
            ->set_form_data( ['created_by' => $form->created_by ] )
            ->set_is_lead( 1 );

            $this->contact_repository->create( $contact_dto );
        } else {
            $contact_dto = ( new ContactInfoDTO )->set_id( $response_contact_info->id )
            ->set_name( $request->get_param( 'name' ) )
            ->set_email( $email )
            ->set_mobile_number( $phone_number )
            ->set_company( $company_name );

            $this->contact_repository->update( $contact_dto );
        }

        $user = $this->repository->get_by_email( $email, 'id' );

        if ( $user ) {
            if ( $response->created_by !== $user->id ) {
                // Update response
                $this->response_repository->update_create_by( $response->id, $user->id );

                // Switch user
                $this->repository->switch_user( $response->created_by, $user->id, true );
            }
        } else {
            $dto = ( new UserDTO )
                ->set_id( $response->created_by )
                ->set_user_type( UserType::USER )
                ->set_email( $email )
                ->set_name( $request->get_param( 'name' ) )
                ->set_number( $phone_number )
                ->set_company( $company_name );

            // $form_elements = json_decode( $form->content, true )['elements'];

            // $user_validation_element_key = array_search( 'user_validation', array_column( $form_elements, 'element_type' ) );

            // if ( is_int( $user_validation_element_key ) ) {
            //     $is_visible_acceptance = intval( helpgent_get_nested_value( 'guest_field.acceptance.isVisible', $form_elements[$user_validation_element_key] ) );
            //     if ( 1 === $is_visible_acceptance ) {
            //         $guest_dto->set_acceptance( 1 );
            //     }

            $this->repository->update( $dto );
        }

        return Response::send( [] );
    }

    public function login_user( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'email' => 'required|email'
            ]
        );

        $email = $wp_rest_request->get_param( 'email' );
        $user  = $this->repository->get_by_email( $email );

        if ( ! $user ) {
            throw new Exception( esc_html__( "User not found.", "helpgent" ) );
        }

        $current_user_ip       = helpgent_get_user_ip_address();
        $attempt_key           = "login_{$current_user_ip}";
        $total_allowed_attempt = apply_filters( 'helpgent_user_login_total_allowed_attempt', 2 );
        $waiting_time          = apply_filters( 'helpgent_user_login_waiting_time', HOUR_IN_SECONDS );

        $attempt  = new AttemptValidator( $attempt_key, $total_allowed_attempt, $waiting_time );
        $validate = $attempt->validate();

        if ( $validate instanceof WP_Error ) {
            throw new Exception( esc_html( $validate->get_error_message() ), esc_attr( $validate->get_error_code() ) );
        }

        $mailer = new Login( $this->settings_repository->get_email_template_settings(), $user );
        $mailer->send();

        return Response::send(
            [
                // translators: %s: email address
                'message' => sprintf( esc_html__( "A login link has been sent to the email %s. Please check your inbox.", "helpgent" ), $email )
            ]
        );
    }

    public function token_renew() {
        $user = helpgent_get_user_from_cookie();

        if ( empty( $user ) ) {
            return Response::send(
                [
                    'message' => esc_html__( "User not found.", "helpgent" )
                ], 404
            );
        }

        // if ( $guest->token_expires_at > date( helpgent_date_time_format(), time() ) ) {
        //     return Response::send(
        //         [
        //             'message' => esc_html__( "Token renewal is not possible as the existing token is still valid.", 'helpgent' )
        //         ], 422
        //     );
        // }

        $attempt_key           = "helpgent_user_token_renew_{$user->id}";
        $total_allowed_attempt = apply_filters( 'helpgent_user_login_total_allowed_attempt', 2 );
        $waiting_time          = apply_filters( 'helpgent_user_login_waiting_time', HOUR_IN_SECONDS );

        $attempt  = new AttemptValidator( $attempt_key, $total_allowed_attempt, $waiting_time );
        $validate = $attempt->validate();

        if ( $validate instanceof WP_Error ) {
            throw new Exception( esc_html( $validate->get_error_message() ), esc_attr( $validate->get_error_code() ) );
        }

        $user->token = $this->repository->update_token( $user->id );

        $mailer = new GuestTokenRenew( $this->settings_repository->get_email_template_settings(), $user );
        $mailer->send();

        helpgent_logout();

        return Response::send(
            [
                // translators: %s: email
                'message' => sprintf( esc_html__( "We've sent a login link to the email %s please check your inbox.", "helpgent" ), $user->email )
            ]
        );
    }

    public function delete( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $user = helpgent_get_current_user();

        if ( $user->is_administrator() || $user->get_registered_id() ) {
            throw new Exception( esc_html__( "Sorry, your account can't be deleted form here.", "helpgent" ), 500 );
        }

        // $this->repository->delete_request( $user->get_id() );

        return Response::send(
            [
                'message' => esc_html__( "The account delete request was sent.", "helpgent" ),
            ]
        );
    }
}
