<?php

namespace FSPoster\App\SocialNetworks\Instagram\Helpers;

use Exception;
use FSPoster\App\Libraries\PHPImage\PHPImage;
use FSPoster\App\Providers\Core\Settings;
use FSPoster\App\Providers\Helpers\Helper;
use InvalidArgumentException;

class InstagramHelper
{
    public const FEED_IMAGE_MODE_FIT = 'fit_with_background';
    public const FEED_IMAGE_MODE_ZOOM = 'zoom_and_crop';
    public const DEFAULT_FEED_IMAGE_BACKGROUND_COLOR = '000000';

    const MIN_ASPECT_RATIO_PHOTO = 0.8;
    const MAX_ASPECT_RATIO_PHOTO = 1.91;
    const MIN_ASPECT_RATIO_VIDEO = 0.1;
    const MAX_ASPECT_RATIO_VIDEO = 10;
	public static array $recycle_bin = [];

    /**
     * @throws Exception
     */
    public static function imageForStory ($photo_path, $title, $link, $method ) : ?array
    {
		$storyBackground    = Settings::get( 'instagram_story_customization_bg_color', '636e72' );
		$titleBackground    = Settings::get( 'instagram_story_customization_title_bg_color', '000000' );
		$titleBackgroundOpc = Settings::get( 'instagram_story_customization_title_bg_opacity', '30' );
		$titleColor         = Settings::get( 'instagram_story_customization_title_color', 'FFFFFF' );
		$titleTop           = (int) Settings::get( 'instagram_story_customization_title_top_offset', '125' );
		$titleLeft          = (int) Settings::get( 'instagram_story_customization_title_left_offset', '30' );
		$titleWidth         = (int) Settings::get( 'instagram_story_customization_title_width', '660' );
		$titleFontSize      = (int) Settings::get( 'instagram_story_customization_title_font_size', '30' );
		$titleRtl           = (bool)Settings::get( 'instagram_story_customization_is_rtl', false);

		$linkBackground    = Settings::get( 'instagram_story_customization_link_bg_color', '000000' );
		$linkBackgroundOpc = Settings::get( 'instagram_story_customization_link_bg_opacity', '100' );
		$linkColor         = Settings::get( 'instagram_story_customization_link_color', '3468CF' );
		$linkTop           = (int) Settings::get( 'instagram_story_customization_link_top_offset', '1000' );
		$linkLeft          = (int) Settings::get( 'instagram_story_customization_link_left_offset', '30' );
		$linkWidth         = (int) Settings::get( 'instagram_story_customization_link_width', '660' );
		$linkFontSize      = (int) Settings::get( 'instagram_story_customization_link_font_size', '30' );

		$hashtag              = Settings::get( 'instagram_story_customization_hashtag_text', '' );
		$hashtagBackground    = Settings::get( 'instagram_story_customization_hashtag_bg_color', '000000' );
		$hashtagBackgroundOpc = Settings::get( 'instagram_story_customization_hashtag_bg_opacity', '100' );
		$hashtagColor         = Settings::get( 'instagram_story_customization_hashtag_color', '3468CF' );
		$hashtagTop           = (int) Settings::get( 'instagram_story_customization_hashtag_top_offset', 700 );
		$hashtagLeft          = (int) Settings::get( 'instagram_story_customization_hashtag_left_offset', '30' );
		$hashtagWidth         = (int) Settings::get( 'instagram_story_customization_hashtag_width', '660' );
		$hashtagFontSize      = (int) Settings::get( 'instagram_story_customization_hashtag_font_size', '30' );

		if ( $titleRtl )
		{
			$p_a   = new PersianStringDecorator();
			$title = $p_a->decorate( $title, false, true );
		}

		$titleBackgroundOpc   = $titleBackgroundOpc > 100 || $titleBackgroundOpc < 0 ? 0.3 : $titleBackgroundOpc / 100;
		$linkBackgroundOpc    = $linkBackgroundOpc > 100 || $linkBackgroundOpc < 0 ? 0.3 : $linkBackgroundOpc / 100;
		$hashtagBackgroundOpc = $hashtagBackgroundOpc > 100 || $hashtagBackgroundOpc < 0 ? 0.3 : $hashtagBackgroundOpc / 100;

		$storyBackground   = Helper::hexToRgb( $storyBackground );
		$storyBackground[] = 0;// opacity

		$storyW = 1080 / 1.5;
		$storyH = 1920 / 1.5;

		$imageInf    = new PHPImage( $photo_path );
		$imageWidth  = $imageInf->getWidth();
		$imageHeight = $imageInf->getHeight();

		if ( $imageWidth * $imageHeight > 3400 * 3400 ) // large file
			throw new Exception( fsp__('The file is too large! The allowed max ratio is 3400x3400!') );

		$imageInf->cleanup();
		unset( $imageInf );

		$w1 = $storyW;
		$h1 = intval(( $w1 / $imageWidth ) * $imageHeight);

		if ( $h1 > $storyH )
		{
			$w1 = intval(( $storyH / $h1 ) * $w1);
			$h1 = $storyH;
		}

		$image = new PHPImage();
		$image->initialiseCanvas( $storyW, $storyH, 'img', $storyBackground );

		$image->draw( $photo_path, '50%', '50%', $w1, $h1 );

		// write title
		if ( ! empty( $title ) )
		{
			$titleLength  = mb_strlen( $title, 'UTF-8' );
			$titlePercent = $titleLength - 40;
			if ( $titlePercent < 0 )
			{
				$titlePercent = 0;
			}
			else if ( $titlePercent > 100 )
			{
				$titlePercent = 100;
			}

			$textWidth   = $titleWidth;
			$textHeight  = 100 + $titlePercent;
			$iX          = $titleLeft;
			$iY          = $titleFontSize + $titleTop;
			$iY          = min( $iY, $storyH - $textHeight );

			$titleFontDir = Settings::get( 'instagram_story_customization_title_font_file', '' );

			$image->setFont( $titleFontDir );

			$image->textBox( $title, [
				'fontSize'          => $titleFontSize,
				'fontColor'         => Helper::hexToRgb( $titleColor ),
				'x'                 => $iX,
				'y'                 => $iY,
				'strokeWidth'       => 1,
				'strokeColor'       => [ 99, 110, 114 ],
				'width'             => $textWidth,
				'height'            => $textHeight,
				'alignHorizontal'   => 'center',
				'alignVertical'     => 'center',
                'drawBackground'    => true,
                'backgroundColor'   => Helper::hexToRgb( $titleBackground ),
                'backgroundOpacity' => $titleBackgroundOpc
			], $titleRtl );
		}

		// write link
		if ( ! empty( $link ) )
		{
			$https_pattern = '/^(https:\/\/www\.|https:\/\/|http:\/\/www\.|http:\/\/)/';

			preg_match( $https_pattern, $link, $matches );

			if ( ! empty( $matches[ 0 ] ) )
			{
				$link = substr( $link, strlen( $matches[ 0 ] ) );
			}

			if ( strpos( $link, '/' ) )
			{
				$link = explode( '/', $link )[ 0 ];
			}

			$link = strtoupper( $link );

			$textLength  = mb_strlen( $link, 'UTF-8' );
			$textPercent = $textLength - 40;
			if ( $textPercent < 0 )
			{
				$textPercent = 0;
			}
			else if ( $textPercent > 100 )
			{
				$textPercent = 100;
			}

			$textPadding = 10;
			$textWidth   = $linkWidth;
			$textHeight  = 100 + $textPercent;
			$iX          = $linkLeft;
			$iY          = $linkTop;

            $linkFontDir = Settings::get( 'instagram_story_customization_link_font_file', '' );

            $image->setFont( $linkFontDir );

			$image->textBox( $link, [
				'fontSize'          => $linkFontSize,
				'fontColor'         => Helper::hexToRgb( $linkColor ),
				'x'                 => $iX,
				'y'                 => $iY,
				'strokeWidth'       => 1,
				'strokeColor'       => [ 99, 110, 114 ],
				'width'             => $textWidth,
				'height'            => $textHeight,
				'alignHorizontal'   => 'center',
				'alignVertical'     => 'center',
                'drawBackground'    => true,
                'backgroundColor'   => Helper::hexToRgb( $linkBackground ),
                'backgroundOpacity' => $linkBackgroundOpc
			] );
		}

		// write hashtag
        $addHashtag = (bool)Settings::get('instagram_story_customization_add_hashtag', false);
		if ($addHashtag && ! empty( $hashtag ) && $method === 'login_pass' )
		{
			$hashtag = strtoupper( $hashtag );
			$hashtag = '#' . $hashtag;

			$textLength  = mb_strlen( $hashtag, 'UTF-8' );
			$textPercent = $textLength - 40;
			if ( $textPercent < 0 )
			{
				$textPercent = 0;
			}
			else if ( $textPercent > 100 )
			{
				$textPercent = 100;
			}

			$textPadding = 10;
			$textWidth   = $hashtagWidth;
			$textHeight  = 100 + $textPercent;
			$iX          = $hashtagLeft;
			$iY          = $hashtagTop;

            $hashtagFontDir = Settings::get( 'instagram_story_customization_hashtag_font_file', '' );

			$image->setFont( $hashtagFontDir );

			$image->textBox( $hashtag, [
				'fontSize'          => $hashtagFontSize,
				'fontColor'         => Helper::hexToRgb( $hashtagColor ),
				'x'                 => $iX,
				'y'                 => $iY,
				'strokeWidth'       => 1,
				'strokeColor'       => [ 99, 110, 114 ],
				'width'             => $textWidth,
				'height'            => $textHeight,
				'alignHorizontal'   => 'center',
				'alignVertical'     => 'center',
                'drawBackground'    => true,
                'backgroundColor'   => Helper::hexToRgb( $hashtagBackground ),
                'backgroundOpacity' => $hashtagBackgroundOpc
			] );
		}

        if(empty(wp_upload_dir()[ 'basedir' ]))
        {
            throw new Exception(fsp__('Upload directory is not writeable to create images for story'));
        }

        $newFileName = self::randomPathAndUrl();

        $image->setOutput( 'jpg' )->save( $newFileName['path'] );

		return [
			'width'  => $storyW,
			'height' => $storyH,
			'path'   => $newFileName['path'],
            'url'    => $newFileName['url']
		];
	}

    /**
     * @throws Exception
     */
    public static function imageForFeed ( $photo, string $url = '' ) : array
    {
		$result = @getimagesize( $photo );

		if ( $result === false )
		{
			throw new InvalidArgumentException( sprintf( 'The photo file "%s" is not a valid image.', $photo ) );
		}

		$width  = $result[ 0 ];
		$height = $result[ 1 ];

        if ( self::isFeedAspectRatioValid( $width, $height ) )
            return self::buildFeedImageResult( $photo, $width, $height, $url );

        $feedImageMode = self::normalizeFeedImageMode( Settings::get( 'instagram_feed_image_mode', self::FEED_IMAGE_MODE_FIT ) );

        if ( $feedImageMode === self::FEED_IMAGE_MODE_ZOOM )
            return self::cover( $photo, $width, $height );

        $backgroundColor = Helper::hexToRgb( self::normalizeHexColor( Settings::get( 'instagram_feed_image_background_color', self::DEFAULT_FEED_IMAGE_BACKGROUND_COLOR ) ) );

		return self::contain( $photo, $width, $height, $backgroundColor );
    }

    private static function isFeedAspectRatioValid( int $width, int $height ) : bool
    {
        $ratio = $width / $height;

        return $ratio >= self::MIN_ASPECT_RATIO_PHOTO && $ratio <= self::MAX_ASPECT_RATIO_PHOTO;
    }

    private static function buildFeedImageResult( string $photo, int $width, int $height, string $url = '' ) : array
    {
        if ( empty( $url ) )
            $url = self::resolveUploadUrlFromPath( $photo );

        return [
            'width'  => $width,
            'height' => $height,
            'path'   => $photo,
            'url'    => $url
        ];
    }

    /**
     * @throws Exception
     */
    private static function cover($photo, $width, $height) : array
    {
        $ratio     = $width / $height;
        $newWidth  = $width;
        $newHeight = $height;
        $crop      = false;

        if ( $ratio > self::MAX_ASPECT_RATIO_PHOTO ) {
            $crop = true;
            $newWidth = (int) ( $height * self::MAX_ASPECT_RATIO_PHOTO );
        }
        else if ( $ratio < self::MIN_ASPECT_RATIO_PHOTO ) {
            $crop = true;
            $newHeight = (int) ( $width / self::MIN_ASPECT_RATIO_PHOTO );
        }

        $image = new PHPImage();
        $image->initialiseCanvas( $newWidth, $newHeight, 'img', [ 255, 255, 255, 0 ] );

        if ( $crop ) { // crops when the w/h ratio is not appropriate
            $image->draw( $photo );
        } else { // scale
            $image->draw( $photo, '50%', '50%', $newWidth, $newHeight );
        }

        $newFileName = self::randomPathAndUrl();

        $image->setOutput( 'jpg' )->save( $newFileName['path'] );

        return [
            'width'  => $newWidth,
            'height' => $newHeight,
            'path'   => $newFileName['path'],
            'url'    => $newFileName['url']
        ];
    }

    /**
     * @throws Exception
     */
    private static function contain($photo, $width, $height, array $backgroundColor) : array
    {
        $crop      = false;
        $newWidth  = $width;
        $newHeight = $height;
        $ratio     = $width / $height;

        if ( $ratio > self::MAX_ASPECT_RATIO_PHOTO ) {
            $crop = true;
            $ratio = self::MAX_ASPECT_RATIO_PHOTO;
        }
        else if ( $ratio < self::MIN_ASPECT_RATIO_PHOTO ) {
            $crop = true;
            $ratio = self::MIN_ASPECT_RATIO_PHOTO;
        }

        if ($crop) {
            if ($ratio < 1) {
                $newWidth = (int) ($height * $ratio);
            } else {
                $newHeight = (int) ($width / $ratio);
            }
        }

        $canvasColor   = $backgroundColor;
        $canvasColor[] = 0; // opacity (GD alpha: 0 = opaque)

        $image = new PHPImage();
        $image->initialiseCanvas($newWidth, $newHeight, 'img', $canvasColor);

        $scale = min(
            $newWidth / $width,
            $newHeight / $height
        );

        $drawW = (int) ($width * $scale);
        $drawH = (int) ($height * $scale);

        $x = (int) (($newWidth - $drawW) / 2);
        $y = (int) (($newHeight - $drawH) / 2);

        $image->draw($photo, $x, $y, $drawW, $drawH);

        $newFileName = self::randomPathAndUrl();

        $image->setOutput( 'jpg' )->save( $newFileName['path'] );

        return [
            'width'  => $newWidth,
            'height' => $newHeight,
            'path'   => $newFileName['path'],
            'url'    => $newFileName['url']
        ];
    }

    private static function normalizeFeedImageMode( $mode ) : string
    {
        return $mode === self::FEED_IMAGE_MODE_ZOOM ? self::FEED_IMAGE_MODE_ZOOM : self::FEED_IMAGE_MODE_FIT;
    }

    private static function resolveUploadUrlFromPath( string $path ) : string
    {
        $wpUploadDir = wp_upload_dir();

        if ( empty( $wpUploadDir['basedir'] ) || empty( $wpUploadDir['baseurl'] ) )
            return '';

        $normalizedBaseDir = rtrim( wp_normalize_path( $wpUploadDir['basedir'] ), '/' );
        $normalizedPath = wp_normalize_path( $path );

        if ( $normalizedPath !== $normalizedBaseDir && strpos( $normalizedPath, $normalizedBaseDir . '/' ) !== 0 )
            return '';

        $relativePath = ltrim( substr( $normalizedPath, strlen( $normalizedBaseDir ) ), '/' );

        return empty( $relativePath ) ? rtrim( $wpUploadDir['baseurl'], '/' ) : rtrim( $wpUploadDir['baseurl'], '/' ) . '/' . $relativePath;
    }

    public static function normalizeHexColor( $color, string $default = self::DEFAULT_FEED_IMAGE_BACKGROUND_COLOR ) : string
    {
        $color = is_string( $color ) ? trim( $color ) : '';
        $color = ltrim( $color, '#' );

        return preg_match( '/^[0-9a-fA-F]{6}$/', $color ) ? strtolower( $color ) : $default;
    }

    /**
     * @throws Exception
     */
    public static function renderVideo ( $video_path, $target ) : array
    {
		if ( !function_exists('exec') || @exec( 'echo EXEC' ) !== 'EXEC' )
		{
			throw new Exception( fsp__( 'exec() function have to be enabled to share videos on Instagram. <a href=\'https://www.fs-poster.com/documentation/how-to-install-ffmpeg\' target=\'_blank\'>How to?</a>', [], false ) );
		}

        $ffmpeg = FFmpeg::factory();

		$details = $ffmpeg->videoDetails( $video_path );

		$width       = $details[ 'width' ];
		$height      = $details[ 'height' ];
		$duration    = (int) $details[ 'duration' ];
		$video_codec = (int) $details[ 'video_codec' ];
		$audio_codec = (int) $details[ 'audio_codec' ];

		$maxDuration = 60 - 0.1;
		$minDuration = 3;

		if ( $duration < $minDuration )
		{
			throw new Exception( 'Video is too short' );
		}

		$ratio1 = $width / $height;

		if ( $ratio1 > self::MAX_ASPECT_RATIO_VIDEO )
		{
			$newWidth  = (int) ( $height * self::MAX_ASPECT_RATIO_VIDEO );
			$newHeight = $height;
		}
		else if ( $ratio1 < self::MIN_ASPECT_RATIO_VIDEO )
		{
			$newWidth  = $width;
			$newHeight = (int) ( $width / self::MIN_ASPECT_RATIO_VIDEO );
		}
		else
		{
			$newWidth  = $width;
			$newHeight = $height;
		}

		$x = abs( $width - $newWidth ) / 2;
		$y = abs( $height - $newHeight ) / 2;

        $videoPathAndUrl = self::randomPathAndUrl(false);

		$thumbnail = self::randomPathAndUrl()['path'];

		$outputFilters = [
			'-metadata:s:v', 'rotate=0',
			'-f', 'mp4',
			'-c:v', 'libx264',
			'-profile:v', 'high',
			'-pix_fmt', 'yuv420p',
			'-g', '60',
			'-sc_threshold', '0',
			'-r', '30',
			'-b:v', '0',
            '-crf', '20',
            '-maxrate', '25M',
            '-bufsize', '50M',

            '-c:a', 'aac',
            '-b:a', '128k',
            '-ac', '2',
            '-ar', '48000',
            '-movflags', '+faststart',
		];

		/*if ( $audio_codec !== 'aac' )
		{
			if ( $ffmpeg->hasLibFdkAac() )
			{
				$outputFilters[] = '-c:a';
				$outputFilters[] = 'libfdk_aac';
				$outputFilters[] = '-vbr';
				$outputFilters[] = 4;
			}
			else
			{
				$outputFilters[] = '-strict';
				$outputFilters[] = '-2';
				$outputFilters[] = '-c:a';
				$outputFilters[] = 'aac';
				$outputFilters[] = '-b:a';
				$outputFilters[] = '96k';
			}
		}
		else
		{
			$outputFilters[] = '-c:a';
			$outputFilters[] = 'copy';
		}*/

		if ( $duration > $maxDuration )
		{
			$outputFilters[] = '-t';
			$outputFilters[] = sprintf( '%.2F', $maxDuration );
		}

		$ffmpegOutput          = $ffmpeg->run( ['-y', '-i', $video_path, '-vf', sprintf( 'crop=w=%d:h=%d:x=%d:y=%d', $newWidth, $newHeight, $x, $y ), ...$outputFilters, $videoPathAndUrl['path']] );
		$ffmpegOutputThumbnail = $ffmpeg->run( ['-y', '-i', $video_path, '-f', 'mjpeg', '-vframes', 1, '-ss', '00:00:00.000', $thumbnail] );

		return [
			'width'       => $width,
			'height'      => $height,
			'duration'    => $duration,
			'audio_codec' => $audio_codec,
			'vudie_codec' => $video_codec,
			'path'        => $videoPathAndUrl['path'],
			'url'         => $videoPathAndUrl['url'],
			'thumbnail'   => self::imageForFeed( $thumbnail )
		];
	}

	public static function moveToTrash ( $filePath )
	{
		self::$recycle_bin[] = $filePath;
	}

    /**
     * @throws Exception
     * @return array{path: string, url: string}
     */
    private static function randomPathAndUrl(bool $isImage = true): array
    {
        $ext = $isImage ? '.jpg' : '.mp4';

        $wpUploadDir = wp_upload_dir();

        if(empty($wpUploadDir['basedir']) || empty($wpUploadDir['baseurl']))
        {
            throw new Exception(fsp__('Wordpress upload directory is not writeable to save processed media files'));
        }

        $dirPath = $wpUploadDir['basedir'] . DIRECTORY_SEPARATOR . FSP_PLUGIN_SLUG . DIRECTORY_SEPARATOR . 'instagram';

        if(function_exists('mkdir') && !file_exists($dirPath))
        {
            $madeDir = mkdir($dirPath, 0777, true);

             if(!$madeDir)
             {
                 throw new Exception(fsp__('Failed to create upload directory'));
             }
        }

        $relativePath = [FSP_PLUGIN_SLUG, 'instagram', uniqid() . $ext];

        self::$recycle_bin[] = implode(DIRECTORY_SEPARATOR, [$wpUploadDir['basedir'], ...$relativePath]);

        return [
            'path' => implode(DIRECTORY_SEPARATOR, [$wpUploadDir['basedir'], ...$relativePath]),
            'url'  => implode('/', [$wpUploadDir['baseurl'], ...$relativePath]),
        ];
    }

}
