<?php

namespace HelpGent\App\Services;

defined( 'ABSPATH' ) || exit;

use HelpGent\App\DTO\FormDTO;
use WP_REST_Request;

class BuilderMediaOptimizationService {
    public const CRON_HOOK = 'helpgent_optimize_builder_media_attachment';

    protected const META_STATUS                  = '_helpgent_builder_media_optimization_status';
    protected const META_SOURCE_SIGNATURE        = '_helpgent_builder_media_source_signature';
    protected const META_OPTIMIZED_ATTACHMENT_ID = '_helpgent_builder_media_optimized_attachment_id';
    protected const META_POSTER_ATTACHMENT_ID    = '_helpgent_builder_media_poster_attachment_id';
    protected const META_ERROR                   = '_helpgent_builder_media_optimization_error';
    protected const META_UPDATED_AT              = '_helpgent_builder_media_optimization_updated_at';
    protected const GENERATED_SOURCE_ID          = '_helpgent_builder_media_source_attachment_id';
    protected const GENERATED_TYPE               = '_helpgent_builder_media_generated_type';

    protected const STATUS_PENDING            = 'pending';
    protected const STATUS_PROCESSING         = 'processing';
    protected const STATUS_DONE               = 'done';
    protected const STATUS_FAILED             = 'failed';
    protected const STATUS_UNSUPPORTED        = 'unsupported';
    protected const STATUS_TOO_LARGE          = 'too_large';
    protected const STATUS_FFMPEG_UNAVAILABLE = 'ffmpeg_unavailable';

    public function enqueue_from_form_dto( FormDTO $form_dto ): void {
        $this->enqueue_from_content( $form_dto->get_content() );
    }

    public function enqueue_from_auto_save_request( WP_REST_Request $request ): void {
        $this->enqueue_from_content( (string) $request->get_param( 'draft' ) );
    }

    public function enqueue_from_content( $content ): void {
        $content = $this->normalize_form_content( $content );

        if ( empty( $content ) ) {
            return;
        }

        foreach ( $this->get_video_attachment_ids( $content ) as $attachment_id ) {
            $this->enqueue_attachment( $attachment_id );
        }
    }

    public function enrich_form( $form ) {
        if ( ! is_object( $form ) ) {
            return $form;
        }

        if ( isset( $form->content ) ) {
            $form->content = $this->enrich_content_value( $form->content );
        }

        if ( ! empty( $form->draft ) ) {
            $form->draft = $this->enrich_draft_value( $form->draft );
        }

        return $form;
    }

    public function optimize_attachment( int $attachment_id ): void {
        if ( $attachment_id <= 0 ) {
            return;
        }

        $source_path = get_attached_file( $attachment_id );

        if ( ! $source_path || ! is_file( $source_path ) ) {
            $this->mark_attachment( $attachment_id, self::STATUS_FAILED, 'source_missing' );
            return;
        }

        $source_signature = $this->get_source_signature( $source_path );

        if ( get_post_mime_type( $attachment_id ) !== 'video/mp4' ) {
            $this->mark_attachment( $attachment_id, self::STATUS_UNSUPPORTED, '', $source_signature );
            return;
        }

        $max_file_size = (int) apply_filters(
            'helpgent_builder_media_optimization_max_file_size',
            200 * MB_IN_BYTES
        );

        if ( filesize( $source_path ) > $max_file_size ) {
            $this->mark_attachment( $attachment_id, self::STATUS_TOO_LARGE, '', $source_signature );
            return;
        }

        if ( $this->is_optimized_for_signature( $attachment_id, $source_signature ) ) {
            return;
        }

        $ffmpeg = $this->get_ffmpeg_binary();

        if ( ! $ffmpeg ) {
            $this->mark_attachment( $attachment_id, self::STATUS_FFMPEG_UNAVAILABLE, '', $source_signature );
            return;
        }

        $this->mark_attachment( $attachment_id, self::STATUS_PROCESSING, '' );

        $paths = $this->get_output_paths( $attachment_id, $source_path, $source_signature );

        if ( ! wp_mkdir_p( $paths['dir'] ) ) {
            $this->mark_attachment( $attachment_id, self::STATUS_FAILED, 'output_dir_failed' );
            return;
        }

        $video_created = $this->run_ffmpeg(
            $ffmpeg,
            [
                '-y',
                '-hide_banner',
                '-loglevel',
                'error',
                '-i',
                $source_path,
                '-map',
                '0',
                '-c',
                'copy',
                '-movflags',
                '+faststart',
                $paths['video'],
            ]
        );

        if ( ! $video_created || ! is_file( $paths['video'] ) ) {
            $this->mark_attachment( $attachment_id, self::STATUS_FAILED, 'faststart_failed' );
            return;
        }

        $poster_created = $this->run_ffmpeg(
            $ffmpeg,
            [
                '-y',
                '-hide_banner',
                '-loglevel',
                'error',
                '-ss',
                '00:00:00.5',
                '-i',
                $source_path,
                '-frames:v',
                '1',
                '-q:v',
                '3',
                $paths['poster'],
            ]
        );

        $video_attachment_id = $this->get_or_create_generated_attachment(
            $paths['video'],
            'video/mp4',
            $attachment_id,
            'optimized-video'
        );

        if ( ! $video_attachment_id ) {
            $this->mark_attachment( $attachment_id, self::STATUS_FAILED, 'attachment_create_failed', $source_signature );
            return;
        }

        update_post_meta( $attachment_id, self::META_OPTIMIZED_ATTACHMENT_ID, $video_attachment_id );

        if ( $poster_created && is_file( $paths['poster'] ) ) {
            $poster_attachment_id = $this->get_or_create_generated_attachment(
                $paths['poster'],
                'image/jpeg',
                $attachment_id,
                'poster'
            );

            update_post_meta( $attachment_id, self::META_POSTER_ATTACHMENT_ID, $poster_attachment_id );
        }

        $this->mark_attachment( $attachment_id, self::STATUS_DONE, '', $source_signature );
    }

    protected function enqueue_attachment( int $attachment_id ): void {
        if ( $attachment_id <= 0 ) {
            return;
        }

        $source_path = get_attached_file( $attachment_id );

        if ( ! $source_path || ! is_file( $source_path ) ) {
            return;
        }

        $source_signature = $this->get_source_signature( $source_path );

        if ( get_post_mime_type( $attachment_id ) !== 'video/mp4' ) {
            $this->mark_attachment( $attachment_id, self::STATUS_UNSUPPORTED, '', $source_signature );
            return;
        }

        if ( $this->is_optimized_for_signature( $attachment_id, $source_signature ) ) {
            return;
        }

        $status           = (string) get_post_meta( $attachment_id, self::META_STATUS, true );
        $stored_signature = (string) get_post_meta( $attachment_id, self::META_SOURCE_SIGNATURE, true );

        if ( in_array( $status, [ self::STATUS_PENDING, self::STATUS_PROCESSING ], true ) ) {
            return;
        }

        if ( in_array(
            $status,
            [
                self::STATUS_FAILED,
                self::STATUS_UNSUPPORTED,
                self::STATUS_TOO_LARGE,
                self::STATUS_FFMPEG_UNAVAILABLE,
            ],
            true
        ) &&
            $stored_signature === $source_signature
        ) {
            return;
        }

        update_post_meta( $attachment_id, self::META_STATUS, self::STATUS_PENDING );
        update_post_meta( $attachment_id, self::META_SOURCE_SIGNATURE, $source_signature );
        update_post_meta( $attachment_id, self::META_UPDATED_AT, time() );

        if ( ! wp_next_scheduled( self::CRON_HOOK, [ $attachment_id ] ) ) {
            wp_schedule_single_event( time() + 10, self::CRON_HOOK, [ $attachment_id ] );
        }
    }

    protected function enrich_content_value( $content ) {
        $raw_content = $content;
        $is_json     = is_string( $content );
        $content     = $this->normalize_form_content( $content );

        if ( empty( $content ) ) {
            return $is_json ? $raw_content : [];
        }

        $content = $this->enrich_content_array( $content );

        return $is_json ? wp_json_encode( $content ) : $content;
    }

    protected function enrich_draft_value( $draft ) {
        $raw_draft = $draft;
        $is_json   = is_string( $draft );
        $draft     = $this->decode_json_array( $draft );

        if ( empty( $draft ) ) {
            return $is_json ? $raw_draft : [];
        }

        if ( isset( $draft['content'] ) ) {
            $draft['content'] = $this->enrich_content_value( $draft['content'] );
        }

        return $is_json ? wp_json_encode( $draft ) : $draft;
    }

    protected function enrich_content_array( array $content ): array {
        if ( empty( $content['elements'] ) || ! is_array( $content['elements'] ) ) {
            return $content;
        }

        foreach ( $content['elements'] as &$element ) {
            if ( empty( $element['attributes']['media'] ) || ! is_array( $element['attributes']['media'] ) ) {
                continue;
            }

            $media = &$element['attributes']['media'];

            if ( ! $this->is_video_media( $media ) ) {
                continue;
            }

            $attachment_id = $this->resolve_attachment_id( $media );

            if ( ! $attachment_id ) {
                continue;
            }

            $assets = $this->get_optimized_asset_urls( $attachment_id );

            if ( ! empty( $assets['video_url'] ) ) {
                $media['helpgent_optimized_url'] = $assets['video_url'];
            }

            if ( ! empty( $assets['poster_url'] ) ) {
                $media['helpgent_poster_url'] = $assets['poster_url'];
                $media['poster']              = $assets['poster_url'];
            }

            if ( ! empty( $assets['status'] ) ) {
                $media['helpgent_optimization_status'] = $assets['status'];
            }
        }
        unset( $element, $media );

        return $content;
    }

    protected function get_optimized_asset_urls( int $attachment_id ): array {
        $status                  = (string) get_post_meta( $attachment_id, self::META_STATUS, true );
        $optimized_attachment_id = absint( get_post_meta( $attachment_id, self::META_OPTIMIZED_ATTACHMENT_ID, true ) );
        $poster_attachment_id    = absint( get_post_meta( $attachment_id, self::META_POSTER_ATTACHMENT_ID, true ) );
        $video_url               = $optimized_attachment_id ? wp_get_attachment_url( $optimized_attachment_id ) : '';
        $poster_url              = $poster_attachment_id ? wp_get_attachment_url( $poster_attachment_id ) : '';

        return [
            'status'     => $status,
            'video_url'  => is_string( $video_url ) ? $video_url : '',
            'poster_url' => is_string( $poster_url ) ? $poster_url : '',
        ];
    }

    protected function normalize_form_content( $content ): array {
        $content = $this->decode_json_array( $content );

        if ( isset( $content['content'] ) ) {
            return $this->normalize_form_content( $content['content'] );
        }

        return $content;
    }

    protected function decode_json_array( $value ): array {
        if ( is_array( $value ) ) {
            return $value;
        }

        if ( is_object( $value ) ) {
            return json_decode( wp_json_encode( $value ), true ) ?: [];
        }

        if ( ! is_string( $value ) || '' === trim( $value ) ) {
            return [];
        }

        $decoded = json_decode( $value, true );

        return is_array( $decoded ) ? $decoded : [];
    }

    protected function get_video_attachment_ids( array $content ): array {
        if ( empty( $content['elements'] ) || ! is_array( $content['elements'] ) ) {
            return [];
        }

        $attachment_ids = [];

        foreach ( $content['elements'] as $element ) {
            $media = $element['attributes']['media'] ?? null;

            if ( ! is_array( $media ) || ! $this->is_video_media( $media ) ) {
                continue;
            }

            $attachment_id = $this->resolve_attachment_id( $media );

            if ( $attachment_id ) {
                $attachment_ids[] = $attachment_id;
            }
        }

        return array_values( array_unique( $attachment_ids ) );
    }

    protected function resolve_attachment_id( array $media ): int {
        $attachment_id = absint( $media['id'] ?? $media['ID'] ?? 0 );

        if ( $attachment_id ) {
            return $attachment_id;
        }

        $url = (string) ( $media['source_url'] ?? $media['url'] ?? '' );

        if ( ! $url ) {
            return 0;
        }

        return absint( attachment_url_to_postid( $url ) );
    }

    protected function is_video_media( array $media ): bool {
        if ( ( $media['type'] ?? '' ) === 'video' ) {
            return true;
        }

        $mime_type = (string) ( $media['mime'] ?? $media['mime_type'] ?? '' );

        return 0 === strpos( $mime_type, 'video/' );
    }

    protected function is_optimized_for_signature( int $attachment_id, string $source_signature ): bool {
        $status           = (string) get_post_meta( $attachment_id, self::META_STATUS, true );
        $stored_signature = (string) get_post_meta( $attachment_id, self::META_SOURCE_SIGNATURE, true );

        if ( self::STATUS_DONE !== $status || $stored_signature !== $source_signature ) {
            return false;
        }

        $optimized_attachment_id = absint( get_post_meta( $attachment_id, self::META_OPTIMIZED_ATTACHMENT_ID, true ) );

        if ( ! $optimized_attachment_id ) {
            return false;
        }

        $optimized_path = get_attached_file( $optimized_attachment_id );

        return $optimized_path && is_file( $optimized_path );
    }

    protected function get_source_signature( string $source_path ): string {
        return md5( $source_path . '|' . filesize( $source_path ) . '|' . filemtime( $source_path ) );
    }

    protected function get_output_paths( int $attachment_id, string $source_path, string $source_signature ): array {
        $upload_dir = wp_upload_dir();
        $dir        = trailingslashit( $upload_dir['basedir'] ) . 'helpgent-builder-media';
        $name       = sanitize_file_name( pathinfo( $source_path, PATHINFO_FILENAME ) );
        $suffix     = substr( $source_signature, 0, 10 );
        $base       = "{$name}-{$attachment_id}-{$suffix}";

        return [
            'dir'    => $dir,
            'video'  => trailingslashit( $dir ) . "{$base}-faststart.mp4",
            'poster' => trailingslashit( $dir ) . "{$base}-poster.jpg",
        ];
    }

    protected function get_ffmpeg_binary(): string {
        if ( ! function_exists( 'exec' ) ) {
            return '';
        }

        $binary = (string) apply_filters( 'helpgent_ffmpeg_binary', 'ffmpeg' );

        if ( '' === $binary ) {
            return '';
        }

        $output    = [];
        $exit_code = 1;
        exec( escapeshellarg( $binary ) . ' -version 2>&1', $output, $exit_code );

        return 0 === $exit_code ? $binary : '';
    }

    protected function run_ffmpeg( string $ffmpeg, array $arguments ): bool {
        if ( ! function_exists( 'exec' ) ) {
            return false;
        }

        $command = escapeshellarg( $ffmpeg );

        foreach ( $arguments as $argument ) {
            $command .= ' ' . escapeshellarg( (string) $argument );
        }

        $output    = [];
        $exit_code = 1;
        exec( $command . ' 2>&1', $output, $exit_code );

        return 0 === $exit_code;
    }

    protected function get_or_create_generated_attachment( string $file_path, string $mime_type, int $source_attachment_id, string $type ): int {
        $existing_id = $this->find_generated_attachment_id( $file_path, $source_attachment_id, $type );

        if ( $existing_id ) {
            return $existing_id;
        }

        helpgent_require_media_uploader_files();

        $attachment_id = wp_insert_attachment(
            [
                'post_mime_type' => $mime_type,
                'post_title'     => sanitize_file_name( pathinfo( $file_path, PATHINFO_FILENAME ) ),
                'post_content'   => '',
                'post_status'    => 'inherit',
                'post_parent'    => $source_attachment_id,
            ],
            $file_path
        );

        if ( is_wp_error( $attachment_id ) || ! $attachment_id ) {
            return 0;
        }

        update_post_meta( $attachment_id, self::GENERATED_SOURCE_ID, $source_attachment_id );
        update_post_meta( $attachment_id, self::GENERATED_TYPE, $type );

        $metadata = wp_generate_attachment_metadata( $attachment_id, $file_path );

        if ( ! empty( $metadata ) ) {
            wp_update_attachment_metadata( $attachment_id, $metadata );
        }

        return absint( $attachment_id );
    }

    protected function find_generated_attachment_id( string $file_path, int $source_attachment_id, string $type ): int {
        $file_name = basename( $file_path );
        $query     = new \WP_Query(
            [
                'post_type'      => 'attachment',
                'post_status'    => 'inherit',
                'posts_per_page' => 1,
                'fields'         => 'ids',
                'meta_query'     => [
                    [
                        'key'   => self::GENERATED_SOURCE_ID,
                        'value' => $source_attachment_id,
                    ],
                    [
                        'key'   => self::GENERATED_TYPE,
                        'value' => $type,
                    ],
                ],
                's'              => pathinfo( $file_name, PATHINFO_FILENAME ),
            ]
        );

        foreach ( $query->posts as $attachment_id ) {
            if ( get_attached_file( $attachment_id ) === $file_path ) {
                return absint( $attachment_id );
            }
        }

        return 0;
    }

    protected function mark_attachment( int $attachment_id, string $status, string $error = '', string $source_signature = '' ): void {
        update_post_meta( $attachment_id, self::META_STATUS, $status );
        update_post_meta( $attachment_id, self::META_UPDATED_AT, time() );

        if ( '' !== $source_signature ) {
            update_post_meta( $attachment_id, self::META_SOURCE_SIGNATURE, $source_signature );
        }

        if ( '' === $error ) {
            delete_post_meta( $attachment_id, self::META_ERROR );
            return;
        }

        update_post_meta( $attachment_id, self::META_ERROR, $error );
    }
}
