<?php //phpcs:ignore Internal.Exception

namespace Vendtics\App\Repositories;

defined( "ABSPATH" ) || exit;

use Vendtics\WpMVC\Database\Query\Builder;
use Vendtics\App\Models\Contact;
use Vendtics\App\DTO\Contact\ContactReadDTO;
use Vendtics\WpMVC\Exceptions\Exception;
use Vendtics\App\DTO\Contact\ContactDTO;
use Vendtics\WpMVC\DTO\DTO;
use Vendtics\App\Models\EntityList;
use Vendtics\App\Models\EntityTag;

class ContactRepository extends Repository {
    use AdvancedFilter, TagListEntityRepository;

    public function get_query_builder(): Builder {
        return Contact::query( "contact" );
    }

    public function get_entity_type() {
        return 'contacts';
    }

    public function get( ContactReadDTO $dto ) {
        $query = $this->get_query_builder()->select( "contact.*" )->with( ['tags', 'lists'] );

        if ( $dto->get_queries() ) {
            $this->advanced_filter_query( $query, $dto->get_queries() );
        }

        $count_query = clone $query;

        do_action( 'vendtics_contacts_query', $query );

        $this->contacts_orders_query( $query, $dto );

        $contacts = array_map(
            function( $contact ) {
                $contact->avatar_url = get_avatar_url( $contact->email );
                return $contact;
            }, $query->group_by( ['contact.id', 'contact.email'] )->pagination( $dto->get_page(), $dto->get_per_page() )
        );

        return [
            "contacts" => apply_filters( 'vendtics_contacts', $contacts ),
            'total'    => $count_query->distinct()->count( 'contact.id' ),
        ];
    }    

    protected function contacts_orders_query( Builder $query, ContactReadDTO $dto ) {
        $order_columns =  [
            'id',
            'first_name',
            'last_name',
            'email',
            'phone_number',
            'company',
            'designation',
            'date_of_birth',
            'timezone',
            'address',
            'city',
            'state',
            'postal_code',
            'country',
            'status',
            'updated_at',
        ];
          
        if ( $dto->get_orders() ) {
            foreach ( $dto->get_orders() as $order ) {
                if ( empty( $order['order'] ) || empty( $order['order_by'] ) ) {
                    continue;
                }

                if ( ! in_array( $order['order_by'], $order_columns, true ) || ! in_array( $order['order'], ['asc', 'desc'], true ) ) {
                    continue;
                }

                $query->order_by( "contact.{$order['order_by']}", $order['order'] );
            }
        } else {
            $query->order_by( "contact.id", 'desc' );
        }
    }

    /**
     * @var ContactDTO $dto
     */
    public function create( DTO $dto ) {
        /**
         * @var ContactDTO $dto
         */
        $contact = $this->get_by( "email", $dto->get_email() );

        $dto->set_exclude_to_array( ['id', 'tags', 'lists'] );

        $this->throw_email_already_exists( $contact );

        $contact_id = parent::create( $dto );

        if ( ! $contact_id ) {
            throw new Exception( esc_html__( "Something was wrong.", 'vendtics' ), 505 );
        }

        $this->assign_tags( $contact_id, $dto->get_tags() );

        $this->assign_lists( $contact_id, $dto->get_lists() );

        return $contact_id;
    }

     /**
     * @var ContactDTO $dto
     */
    public function update( DTO $dto ) {
        /**
         * @var ContactDTO $dto
         */
        $contact = $this->get_by_id( $dto->get_id() );

        if ( ! $contact ) {
            throw (new Exception( esc_html__( 'The contact not found.', 'vendtics' ), 404 ));
        }

        if ( $contact->email !== $dto->get_email() ) {
            $email_contact = $this->get_by( "email", $dto->get_email() );

            $this->throw_email_already_exists( $email_contact );
        }

        $this->update_tags( $dto->get_id(), $dto->get_tags() );

        $this->update_lists( $dto->get_id(), $dto->get_lists() );

        $dto->set_exclude_to_array( ['id', 'tags', 'lists'] );

        return parent::update( $dto );
    }

    public function get_single( int $id ) {
        return $this->get_query_builder()->where( 'contact.id', $id )->with( ['tags', 'lists'] )->first();
    }

    public function get_by_email( string $email ) {
        return $this->get_by( 'email', $email );
    }

    private function throw_email_already_exists( $contact ) {
        if ( $contact ) {
            throw (new Exception( '', 409 ))->set_messages(
                [
                    "email" => [
                        esc_html__( "The email already exists.", 'vendtics' )
                    ]
                ]
            );
        }
    }

    public function count() {
        return Contact::query()->count();
    }
    
    public function total_subscriber() {
        return Contact::query()->where( 'status', 'subscribe' )->count();
    }

    public function total_unsubscriber() {
        return Contact::query()->where( 'status', 'unsubscribe' )->count();
    }

    public function total_pending() {
        return Contact::query()->where( 'status', 'pending' )->count();
    }

    public function bulk_update( array $ids, ContactDTO $dto ) {
        return Contact::query()->where_in( 'id', $ids )->update( $dto->to_array() );
    }

    public function update_status( int $contact_id, string $status ) {
        return $this->update_column( $contact_id, 'status', $status );
    }

    public function update_type( int $contact_id, string $type ) {
        return $this->update_column( $contact_id, 'type', $type );
    }

    public function count_by_tags_lists( array $tags = [], array $lists = [] ) {
        $query = $this->get_query_builder()->where( 'contact.status', 'subscribe' );

        if ( ! empty( $tags ) ) {
            $query->join(
                EntityTag::get_table_name() . ' as entity_tag', function( $join ) use( $tags ) {
                    $join->on_column( 'entity_tag.entity_id', 'contact.id' )->on_in( 'entity_tag.tag_id', $tags )->on( "entity_tag.entity_type", "contacts" );
                } 
            );
        }

        if ( ! empty( $lists ) ) {
            $query->join(
                EntityList::get_table_name() . ' as entity_list', function( $join ) use( $lists ){
                    $join->on_column( 'entity_list.entity_id', 'contact.id' )->on_in( 'entity_list.list_id', $lists )->on( "entity_list.entity_type", "contacts" );
                } 
            );
        }

        return $query->distinct()->count( 'contact.id' );
    }

    public function count_by_segment_id( int $segment_id ) {
        $segment_repository = vendtics_segment_repository();
        $segment            = $segment_repository->get_by_id( $segment_id );
    
        if ( ! $segment ) {
            throw new Exception( esc_html__( "Segment not found", "vendtics" ) );
        }

        if ( empty( $segment->queries ) ) {
            throw new Exception( esc_html__( "Segment queries is empty", "vendtics" ) );
        }

        return $this->count_by_advanced_filter( json_decode( $segment->queries, true ) );
    }

    public function count_by_advanced_filter( array $queries ) {
        return $this->advanced_filter_query( $this->get_query_builder(), $queries )->distinct()->count( 'contact.id' );
    }
}