<?php //phpcs:ignore Internal.Exception

namespace Vendtics\App\Repositories;

defined( "ABSPATH" ) || exit;

use Vendtics\App\Models\CampaignRecipient;
use Vendtics\WpMVC\Database\Query\Builder;
use Vendtics\App\Models\Campaign;
use Vendtics\WpMVC\Exceptions\Exception;
use Vendtics\App\DTO\Campaign\DTO;
use Vendtics\App\DTO\Campaign\Read;

class CampaignRepository extends Repository {
    use TagListEntityRepository;

    public function get_query_builder(): Builder {
        return Campaign::query( 'campaign' );
    }

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

    public function get( Read $dto ) {
        $query = $this->get_query_builder()->where_is_null( 'campaign.parent_id' );

        if ( $dto->is_initialized( 'type' ) && $dto->get_type() ) {
            $query->where( 'campaign.type', $dto->get_type() );
        }

        $count_query = clone $query;
        $query->select(
            [
                "campaign.id",
                "campaign.name",
                "CASE
    WHEN campaign.status = 'schedule' and campaign.schedule_time <= NOW() THEN 'in_queue'
    ELSE campaign.status
END AS status",
                "COALESCE(COUNT(CASE WHEN recipient.click > 0 THEN recipient.id END), 0) AS clicked_email",
                "COALESCE(COUNT(CASE WHEN recipient.open > 0 THEN recipient.id END), 0) AS opened_email",
                "COALESCE(COUNT(recipient.id), 0) AS recipients",
                "campaign.type",
                "campaign.created_by",
                "campaign.created_at",
                "campaign.updated_at"
            ] 
        )->left_join( CampaignRecipient::get_table_name() . ' as recipient', 'campaign.id', 'recipient.campaign_id' );

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

        return [
            'campaigns' =>  $query->group_by( ['campaign.id'] )->pagination( $dto->get_page(), $dto->get_per_page() ),
            'total'     => $count_query->distinct()->count( 'campaign.id' ),
        ];
    }

    protected function campaigns_orders_query( Builder $query, Read $dto ) {
        $order_columns =  [
            'id'            => 'campaign.id',
            'name'          => 'campaign.name',
            'type'          => 'campaign.type',
            'status'        => 'campaign.status',
            'clicked_email' => 'clicked_email',
            'opened_email'  => 'opened_email',
            'recipients'    => 'recipients'
        ];
          
        if ( $dto->get_orders() ) {
            foreach ( $dto->get_orders() as $order ) {
                if ( empty( $order['order'] ) || empty( $order['order_by'] ) ) {
                    continue;
                }

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

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

    /**
     * @var DTO $dto
     */
    public function create( \Vendtics\WpMVC\DTO\DTO $dto ) {
        /**
         * @var DTO $dto
         */
        $campaign_id = parent::create( $dto );

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

        if ( $dto->is_initialized( 'tags' ) ) {
            $this->assign_tags( $campaign_id, $dto->get_tags() );
        }

        if ( $dto->is_initialized( 'tags' ) ) {
            $this->assign_lists( $campaign_id, $dto->get_lists() );
        }

        return $campaign_id;
    }

    /**
     * Summary of create_multiple
     * @param DTO[] $items
     * @return bool|int
     */
    public function create_multiple( array $items ) {
        return $this->get_query_builder()->insert( array_map( fn( DTO $dto ) => $dto->to_array(), $items ) );
    }

    /**
     * @var DTO $dto
     */
    public function update( \Vendtics\WpMVC\DTO\DTO $dto ) {
        /**
         * @var DTO $dto
         */
        $campaign = $this->get_by_id( $dto->get_id() );

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

        if ( $dto->is_initialized( 'tags' ) ) {
            $this->update_tags( $dto->get_id(), $dto->get_tags() );
        }

        if ( $dto->is_initialized( 'lists' ) ) {
            $this->update_lists( $dto->get_id(), $dto->get_lists() );
        }

        return parent::update( $dto );
    }

    /**
     * @var DTO $dto
     */
    public function update_children( int $parent_id, DTO $dto ) {
        /**
         * @var DTO $dto
         */
        $campaign = $this->get_query_builder()->where( 'id', $dto->get_id() )->where( 'parent_id', $parent_id )->first();

        if ( ! $campaign ) {
            // Translators: %d is the ID of the child campaign.
            throw (new Exception( sprintf( esc_html__( 'The children campaign(id: %d) not found.', 'vendtics' ), $dto->get_id() ), 404 ));
        }

        if ( $dto->is_initialized( 'tags' ) ) {
            $this->update_tags( $dto->get_id(), $dto->get_tags() );
        }

        if ( $dto->is_initialized( 'lists' ) ) {
            $this->update_lists( $dto->get_id(), $dto->get_lists() );
        }

        return parent::update( $dto );
    }

    public function get_single( int $id ) {
        $campaign = $this->get_query_builder()->with(
            'children', function( $query ) {
                // $query->select( 'id', 'parent_id', 'name', 'subject', 'body', 'pre_header', 'utm_parameters' );
            }
        )->with( ['tags', 'lists'] )->where( 'id', $id )->first();

        if ( $campaign ) {
            if ( 'schedule' === $campaign->status && current_time( 'mysql' ) > $campaign->schedule_time ) {
                $campaign->status = 'in_queue';
            }
        }

        return $campaign;
    }

    public function mark_as_completed( int $campaign_id ) {
        return $this->get_query_builder()->where( 'id', $campaign_id )->update(
            [
                'status'       => 'completed',
                'completed_at' => current_time( 'mysql' )
            ]
        );
    }

    public function mark_as_draft( int $campaign_id ) {
        return $this->get_query_builder()->where( 'id', $campaign_id )->update(
            [
                'status'       => 'draft',
                'completed_at' => current_time( 'mysql' )
            ]
        );
    }

    public function mark_as_processing( int $campaign_id ) {
        return $this->get_query_builder()->where( 'id', $campaign_id )->update(
            [
                'status'     => 'processing',
                'started_at' => current_time( 'mysql' )
            ]
        );
    }

    public function mark_as_schedule( int $campaign_id, string $schedule_time ) {
        return $this->get_query_builder()->where( 'id', $campaign_id )->update(
            [
                'status'        => 'schedule',
                'schedule_time' => $schedule_time
            ]
        );
    }

    public function update_name( int $campaign_id, string $name ) {
        return $this->update_column( $campaign_id, "name", $name );
    }

    public function mark_as_paused( int $campaign_id, array $pause_reason = [] ) {
        $data = [
            'status'    => 'paused',
            'paused_at' => current_time( 'mysql' )
        ];

        if ( ! empty( $pause_reason ) ) {
            $data['pause_reason'] = maybe_serialize( $pause_reason );
        }
        return $this->get_query_builder()->where( 'id', $campaign_id )->update( $data );
    }

    public function mark_as_cancel( int $campaign_id, string $cancel_reason ) {
        return $this->get_query_builder()->where( 'id', $campaign_id )->update(
            [
                'status'        => 'cancel',
                'canceled_at'   => current_time( 'mysql' ),
                'cancel_reason' => $cancel_reason
            ]
        );
    }

    public function scheduled_first( $columns = ['id', 'subject', 'body', 'pre_header', 'status', 'tracking_open_read', 'tracking_link_click', 'utm_parameters', 'type', 'recipient_type', 'segment_id', 'advanced_filters'] ) {
        $campaign = $this->campaign_query()->select( $columns )->first();

        if ( $campaign ) {
            $this->mark_as_processing( $campaign->id );
        }

        return $campaign;
    }

    protected function campaign_query() {
        return $this->get_query_builder()->where_null( 'parent_id' )->where( 'status', 'schedule' )->where( 'schedule_time', '<=', current_time( 'mysql' ) );
    }
}