<?php

namespace Fotogenic_AI;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

final class Media_Saver {
    private static ?Media_Saver $instance = null;
    private const ALLOWED_IMAGE_MIME_TYPES = [
        'image/jpeg' => 'jpg',
        'image/png'  => 'png',
        'image/webp' => 'webp',
    ];

    private function __construct() {}

    public static function instance(): Media_Saver {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Build a browser-facing attachment URL with a stable cache-busting revision token.
     * Prevents stale browser reuse when a new attachment is created with the same filename/path.
     *
     * @param int    $attachment_id Attachment ID.
     * @param string $size          Image size slug. Use 'full' for the original file URL.
     * @return string
     */
    public static function get_attachment_view_url( int $attachment_id, string $size = 'full' ): string {
        $attachment_id = absint( $attachment_id );
        if ( $attachment_id <= 0 ) {
            return '';
        }

        if ( 'full' === $size ) {
            $url = wp_get_attachment_url( $attachment_id );
        } else {
            $url = wp_get_attachment_image_url( $attachment_id, $size );
            if ( ! $url ) {
                $url = wp_get_attachment_url( $attachment_id );
            }
        }

        if ( ! $url ) {
            return '';
        }

        return add_query_arg( 'fa_rev', self::get_attachment_revision_token( $attachment_id ), (string) $url );
    }

    /**
     * Revision token for browser cache busting.
     *
     * @param int $attachment_id Attachment ID.
     * @return string
     */
    private static function get_attachment_revision_token( int $attachment_id ): string {
        $attachment_id = absint( $attachment_id );
        if ( $attachment_id <= 0 ) {
            return '';
        }

        $modified = (int) get_post_modified_time( 'U', true, $attachment_id );
        return $modified > 0
            ? $attachment_id . '-' . $modified
            : (string) $attachment_id;
    }

    /**
     * Apply attachment metadata (alt, title, caption, description) consistently.
     * Use after creating an attachment or when refreshing meta (e.g. on Step3 save to product).
     *
     * @param int   $attachment_id Attachment post ID.
     * @param array $copy         Copy to apply: [ 'alt' => '', 'caption' => '', 'description' => '', 'title' => '' ].
     * @return void
     */
    public static function apply_generated_attachment_meta( int $attachment_id, array $copy ): void {
        if ( $attachment_id <= 0 || ! get_post( $attachment_id ) ) {
            return;
        }
        $alt = isset( $copy['alt'] ) ? (string) $copy['alt'] : '';
        update_post_meta( $attachment_id, '_wp_attachment_image_alt', $alt );

        $post_data = [ 'ID' => $attachment_id ];
        if ( array_key_exists( 'title', $copy ) ) {
            $post_data['post_title'] = (string) $copy['title'];
        }
        if ( array_key_exists( 'caption', $copy ) ) {
            $post_data['post_excerpt'] = (string) $copy['caption'];
        }
        if ( array_key_exists( 'description', $copy ) ) {
            $post_data['post_content'] = (string) $copy['description'];
        }
        if ( count( $post_data ) > 1 ) {
            wp_update_post( $post_data );
        }
    }

    /**
     * Build copy array for attachment meta from run/context.
     * For media/post/page targets this reuses source attachment meta with safe fallbacks.
     *
     * @param array $context Context with run_target/subject_id or product context.
     * @return array{ title: string, caption: string, description: string, alt: string }
     */
    public function build_copy_for_context( array $context ): array {
        if ( $this->is_source_metadata_target( $context ) ) {
            $fallback = $this->get_generated_default_copy_text();
            $source_attachment_id = $this->resolve_source_attachment_id( $context );
            $source_copy = $this->get_source_attachment_copy( $source_attachment_id );

            return [
                'title'       => ( '' !== $source_copy['title'] ) ? $source_copy['title'] : $fallback,
                'caption'     => ( '' !== $source_copy['caption'] ) ? $source_copy['caption'] : $fallback,
                'description' => ( '' !== $source_copy['description'] ) ? $source_copy['description'] : $fallback,
                'alt'         => ( '' !== $source_copy['alt'] ) ? $source_copy['alt'] : $fallback,
            ];
        }

        return [
            'title'       => $this->build_attachment_title( $context ),
            'caption'     => $this->build_attachment_caption( $context ),
            'description' => $this->build_attachment_description( $context ),
            'alt'         => $this->build_alt_text( $context ),
        ];
    }

    /** @return array|\WP_Error */
    public function save_generated_image_from_url( string $image_url, array $context ) {
        $image_url = trim( $image_url );
        if ( ! $this->is_allowed_generated_image_url( $image_url ) ) {
            return new \WP_Error( 'fotogenic_ai_invalid_image_url', __( 'Invalid generated image URL received.', 'fotogenic-ai' ), [ 'status' => 400 ] );
        }

        require_once ABSPATH . 'wp-admin/includes/file.php';
        $downloaded = download_url( $image_url, 60 );
        if ( is_wp_error( $downloaded ) ) {
            if ( Logger::instance()->enabled() ) {
                Logger::instance()->error( 'media_download_failed', [
                    'error' => $downloaded->get_error_message(),
                ] );
            }
            return new \WP_Error( 'fotogenic_ai_download_failed', __( 'Failed to download generated image.', 'fotogenic-ai' ), [ 'status' => 500 ] );
        }

        $image_info = wp_getimagesize( $downloaded );
        $mime       = is_array( $image_info ) && ! empty( $image_info['mime'] ) ? (string) $image_info['mime'] : '';
        $ext        = $this->get_extension_for_mime( $mime );
        if ( '' === $ext ) {
            wp_delete_file( $downloaded );
            return new \WP_Error( 'fotogenic_ai_invalid_image', __( 'Unsupported image format received.', 'fotogenic-ai' ), [ 'status' => 400 ] );
        }

        return $this->create_attachment_from_temp_file(
            $downloaded,
            $mime,
            $ext,
            $context,
            true,
            wp_filesize( $downloaded )
        );
    }

    private function is_allowed_generated_image_url( string $url ): bool {
        $url    = esc_url_raw( trim( $url ) );
        $host   = strtolower( (string) wp_parse_url( $url, PHP_URL_HOST ) );
        $scheme = strtolower( (string) wp_parse_url( $url, PHP_URL_SCHEME ) );

        if ( '' === $url || 'https' !== $scheme || '' === $host || ! wp_http_validate_url( $url ) ) {
            return false;
        }

        $allowed_root_domains = array_values(
            array_unique(
                array_filter(
                    array_map(
                        static fn( $value ) => strtolower( ltrim( sanitize_text_field( (string) $value ), '.' ) ),
                        (array) apply_filters( 'fotogenic_ai_allowed_generated_image_root_domains', [ 'fotogenic.ai' ] )
                    )
                )
            )
        );

        foreach ( $allowed_root_domains as $root_domain ) {
            if ( $host === $root_domain || fotogenic_ai_str_ends_with( $host, '.' . $root_domain ) ) {
                return true;
            }
        }

        return false;
    }

    /** @return array|\WP_Error */
    private function create_attachment_from_temp_file( string $temp_file, string $mime, string $ext, array $context, bool $cleanup, int $bytes ) {
        require_once ABSPATH . 'wp-admin/includes/file.php';
        require_once ABSPATH . 'wp-admin/includes/media.php';
        require_once ABSPATH . 'wp-admin/includes/image.php';

        $copy          = $this->build_copy_for_context( $context );
        $filename_base = $this->build_filename_base( $context );
        $file_array    = [
            'name'     => sanitize_file_name( $filename_base . '.' . $ext ),
            'tmp_name' => $temp_file,
            'type'     => $mime,
            'error'    => 0,
            'size'     => $bytes,
        ];

        $attachment_id = media_handle_sideload(
            $file_array,
            0,
            $copy['description'],
            [
                'post_author'  => $this->resolve_attachment_author_id( $context ),
                'post_title'   => $copy['title'],
                'post_excerpt' => $copy['caption'],
                'post_content' => $copy['description'],
            ]
        );

        if ( $cleanup && file_exists( $temp_file ) ) {
            wp_delete_file( $temp_file );
        }

        if ( is_wp_error( $attachment_id ) ) {
            if ( Logger::instance()->enabled() ) {
                Logger::instance()->error( 'media_attachment_failed', [
                    'error' => $attachment_id->get_error_message(),
                ] );
            }
            return new \WP_Error( 'fotogenic_ai_attachment_failed', __( 'Failed to create attachment.', 'fotogenic-ai' ), [ 'status' => 500 ] );
        }

        $attachment_id = (int) $attachment_id;
		if ( ! $this->store_attachment_meta( $attachment_id, $context ) ) {
			wp_delete_attachment( $attachment_id, true );
			return new \WP_Error(
				'fotogenic_ai_attachment_provenance_failed',
				__( 'Generated attachment provenance could not be saved.', 'fotogenic-ai' ),
				[ 'status' => 500 ]
			);
		}
        $this->enforce_attachment_author( $attachment_id, $context );
        self::apply_generated_attachment_meta( $attachment_id, $copy );

        if ( Logger::instance()->enabled() ) {
            Logger::instance()->info( 'media_saved', [
                'attachment_id' => $attachment_id,
                'bytes'         => $bytes,
            ] );
        }

        return [
            'attachment_id' => $attachment_id,
            'url'           => self::get_attachment_view_url( $attachment_id ),
            'mime'          => $mime,
        ];
    }

	public function ensure_generated_attachment_context( int $attachment_id, array $context ): bool {
        if ( $attachment_id <= 0 || ! get_post( $attachment_id ) ) {
			return false;
        }
		if ( ! $this->store_attachment_meta( $attachment_id, $context ) ) {
			return false;
		}
        $this->enforce_attachment_author( $attachment_id, $context );
		return true;
    }

    private function get_extension_for_mime( string $mime ): string {
        return self::ALLOWED_IMAGE_MIME_TYPES[ $mime ] ?? '';
    }

    private function build_attachment_title( array $context ): string {
		$title = sanitize_text_field( (string) ( $context['product_title'] ?? '' ) );
        if ( '' === $title ) {
            $title = __( 'Fotogenic AI image', 'fotogenic-ai' );
        }

        return (string) $title;
    }

    private function build_attachment_caption( array $context ): string {
		$product_title = fotogenic_ai_plain_text_label( (string) ( $context['product_title'] ?? '' ) );
        $description = trim( (string) ( $context['scene_description'] ?? '' ) );
        if ( '' !== $description ) {
            return $description;
        }
        if ( '' !== $product_title ) {
            $summary = $this->build_selection_summary( $context );
            if ( '' !== $summary ) {
                /* translators: 1: product title, 2: generated selection summary. */
                return sprintf( __( 'Image of %1$s (%2$s)', 'fotogenic-ai' ), $product_title, $summary );
            }
            /* translators: %s: product title. */
            return sprintf( __( 'Image of %s', 'fotogenic-ai' ), $product_title );
        }
        return '';
    }

    private function build_attachment_description( array $context ): string {
        $description = trim( (string) ( $context['scene_description'] ?? '' ) );
        if ( '' !== $description ) {
            return $description;
        }
		$product_title = fotogenic_ai_plain_text_label( (string) ( $context['product_title'] ?? '' ) );
        if ( '' !== $product_title ) {
            $summary = $this->build_selection_summary( $context );
            if ( '' !== $summary ) {
                /* translators: 1: product title, 2: generated selection summary. */
                return sprintf( __( 'Product photo of %1$s. %2$s.', 'fotogenic-ai' ), $product_title, $summary );
            }
            /* translators: %s: product title. */
            return sprintf( __( 'Product photo of %s.', 'fotogenic-ai' ), $product_title );
        }
        return '';
    }

    private function build_filename_base( array $context ): string {
        if ( $this->is_source_metadata_target( $context ) ) {
            $source_attachment_id = $this->resolve_source_attachment_id( $context );
            $source_base = $this->get_source_filename_base( $source_attachment_id );
            if ( '' !== $source_base ) {
                return $source_base;
            }
        }

		$product_title = fotogenic_ai_plain_text_label( (string) ( $context['product_title'] ?? '' ) );
        $product_id = (int) ( $context['product_id'] ?? 0 );

        $base = $product_title !== '' ? $product_title : 'fotogenic-image';
        if ( $product_id ) {
            $base .= '-' . $product_id;
        }

        return sanitize_file_name( $base );
    }

    private function build_alt_text( array $context ): string {
        $description = trim( (string) ( $context['scene_description'] ?? '' ) );
        if ( '' !== $description ) {
            return $description;
        }

		$product_title = fotogenic_ai_plain_text_label( (string) ( $context['product_title'] ?? '' ) );
        $summary = $this->build_selection_summary( $context );
        if ( '' !== $product_title && '' !== $summary ) {
            /* translators: 1: product title, 2: generated selection summary. */
            return sprintf( __( '%1$s - %2$s', 'fotogenic-ai' ), $product_title, $summary );
        }
        if ( '' !== $product_title ) {
            return $product_title;
        }
        return $summary;
    }

    private function build_selection_summary( array $context, int $max = 3 ): string {
        return '';
    }

    private function is_source_metadata_target( array $context ): bool {
        $run_target = strtolower( trim( (string) ( $context['run_target'] ?? '' ) ) );
        return in_array( $run_target, [ 'media', 'post', 'page' ], true );
    }

    private function resolve_source_attachment_id( array $context ): int {
        $direct = (int) ( $context['source_attachment_id'] ?? 0 );
        if ( $direct > 0 ) {
            return $direct;
        }

        $run_target = strtolower( trim( (string) ( $context['run_target'] ?? '' ) ) );
        $subject_id = (int) ( $context['subject_id'] ?? 0 );
        if ( $subject_id <= 0 ) {
            return 0;
        }

        if ( 'media' === $run_target ) {
            return $subject_id;
        }
        if ( 'post' === $run_target || 'page' === $run_target ) {
            return (int) get_post_thumbnail_id( $subject_id );
        }
        return 0;
    }

    /**
     * @return array{title:string,caption:string,description:string,alt:string}
     */
    private function get_source_attachment_copy( int $attachment_id ): array {
        $empty = [
            'title'       => '',
            'caption'     => '',
            'description' => '',
            'alt'         => '',
        ];
        if ( $attachment_id <= 0 ) {
            return $empty;
        }

        $attachment = get_post( $attachment_id );
        if ( ! ( $attachment instanceof \WP_Post ) || 'attachment' !== $attachment->post_type ) {
            return $empty;
        }

        return [
			'title'       => sanitize_text_field( (string) $attachment->post_title ),
            'caption'     => trim( (string) $attachment->post_excerpt ),
            'description' => trim( (string) $attachment->post_content ),
            'alt'         => trim( (string) get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ),
        ];
    }

    private function get_source_filename_base( int $attachment_id ): string {
        if ( $attachment_id <= 0 ) {
            return '';
        }

        $file_path = get_attached_file( $attachment_id );
        if ( is_string( $file_path ) && '' !== $file_path ) {
            $file_name = pathinfo( $file_path, PATHINFO_FILENAME );
            $sanitized = sanitize_file_name( (string) $file_name );
            if ( '' !== $sanitized ) {
                return sanitize_file_name( $sanitized . '-1' );
            }
        }

		$title = fotogenic_ai_plain_text_label( (string) get_the_title( $attachment_id ) );
        if ( '' === $title ) {
            return '';
        }
        return sanitize_file_name( $title . '-1' );
    }

    private function get_generated_default_copy_text(): string {
        return __( 'This image was generated using Fotogenic.ai WordPress plugin.', 'fotogenic-ai' );
    }

    private function resolve_attachment_author_id( array $context ): int {
        $user_id = (int) ( $context['user_id'] ?? 0 );
        if ( $user_id > 0 && get_userdata( $user_id ) ) {
            return $user_id;
        }

        $current_user_id = get_current_user_id();
        if ( $current_user_id > 0 && get_userdata( $current_user_id ) ) {
            return $current_user_id;
        }

        $source_attachment_id = $this->resolve_source_attachment_id( $context );
        if ( $source_attachment_id > 0 ) {
            $source_attachment = get_post( $source_attachment_id );
            $source_author_id  = (int) ( $source_attachment->post_author ?? 0 );
            if ( $source_author_id > 0 && get_userdata( $source_author_id ) ) {
                return $source_author_id;
            }
        }

        $subject_id = (int) ( $context['subject_id'] ?? 0 );
        if ( $subject_id > 0 ) {
            $subject = get_post( $subject_id );
            $author_id = (int) ( $subject->post_author ?? 0 );
            if ( $author_id > 0 && get_userdata( $author_id ) ) {
                return $author_id;
            }
        }

        return 0;
    }

    private function enforce_attachment_author( int $attachment_id, array $context ): void {
        if ( $attachment_id <= 0 ) {
            return;
        }

        $author_id = $this->resolve_attachment_author_id( $context );
        if ( $author_id <= 0 ) {
            return;
        }

        $attachment = get_post( $attachment_id );
        if ( ! ( $attachment instanceof \WP_Post ) ) {
            return;
        }

        if ( (int) $attachment->post_author === $author_id ) {
            return;
        }

        wp_update_post( [
            'ID'          => $attachment_id,
            'post_author' => $author_id,
        ] );
    }

	private function store_attachment_meta( int $attachment_id, array $context ): bool {
		$job_id  = Upstream_Identifier::normalize( $context['job_id'] ?? null );
		$user_id = absint( $context['user_id'] ?? 0 );
		if ( $attachment_id <= 0 || '' === $job_id || $user_id <= 0 ) {
			return false;
		}

		$expected = [
			'_fotogenic_ai_generated'         => 1,
			'_fotogenic_ai_job_id'            => $job_id,
			'_fotogenic_ai_user_id'           => $user_id,
			'_fotogenic_ai_quality'           => (string) ( $context['quality'] ?? '' ),
			'_fotogenic_ai_source_product_id' => absint( $context['product_id'] ?? 0 ),
			'_fotogenic_ai_scene_description' => (string) ( $context['scene_description'] ?? '' ),
		];
		$raw_asset_id = $context['asset_id'] ?? null;
		if ( is_string( $raw_asset_id ) && '' !== trim( $raw_asset_id ) ) {
			$asset_id   = Upstream_Identifier::normalize( $raw_asset_id );
			$asset_order = $context['asset_order'] ?? null;
			if ( '' === $asset_id || ! is_int( $asset_order ) || $asset_order < 0 || $asset_order > 999999 ) {
				return false;
			}
			$expected['_fotogenic_ai_asset_id']    = $asset_id;
			$expected['_fotogenic_ai_asset_order'] = $asset_order;
		} elseif ( null !== $raw_asset_id && ! is_string( $raw_asset_id ) ) {
			return false;
		}

		foreach ( $expected as $meta_key => $value ) {
			update_post_meta( $attachment_id, $meta_key, $value );
		}
		foreach ( $expected as $meta_key => $value ) {
			$stored = get_post_meta( $attachment_id, $meta_key, true );
			if ( is_int( $value ) ? (int) $stored !== $value : $stored !== $value ) {
				return false;
			}
		}
		return true;
	}
}
