<?php

namespace YayMail\Countdown;

use YayMail\Utils\SingletonTrait;

/* phpcs:disable Generic.WhiteSpace.DisallowTabIndent */

/**
 * Countdown
 *
 * Serves a dynamic countdown animated GIF based on query parameters.
 */
class Countdown {

    use SingletonTrait;

    public function __construct() {

        $yaymail_timer_action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

        if ( empty( $yaymail_timer_action ) || 'yaymail_timer' !== $yaymail_timer_action ) {
            return;
        }

        $__yaymail_dt = isset( $_GET['dt'] ) ? sanitize_text_field( wp_unslash( $_GET['dt'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if ( '' !== $__yaymail_dt ) {
            $this->maybe_serve_gif();
        }
    }

    /**
     * If the current request includes countdown query params, render the GIF and exit.
     *
     * Expected params:
     * - dt: target date/time string
     * - tc: text color hex (optional)
     * - bg: background color hex (optional)
     * - fs: font size (optional)
     * - gap: horizontal gap between groups (optional)
     * - sl: show labels flag (optional)
     * - ld, lh, lm, ls: labels text (optional)
     * - lfs: label font size (optional)
     * - lc: label color hex (optional)
     */
    public function maybe_serve_gif() {
        if ( empty( $_GET['dt'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
            return;
        }

        $time_input = isset( $_GET['dt'] ) ? sanitize_text_field( wp_unslash( $_GET['dt'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $time       = str_replace( '/', ' ', $time_input );

        // Get WordPress timezone
        $wp_timezone = wp_timezone();

        // Parse the input time in the WordPress timezone
        $future_date = new \DateTime( $time, $wp_timezone );
        $now         = new \DateTime( 'now', $wp_timezone );

        $f = '%a:%H:%I:%S';
        $t = 'second';

        $frames = [];
        $delays = [];

        $image_path = YAYMAIL_PLUGIN_PATH . 'src/Countdown/countdown-background.png';

        // Allow runtime customization via query params.
        $tc = isset( $_GET['tc'] ) ? sanitize_text_field( wp_unslash( $_GET['tc'] ) ) : 'FFFFFF'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $bg = isset( $_GET['bg'] ) ? sanitize_text_field( wp_unslash( $_GET['bg'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        // Counter (digits) font size: clamp to 10-50 px with a sensible default.
        $fs_raw = isset( $_GET['fs'] ) ? absint( wp_unslash( $_GET['fs'] ) ) : 30; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $fs     = max( 10, min( 50, $fs_raw ) );
        $gap    = isset( $_GET['gap'] ) ? max( 0, absint( wp_unslash( $_GET['gap'] ) ) ) : 40; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        // Labels params.
        $sl    = isset( $_GET['sl'] ) ? (bool) absint( wp_unslash( $_GET['sl'] ) ) : false; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $ld    = isset( $_GET['ld'] ) ? sanitize_text_field( wp_unslash( $_GET['ld'] ) ) : 'days'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $lh    = isset( $_GET['lh'] ) ? sanitize_text_field( wp_unslash( $_GET['lh'] ) ) : 'hours'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $lm    = isset( $_GET['lm'] ) ? sanitize_text_field( wp_unslash( $_GET['lm'] ) ) : 'minutes'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $ls    = isset( $_GET['ls'] ) ? sanitize_text_field( wp_unslash( $_GET['ls'] ) ) : 'seconds'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $lfs   = isset( $_GET['lfs'] ) ? absint( wp_unslash( $_GET['lfs'] ) ) : 30; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $lc    = isset( $_GET['lc'] ) ? sanitize_text_field( wp_unslash( $_GET['lc'] ) ) : '666666'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $xo    = 10;
        $yo    = 10;
        $delay = 100;

        // Reserve exact space for labels at the bottom (based on their actual height).
        $reserved_bottom_px = 0;
        if ( $sl ) {
            $measure_file = YAYMAIL_PLUGIN_PATH . 'src/Countdown/PT_Sans-Web-Regular.ttf';
            $labels_set   = [ $ld, $lh, $lm, $ls ];
            $max_h        = 0;
            foreach ( $labels_set as $lbl ) {
                if ( '' === $lbl ) {
                    continue;
                }
                $bbox   = imagettfbbox( max( 5, (int) $lfs ), 0, $measure_file, $lbl );
                $text_h = abs( $bbox[5] - $bbox[1] );
                if ( $text_h > $max_h ) {
                    $max_h = $text_h;
                }
            }
            // Add padding to separate digits and labels rows, but soft-cap to 35% of content height later.
            $reserved_bottom_px = $max_h ? ( $max_h + 8 ) : 0;
        }

        $font = [
            'size'            => $fs,
            'angle'           => 0,
            'x-offset'        => $xo,
            'y-offset'        => $yo,
            'file'            => YAYMAIL_PLUGIN_PATH . 'src/Countdown/PT_Sans-Web-Regular.ttf',
            // Reserve a fixed pixel height at the bottom for labels when enabled.
            'reserved_bottom' => max( 0, (int) $reserved_bottom_px ),
            // Horizontal gap between groups.
            'gap'             => $gap,
        ];

        for ( $i = 0; $i <= 60; $i++ ) {
            $interval = date_diff( $future_date, $now );
            if ( $future_date < $now ) {
                $image = imagecreatefrompng( $image_path );
                // Optional solid background fill inside GIF.
                if ( ! empty( $bg ) ) {
                    list( $br, $bgc, $bb ) = sscanf( ltrim( $bg, '#' ), '%02x%02x%02x' );
                    $bg_color              = imagecolorallocate( $image, $br, $bgc, $bb );
                    imagefilledrectangle( $image, 0, 0, imagesx( $image ), imagesy( $image ), $bg_color );
                }
                list( $r, $g, $b ) = sscanf( ltrim( $tc, '#' ), '%02x%02x%02x' );
                $font_color        = imagecolorallocate( $image, $r, $g, $b );
                $text              = $interval->format( '00:00:00:00' );
                $labels            = $sl ? [ $ld, $lh, $lm, $ls ] : null;
                $lcolor            = null;
                if ( $sl ) {
                    list( $lr, $lg, $lb ) = sscanf( ltrim( $lc, '#' ), '%02x%02x%02x' );
                    $lcolor               = imagecolorallocate( $image, $lr, $lg, $lb );
                }
                self::draw_counter_with_labels( $image, $text, $font, $font_color, $labels, $lcolor, $sl ? $lfs : null );
                ob_start();
                imagegif( $image );
                $frames[] = ob_get_contents();
                $delays[] = $delay;
                $loops    = 1;
                ob_end_clean();
                break;
            } else {
                $image = imagecreatefrompng( $image_path );
                // Optional solid background fill inside GIF.
                if ( ! empty( $bg ) ) {
                    list( $br, $bgc, $bb ) = sscanf( ltrim( $bg, '#' ), '%02x%02x%02x' );
                    $bg_color              = imagecolorallocate( $image, $br, $bgc, $bb );
                    imagefilledrectangle( $image, 0, 0, imagesx( $image ), imagesy( $image ), $bg_color );
                }
                list( $r, $g, $b ) = sscanf( ltrim( $tc, '#' ), '%02x%02x%02x' );
                $font_color        = imagecolorallocate( $image, $r, $g, $b );
                $text              = $interval->format( $f );
                if ( preg_match( '/^[0-9]\:/', $text ) ) {
                    $text = '0' . $text;
                }
                $labels = $sl ? [ $ld, $lh, $lm, $ls ] : null;
                $lcolor = null;
                if ( $sl ) {
                    list( $lr, $lg, $lb ) = sscanf( ltrim( $lc, '#' ), '%02x%02x%02x' );
                    $lcolor               = imagecolorallocate( $image, $lr, $lg, $lb );
                }
                self::draw_counter_with_labels( $image, $text, $font, $font_color, $labels, $lcolor, $sl ? $lfs : null );
                ob_start();
                imagegif( $image );
                $frames[] = ob_get_contents();
                $delays[] = $delay;
                $loops    = 0;
                ob_end_clean();
            }//end if
            $now->modify( '+1 ' . $t );
        }//end for

        $gif = new \YayMail\Countdown\AnimatedGif( $frames, $delays, $loops );
        header( 'Expires: ' . gmdate( 'D, d M Y H:i:s T', strtotime( $time ) ) );
        // expire this image.
        header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
        header( 'Cache-Control: no-store, no-cache, must-revalidate' );
        header( 'Cache-Control: post-check=0, pre-check=0', false );
        header( 'Pragma: no-cache' );
        $gif->display();
        exit;
    }

    /**
     * Draw centered text fitted within the image minus margins.
     *
     * @param \GdImage|resource $image GD resource.
     * @param string            $text  Text to draw.
     * @param array             $font  Font config.
     * @param int               $font_color Color index.
     * @return void
     */
    private static function draw_fitted_text( $image, $text, $font, $font_color ) {
        $img_w = imagesx( $image );
        $img_h = imagesy( $image );

        $margin_x        = max( 0, (int) $font['x-offset'] );
        $margin_y        = max( 0, (int) $font['y-offset'] );
        $content_w       = max( 1, $img_w - 2 * $margin_x );
        $content_h       = max( 1, $img_h - 2 * $margin_y );
        $reserved_bottom = isset( $font['reserved_bottom'] ) ? max( 0, (int) $font['reserved_bottom'] ) : 0;
        // Prevent labels from consuming too much vertical space. Cap to 35%.
        $reserved_bottom = min( $reserved_bottom, (int) floor( $content_h * 0.35 ) );
        $digits_area_h   = max( 1, (int) floor( $content_h - $reserved_bottom ) );

        $requested_size = max( 5, (int) $font['size'] );
        $angle          = (int) $font['angle'];
        $file           = $font['file'];

        $size = min( $requested_size, (int) floor( $digits_area_h * 0.9 ) );
        if ( $size < 5 ) {
            $size = 5;
        }

        $attempts = 0;
        do {
            $bbox   = imagettfbbox( $size, $angle, $file, $text );
            $text_w = abs( $bbox[2] - $bbox[0] );
            $text_h = abs( $bbox[5] - $bbox[1] );
            if ( $text_w <= $content_w && $text_h <= $content_h ) {
                break;
            }
            --$size;
            ++$attempts;
        } while ( $size > 5 && $attempts < 300 );

        $bbox   = imagettfbbox( $size, $angle, $file, $text );
        $text_w = abs( $bbox[2] - $bbox[0] );
        $text_h = abs( $bbox[5] - $bbox[1] );

        $x = (int) ( $margin_x + ( ( $content_w - $text_w ) / 2 ) - $bbox[0] );
        $y = (int) ( $margin_y + ( ( $digits_area_h - $text_h ) / 2 ) + $text_h );

        imagettftext( $image, $size, $angle, $x, $y, $font_color, $file, $text );
    }

	/* phpcs:disable Squiz.Commenting.FunctionComment.SpacingAfterParamType, Squiz.Commenting.FunctionComment.ParamCommentFullStop */
    /**
     * Draw labels row beneath the digits.
     *
     * @param \GdImage|resource $image Image resource.
     * @param array              $labels Array of up to four strings.
     * @param array              $font Font settings and margins.
     * @param int                $label_color GD color index.
     * @param int                $label_font_size Base label font size in px.
     */
    private static function draw_labels_row( $image, array $labels, array $font, $label_color, $label_font_size ) {
        if ( empty( $labels ) ) {
            return;
        }

        $img_w = imagesx( $image );
        $img_h = imagesy( $image );

        $margin_x        = max( 0, (int) $font['x-offset'] );
        $margin_y        = max( 0, (int) $font['y-offset'] );
        $content_w       = max( 1, $img_w - 2 * $margin_x );
        $content_h       = max( 1, $img_h - 2 * $margin_y );
        $reserved_bottom = isset( $font['reserved_bottom'] ) ? max( 0, (int) $font['reserved_bottom'] ) : 0;

        $digits_area_h = max( 1, (int) floor( $content_h - $reserved_bottom ) );
        $labels_y      = $margin_y + $digits_area_h;
        $labels_h      = max( 1, $reserved_bottom );

        $num_cells  = count( $labels );
        $cell_width = (int) floor( $content_w / $num_cells );

        $angle = 0;
        $file  = $font['file'];

        for ( $i = 0; $i < $num_cells; $i++ ) {
            $label = (string) $labels[ $i ];
            if ( '' === $label ) {
                continue;
            }

            $size     = max( 5, (int) $label_font_size );
            $attempts = 0;
            do {
                $bbox   = imagettfbbox( $size, $angle, $file, $label );
                $text_w = abs( $bbox[2] - $bbox[0] );
                $text_h = abs( $bbox[5] - $bbox[1] );
                if ( $text_w <= $cell_width && $text_h <= $labels_h ) {
                    break;
                }
                --$size;
                ++$attempts;
            } while ( $size > 5 && $attempts < 200 );

            $bbox   = imagettfbbox( $size, $angle, $file, $label );
            $text_w = abs( $bbox[2] - $bbox[0] );
            $text_h = abs( $bbox[5] - $bbox[1] );

            $cell_x = $margin_x + ( $i * $cell_width );
            $x      = (int) ( $cell_x + ( ( $cell_width - $text_w ) / 2 ) - $bbox[0] );
            $y      = (int) ( $labels_y + ( ( $labels_h - $text_h ) / 2 ) + $text_h );

            imagettftext( $image, $size, $angle, $x, $y, $label_color, $file, $label );
        }//end for
    }

    /**
     * Draw the countdown digits and, if provided, center labels under each group.
     *
     * @param \GdImage|resource $image GD resource.
     * @param string             $text Counter string like 22:03:32:15.
     * @param array              $font Font settings including reserved_bottom.
     * @param int                $font_color Digits color.
     * @param array|null         $labels Optional labels [days,hours,minutes,seconds].
     * @param int|null           $label_color Labels color.
     * @param int|null           $label_size Base label font size.
     * @return void
     */
    private static function draw_counter_with_labels( $image, $text, array $font, $font_color, $labels = null, $label_color = null, $label_size = null ) {
        $img_w = imagesx( $image );
        $img_h = imagesy( $image );

        $margin_x        = max( 0, (int) $font['x-offset'] );
        $margin_y        = max( 0, (int) $font['y-offset'] );
        $content_w       = max( 1, $img_w - 2 * $margin_x );
        $content_h       = max( 1, $img_h - 2 * $margin_y );
        $reserved_bottom = isset( $font['reserved_bottom'] ) ? max( 0, (int) $font['reserved_bottom'] ) : 0;
        $digits_area_h   = max( 1, (int) floor( $content_h - $reserved_bottom ) );

        $requested_size = max( 5, (int) $font['size'] );
        $file           = $font['file'];
        $angle          = 0;

        $groups      = explode( ':', $text );
        $group_count = count( $groups );
        while ( $group_count < 4 ) {
            $groups[] = '00';
            ++$group_count;
        }

        $size     = min( $requested_size, (int) floor( $digits_area_h * 0.8 ) );
        $attempts = 0;
        do {
            $group_widths = [];
            $max_text_h   = 0;
            $total_width  = 0;
            for ( $i = 0; $i < 4; $i++ ) {
                $bbox               = imagettfbbox( $size, $angle, $file, $groups[ $i ] );
                $group_widths[ $i ] = abs( $bbox[2] - $bbox[0] );
                $total_width       += $group_widths[ $i ];
                $h_i                = abs( $bbox[5] - $bbox[1] );
                if ( $h_i > $max_text_h ) {
                    $max_text_h = $h_i;
                }
            }
            $total_width += 3 * ( $font['gap'] ?? 0 );
            if ( $total_width <= $content_w && $max_text_h <= $digits_area_h ) {
                break;
            }
            --$size;
            ++$attempts;
        } while ( $size > 5 && $attempts < 200 );

        $bbox_line  = imagettfbbox( $size, $angle, $file, '0123456789' );
        $text_h     = abs( $bbox_line[5] - $bbox_line[1] );
        $baseline_y = (int) ( $margin_y + ( ( $digits_area_h - $text_h ) / 2 ) + $text_h );

        $group_widths = [];
        $total_width  = 0;
        for ( $i = 0; $i < 4; $i++ ) {
            $bbox               = imagettfbbox( $size, $angle, $file, $groups[ $i ] );
            $group_widths[ $i ] = abs( $bbox[2] - $bbox[0] );
            $total_width       += $group_widths[ $i ];
        }
        $total_width += 3 * ( $font['gap'] ?? 0 );
        $x            = (int) ( $margin_x + ( ( $content_w - $total_width ) / 2 ) );

        $left_edges    = [];
        $right_edges   = [];
        $group_centers = [];
        for ( $i = 0; $i < 4; $i++ ) {
            $bbox   = imagettfbbox( $size, $angle, $file, $groups[ $i ] );
            $x_draw = $x - $bbox[0];
            imagettftext( $image, $size, $angle, $x_draw, $baseline_y, $font_color, $file, $groups[ $i ] );
            $left_edges[ $i ]    = $x;
            $right_edges[ $i ]   = $x + $group_widths[ $i ];
            $group_centers[ $i ] = $x + ( $group_widths[ $i ] / 2 );
            $x                  += $group_widths[ $i ];
            if ( $i < 3 ) {
                $x += ( $font['gap'] ?? 0 );
            }
        }

        for ( $i = 0; $i < 3; $i++ ) {
            $mid_x      = ( $right_edges[ $i ] + $left_edges[ $i + 1 ] ) / 2.0;
            $bbox_sep   = imagettfbbox( $size, $angle, $file, ':' );
            $sep_w      = abs( $bbox_sep[2] - $bbox_sep[0] );
            $x_sep_draw = (int) ( $mid_x - ( $sep_w / 2 ) - $bbox_sep[0] );
            imagettftext( $image, $size, $angle, $x_sep_draw, $baseline_y, $font_color, $file, ':' );
        }

        if ( ! empty( $labels ) && null !== $label_color && null !== $label_size ) {
            $labels_y = $margin_y + $digits_area_h;
            $labels_h = max( 1, (int) $reserved_bottom );
            if ( $labels_h < 10 ) {
                $labels_h = 10;
            }

            $labels_count      = min( 4, (int) count( $labels ) );
            $cell_centers      = [];
            $group_cell_widths = [];
            $gap_px            = (int) ( $font['gap'] ?? 0 );
            for ( $i = 0; $i < 4; $i++ ) {
                $left_pad                = $gap_px / 2;
                $right_pad               = $gap_px / 2;
                $cell_left               = $left_edges[ $i ] - $left_pad;
                $cell_right              = $right_edges[ $i ] + $right_pad;
                $cell_centers[ $i ]      = ( $cell_left + $cell_right ) / 2.0;
                $group_cell_widths[ $i ] = $cell_right - $cell_left;
            }

            $final_label_size = max( 5, (int) $label_size );
            $attempts         = 0;
            do {
                $all_fit = true;
                for ( $i = 0; $i < $labels_count; $i++ ) {
                    $label_i = (string) $labels[ $i ];
                    if ( '' === $label_i ) {
                        continue;
                    }
                    $bbox_i = imagettfbbox( $final_label_size, 0, $file, $label_i );
                    $lw_i   = abs( $bbox_i[2] - $bbox_i[0] );
                    $lh_i   = abs( $bbox_i[5] - $bbox_i[1] );
                    if ( $lw_i > $group_cell_widths[ $i ] || $lh_i > $labels_h ) {
                        $all_fit = false;
                        break;
                    }
                }
                if ( $all_fit ) {
                    break;
                }
                --$final_label_size;
                ++$attempts;
            } while ( $final_label_size > 5 && $attempts < 120 );

            $bbox_ref     = imagettfbbox( $final_label_size, 0, $file, 'Ay' );
            $top_ref_y    = min( $bbox_ref[1], $bbox_ref[3], $bbox_ref[5], $bbox_ref[7] );
            $bottom_ref_y = max( $bbox_ref[1], $bbox_ref[3], $bbox_ref[5], $bbox_ref[7] );
            $ascent       = - (int) $top_ref_y;
            $descent      = (int) $bottom_ref_y;
            $baseline_y   = (int) ( $labels_y + ( ( $labels_h - ( $ascent + $descent ) ) / 2 ) + $ascent );

            for ( $i = 0; $i < $labels_count; $i++ ) {
                $label = (string) $labels[ $i ];
                if ( '' === $label ) {
                    continue;
                }
                $bbox    = imagettfbbox( $final_label_size, 0, $file, $label );
                $lw      = abs( $bbox[2] - $bbox[0] );
                $x_label = (int) ( $cell_centers[ $i ] - ( $lw / 2 ) - $bbox[0] );
                imagettftext( $image, $final_label_size, 0, $x_label, $baseline_y, $label_color, $file, $label );
            }
        }//end if
    }
}
