<?php

namespace HelpGent\App\Http\Controllers;

defined( 'ABSPATH' ) || exit;

use Exception;
use WP_REST_Request;

use HelpGent\WpMVC\Routing\Response;
use HelpGent\WpMVC\RequestValidator\Validator;
use HelpGent\App\Http\Controllers\Controller;

use HelpGent\App\EnumeratedList\AttachmentTransferQueueStatus;
use HelpGent\App\EnumeratedList\AttachmentTransferQueueType;
use HelpGent\App\EnumeratedList\BackgroundProcessStatus;

use HelpGent\App\Models\Attachment as AttachmentModel;
use HelpGent\App\Models\AttachmentUploadSession as AttachmentUploadSessionModel;
use HelpGent\App\Models\AttachmentTransferQueue as AttachmentTransferQueueModel;

use HelpGent\App\Repositories\AttachmentTransferQueueRepository;
use HelpGent\App\MediaDrivers\Local as LocalDrive;

use HelpGent\App\MediaDriverMigration\Migrator;

use HelpGent\App\BackgroundServices\MediaMigrationService;
use HelpGent\App\BackgroundServices\MediaTransferService;
use HelpGent\App\BackgroundServices\MediaCleanUpService;

class AttachmentMigrationController extends Controller {
    public AttachmentTransferQueueRepository $transfer_queue_repository;

    public string $attachment_driver;

    public ?MediaMigrationService $media_migration_service;

    public ?MediaTransferService $media_transfer_service;

    public ?MediaCleanUpService $media_clean_up_service;

    public function __construct( AttachmentTransferQueueRepository $transfer_queue_repository ) {
        /**
         * @var ?MediaMigrationService $helpgent_media_migration_service
         */
        global $helpgent_media_migration_service;

        /**
         * @var ?MediaTransferService $helpgent_media_transfer_service
         */
        global $helpgent_media_transfer_service;

        /**
         * @var ?MediaCleanUpService $media_clean_up_service
         */
        global $helpgent_media_clean_up_service;
        
        $this->media_migration_service = $helpgent_media_migration_service;
        $this->media_transfer_service  = $helpgent_media_transfer_service;
        $this->media_clean_up_service  = $helpgent_media_clean_up_service;

        $this->transfer_queue_repository = $transfer_queue_repository;
        $this->attachment_driver         = get_option( 'helpgent_active_media_driver', LocalDrive::get_key() );
    }

    public function index( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate( 
            [ 
                'page'         => 'numeric',
                'per_page'     => 'numeric',
                'queue_type'   => 'accepted:upload,migration',
                'status'       => 'accepted:in_queue,transferring,failed,done',
                'with_summary' => 'accepted:1,0',
            ] 
        );

        $page     = $wp_rest_request->get_param( 'page' );
        $page     = empty( $page ) ? 1 : intval( $page );
        $per_page = $wp_rest_request->get_param( 'per_page' );
        $per_page = empty( $per_page ) ? 10 : intval( $per_page );

        $migration_route = $this->media_migration_service ? $this->media_migration_service->get_route() : '';
        $migration_from  = ! empty( $migration_route ) ? $migration_route['from'] : '';

        $queue_type   = $wp_rest_request->get_param( 'queue_type' );
        $status       = $wp_rest_request->get_param( 'status' );
        $with_summary = ! empty( $wp_rest_request->get_param( 'with_summary' ) );

        $queue_table   = AttachmentTransferQueueModel::get_table_name();
        $session_table = AttachmentUploadSessionModel::get_table_name();

        $queue_type_migration = AttachmentTransferQueueType::MIGRATION;
        $queue_type_upload    = AttachmentTransferQueueType::UPLOAD;

        $base_query = AttachmentModel::query( 'attachment' )
            ->select(
                [  
                    'attachment.id as attachment_id',
                    'queue.id as queue_id',
                    'queue.type',
                    'queue.transfer_from',
                    'queue.transfer_to',
                    'queue.transfer_next',
                    'queue.upload_id',
                    'queue.upload_token',
                    'queue.status',
                    'queue.created_at as queue_created_at',
                    'attachment.title', 
                    'attachment.mime_type',
                    'attachment.file_size',
                    'attachment.storage',
                    'session.last_sent_bytes',
                ] 
            )
            ->left_join( "{$queue_table} as queue", 'queue.attachment_id', 'attachment.id' )
            ->left_join( "{$session_table} as session", 'session.id', 'queue.upload_id' )
            ->order_by_raw( 'CASE WHEN queue.status = "transferring" THEN 1 WHEN queue.status = "in_queue" THEN 2 WHEN queue.status = "failed" THEN 3 WHEN queue.status = "done" THEN 4 WHEN queue.status IS null THEN 5 END ASC' );

        $queue_type_migration_sql = ! empty( $migration_from ) ? " OR ( attachment.storage = '{$migration_from}' AND queue.type IS null )" : '';
        
        if ( ! empty( $queue_type ) ) {
            if ( $queue_type_migration === $queue_type ) {
                $base_query->where_raw( "( queue.type = '{$queue_type}' {$queue_type_migration_sql} )" );
            } else {
                $base_query->where( 'queue.type', $queue_type );
            }
        } else {
            $base_query->where_raw( "( queue.type IN ( '{$queue_type_migration}', '{$queue_type_upload}' ) {$queue_type_migration_sql} )" );
        }
        
        if ( ! empty( $status ) ) {
            $sql = "queue.status = '{$status}'";

            if ( ( empty( $queue_type ) || $queue_type_migration === $queue_type ) && 
                AttachmentTransferQueueStatus::IN_QUEUE === $status 
            ) {
                $sql = "( queue.status = '{$status}' OR queue.status IS null )";
            }

            $base_query->where_raw( $sql );
        }

        $count_query = clone $base_query;
        $count       = $count_query->count();
        $results     = $base_query->pagination( $page, $per_page, $per_page );

        $data = [
            'total_items' => $count,
            'data'        => $results,
        ];

        if ( $with_summary ) {
            $data['summary'] = $this->get_migration_status( $wp_rest_request->get_param( 'queue_type' ) );
        }

        return Response::send( $data );
    }

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

        $queue_id   = $wp_rest_request->get_param( 'id' );
        $queue_data = $this->transfer_queue_repository->get_by_id( $queue_id );

        if ( ! $queue_data ) {
            throw new Exception( esc_html__( 'Item not found.', 'helpgent' ), 404 );
        }

        $delete = $this->transfer_queue_repository->delete( $queue_id );

        if ( 0 === $delete ) {
            throw new Exception( esc_html__( 'Oops, something went wrong. Please try again.', 'helpgent' ), 500 );
        }

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

    public function start_migration( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate( 
            [ 
                'migrate_from' => 'required|string',
                'migrate_to'   => 'required|string'
            ] 
        );

        if ( ! $this->media_migration_service ) {
            throw new Exception( esc_html__( 'There was an internal server error, please try again', 'helpgent' ), 500 );
        }

        // Run The Migration
        if ( $this->media_migration_service->background_process->is_active() ) {
            throw new Exception( esc_html__( 'A migration process is running already. Please cancel the previous migration before starting a new one.', 'helpgent' ), 422 );
        }

        if ( $this->media_migration_service->background_process->is_finished() ) {
            $this->media_migration_service->background_process->reset();
            $this->media_migration_service->after_complete_migration();

            try {
                $this->clear_migration_queue_items();
            } catch ( Exception $e ) {
            }
        }

        // Run The Migration
        $this->media_migration_service
            ->add_migration_task(
                [
                    'from' => $wp_rest_request->get_param( 'migrate_from' ),
                    'to'   => $wp_rest_request->get_param( 'migrate_to' ),
                ]
            )
            ->run();

        return Response::send( [ 'message' => esc_html__( 'The migration has started successfully.', 'helpgent' ) ] );
    }

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

        $migrator = $this->get_migrator();

        $migrator->move_to_queue( ( int ) $wp_rest_request->get_param( 'id' ) );
        
        try {
            $this->maybe_re_run_migration_service();
        } catch ( Exception $e ) {
        }

        try {
            $this->maybe_re_run_transfer_service();
        } catch ( Exception $e ) {
        }
    }

    public function retry_queue_items( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate( [ 'queue_type' => 'accepted:upload,migration' ] );

        $queue_type = $wp_rest_request->get_param( 'queue_type' );

        $this->transfer_queue_repository->move_failed_items_to_queue( $queue_type );
        
        if ( ( ! $queue_type || $queue_type === 'migration' ) ) {
            try {
                $this->maybe_re_run_migration_service();
            } catch ( Exception $e ) {
            }
        }

        if ( ( ! $queue_type || $queue_type === 'upload' ) ) {
            try {
                $this->maybe_re_run_transfer_service();
            } catch ( Exception $e ) {
            }
        }

        return Response::send( [ 'success' => true ] );
    }

    function maybe_re_run_migration_service(): void {
        if ( ! $this->media_migration_service ) {
            throw new Exception( esc_html__( 'There was an internal server error, please try again', 'helpgent' ), 500 );
        }

        $route = $this->media_migration_service->get_route();

        if ( empty( $route ) ) {
            throw new Exception( esc_html__( 'No active migration was found, please run the migration again.', 'helpgent' ), 403 );
        }

        try {
            $this->media_migration_service->add_migration_task( $route )->run();
        } catch ( Exception $e ) {
        }
    }
    
    function maybe_re_run_transfer_service(): void {
        if ( ! $this->media_transfer_service ) {
            throw new Exception( esc_html__( 'There was an internal server error, please try again', 'helpgent' ), 500 );
        }

        try {
            $this->media_transfer_service->run();
        } catch ( Exception $e ) {
        }
    }

    public function migration_status( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate( [ 'queue_type' => 'accepted:upload,migration' ] );

        return Response::send(
            $this->get_migration_status( $wp_rest_request->get_param( 'queue_type' ) )
        );
    }

    public function get_migration_status( ?string $queue_type = '' ) {
        if ( ! $this->media_migration_service ||  ! $this->media_transfer_service ) {
            throw new Exception( esc_html__( 'There was an internal server error, please try again', 'helpgent' ), 500 );
        }

        $status = [
            'migration_route'  => $this->media_migration_service->get_route(),
            'migration_status' => $this->media_migration_service->status(),
            'transfer_status'  => $this->media_transfer_service->status(),
        ];

        $counts = $this->get_attachment_queue_counts( $queue_type );
        $status = array_merge( $status, $counts );

        if ( BackgroundProcessStatus::DONE === $status['migration_status'] && 
            $status['remaining_migration_files'] > 0 
        ) {
            $status['migration_status'] = BackgroundProcessStatus::FAILED;
        }

        return $status;
    }

    /**
     * Summary of attachment queue counts
     * 
     * @param ?string $queue_type
     * @throws Exception
     * @return array
     */
    public function get_attachment_queue_counts( ?string $queue_type = '' ): array {
        if ( ! $this->media_migration_service ) {
            throw new Exception( esc_html__( 'There was an internal server error, please try again', 'helpgent' ), 500 );
        }

        $transfer_queue_query = AttachmentTransferQueueModel::query();

        $accepted_queue_types = ! empty( $queue_type ) 
            ? [ $queue_type ] 
            : [
                AttachmentTransferQueueType::MIGRATION,
                AttachmentTransferQueueType::UPLOAD,
            ];

        // Transferring attachments count
        $attachments_in_transferring_query = clone $transfer_queue_query;
        $attachments_transferring_count    = $attachments_in_transferring_query
            ->where( 'status', 'transferring' )
            ->where_in( 'type', $accepted_queue_types )
            ->count();

        // In queue attachments count
        $attachments_in_queue_query = clone $transfer_queue_query;
        $attachments_in_queue_count = $attachments_in_queue_query
            ->where( 'status', 'in_queue' )
            ->where_in( 'type', $accepted_queue_types )
            ->count();

        // Failed attachments count
        $attachments_failed_query = clone $transfer_queue_query;
        $attachments_failed_count = $attachments_failed_query
            ->where( 'status', 'failed' )
            ->where_in( 'type', $accepted_queue_types )
            ->count();
        
        // Done attachments count
        $attachments_done_query = clone $transfer_queue_query;
        $attachments_done_count = $attachments_done_query
            ->where( 'status', 'done' )
            ->where_in( 'type', $accepted_queue_types )
            ->count();

        // Remaining migration files count
        $remaining_migration_files = 0;

        if ( BackgroundProcessStatus::IDLE !== $this->media_migration_service->status() ) {
            $route = $this->media_migration_service->get_route();

            if ( ! empty( $route['from'] ) ) {
                $remaining_migration_files = AttachmentModel::query( 'attachment' )
                    ->select( 'id' )
                    ->where( 'attachment.storage', $route['from'] )
                    ->count();
            }
        }

        if ( AttachmentTransferQueueType::UPLOAD !== $queue_type ) {
            $attachments_in_queue_count = $attachments_in_queue_count + $remaining_migration_files;
        }

        return [
            'total_in_progress'         => $attachments_transferring_count,
            'total_in_queue'            => $attachments_in_queue_count,
            'total_failed'              => $attachments_failed_count,
            'total_done'                => $attachments_done_count,
            'remaining_migration_files' => $remaining_migration_files,
        ];
    }
    
    public function stop_migration() {
        if ( ! $this->media_migration_service ) {
            throw new Exception( esc_html__( 'There was an internal server error, please try again', 'helpgent' ), 500 );
        }

        if ( $this->media_migration_service->background_process->is_cancelled() ) {
            throw new Exception( esc_html__( 'The migration has been cancelled already', 'helpgent' ), 422 );
        }

        if ( ! $this->media_migration_service->background_process->is_active() ) {
            throw new Exception( esc_html__( 'The migration is not running', 'helpgent' ), 422 );
        }

        $this->media_migration_service->background_process->cancel();

        return Response::send( [ 'message' => esc_html__( 'The migration has been cancelled', 'helpgent' ) ] );
    }

    public function reset_migration() {
        if ( $this->media_migration_service->background_process->is_active() ) {
            throw new Exception( esc_html__( 'Migration must be stopped or cancelled before finish', 'helpgent' ), 422 );
        }

        $this->media_migration_service->background_process->reset();
        $this->media_migration_service->after_complete_migration();

        try {
            $this->clear_migration_queue_items();
        } catch ( Exception $e ) {
        }

        return Response::send( [ 'message' => esc_html__( 'The migration has been finished successfully', 'helpgent' ) ] );
    }

    public function clear_migration_queue_items(): void {
        if ( ! $this->media_clean_up_service ) {
            throw new Exception( esc_html__( 'There was an internal server error, please try again', 'helpgent' ), 500 );
        }

        $this->transfer_queue_repository->delete_queues_has_no_session_or_done(
            [ 
                AttachmentTransferQueueType::MIGRATION,
            ] 
        );
        
        $this->transfer_queue_repository->cancel_queues(
            [
                AttachmentTransferQueueType::MIGRATION
            ],
            [
                AttachmentTransferQueueStatus::IN_QUEUE,
                AttachmentTransferQueueStatus::TRANSFERRING,
            ]
        );

        try {
            $this->media_clean_up_service->run();
        } catch ( Exception $e ) {
        }
    }

    public function get_migrator(): Migrator {
        return helpgent_singleton( Migrator::class );
    }
}