<?php

namespace pictpostpersonal;

use pictpostpersonal\model\Setting;

/**
 * 各所から使用する共通関数群
 */
class Utility
{
    static function isValidDate(string $input): bool
    {
        $input = trim($input);
        if ($input === '') {
            return false;
        }

        // strtotimeで解釈可能か
        $ts = strtotime($input);
        if ($ts === false) {
            return false;
        }

        // 日付のみ or 日時あり を判定
        $hasTime = preg_match('/\d{1,2}:\d{2}(:\d{2})?/', $input);

        try {
            if ($hasTime) {
                // 秒あり/なし両対応
                $normalized = date('Y-m-d H:i:s', $ts);

                // 入力側も秒まで補完して比較
                $dt = new \DateTime($input);
                return $dt->format('Y-m-d H:i:s') === $normalized;
            } else {
                $normalized = date('Y-m-d', $ts);
                $dt = new \DateTime($input);
                return $dt->format('Y-m-d') === $normalized;
            }
        } catch (\Exception $e) {
            return false;
        }
    }
    
    static function GetCurrentTime(string $timezone = 'Asia/Tokyo'): string
    {
        if ($timezone != '') {
            date_default_timezone_set($timezone); // タイムゾーンを設定
        }

        return date('Y-m-d H:i:s');
    }

    static function GetCurrentShortMonth(string $timezone = 'Asia/Tokyo'): string
    {
        if ($timezone != '') {
            date_default_timezone_set($timezone); // タイムゾーンを設定
        }

        return date('Ym');
    }

    /**
     * UNIXタイムスタンプを指定されたフォーマットの日時文字列に変換する
     *
     * @param int $timestamp UNIXタイムスタンプ
     * @param string $format 日時のフォーマット。デフォルトは "Y-m-d H:i:s"
     * @param string $timezone タイムゾーン。デフォルトは "Asia/Tokyo"
     * @return string 変換後の日時文字列
     */
    public static function convertTimestampToDateTime($timestamp, string $format = 'Y-m-d H:i:s', string $timezone = 'Asia/Tokyo'): string
    {
        $date_time = new \DateTime('@' . $timestamp);
        $date_time->setTimezone(new \DateTimeZone($timezone));
        return $date_time->format($format);
    }


    //文字列の中の改行コードを<br>に変換する
    //ただし、HTMLブロック要素の開始・終了直後及び<script>/<style>内の改行コードは変換しない
    static function  convertLineBreaks(string $input): string
    {
        $output = $input;

        // Remove newlines after block elements
        $block_elements = array(
            'address',
            'article',
            'aside',
            'blockquote',
            'canvas',
            'dd',
            'div',
            'dl',
            'dt',
            'fieldset',
            'figcaption',
            'figure',
            'footer',
            'form',
            'h1',
            'h2',
            'h3',
            'h4',
            'h5',
            'h6',
            'header',
            'hr',
            'li',
            'main',
            'nav',
            'noscript',
            'ol',
            'p',
            'pre',
            'section',
            'table',
            'tbody',
            'tr',
            'td',
            'th',
            'tfoot',
            'ul',
            'video',
            'style',
            'script',
            'br'
        );

        $output = str_replace("\r", '', $output);

        //ブロック要素直後の改行コードを無効化する
        foreach ($block_elements as $block_element) {
            $output = preg_replace('/(<\/?' . $block_element .  '[^>]*?>)\n/i', '$1', $output);
        }

        $output = preg_replace_callback('/<script[^>]*?>(.*?)<\/script>/is', function ($matches) {
            //スクリプト内の改行を一時エスケープ
            return str_replace("\n", '[[BR]]', $matches[0]);
        }, $output);

        $output = preg_replace_callback('/<style[^>]*?>(.*?)<\/style>/is', function ($matches) {
            //CSS内の改行を一時エスケープ
            return str_replace("\n", '[[BR]]', $matches[0]);
        }, $output);

        $output = preg_replace('/^\n/m', '<br>', $output); //空行を<br>に変換
        $output = preg_replace('/(\{\/?\.\.[^\}]*?\})\n/i', '$1', $output); // {..***}～{/..}表記直後の改行を無効化
        $output = str_replace("\n", '<br>', $output);

        $output = preg_replace('/\[\[BR\]\]/', "\r\n", $output);

        return $output;
    }

    /**
     * 本文から一覧用CAPTIONを取得する
     *
     * @param string $comment
     * 
     * @return string
     * 
     */
    public static function createCaption(string $comment): string
    {
        if ($comment) {
            preg_match('/{caption}(.*?){\/caption}/si', $comment, $match);
            if ($match) {
                //キャプションがある場合はそれを使用
                $comment = $match[1];
            }

            $comment = htmlspecialchars(strip_tags($comment ?? ''));

            //特殊記述の削除
            $comment = preg_replace('/\{.*\}/si', '', $comment);
            $comment = preg_replace('/\r\n/', ' ', $comment);
            $comment = preg_replace('/\n/', ' ', $comment);
            $comment = strip_tags($comment ?? '');

            // 一部マークダウンを削除する
            $comment = preg_replace('/^(#+)\s*(.*)/m', "$2", $comment);
            $comment = preg_replace('/\*\*(.*)\*\*/', "$1", $comment);
            $comment = preg_replace('/\*(.*)\*/', "$1", $comment);

            if (mb_strlen($comment) > 100) {
                $comment = mb_substr($comment, 0, 100) . '...';
            }
        }
        return $comment;
    }

    /**
     * サムネイル画像の適切なパスを取得する
     *
     * @param array $article
     * @param bool $nsfw_consent
     * @param int $idx
     * 
     * @return string URL Rootからみたサムネイルのパス
     * 
     */
    public static function getThumbnailUrl(array $article, bool $nsfw_consent, $idx = 0): string
    {
        if ($article['media_count'] == 0) {
            if ($article['thumbnail_type'] == 3 && $article['thumbnail'] != '') {
                //ユーザーサムネイル
                return SITE_URL . "/user_resource/thumbnails/{$article['thumbnail']}";
            }
            if ($article['nsfw']) {
                return Environment::$thumbs['nsfw-text'];
            } else {
                return Environment::$thumbs['thumb'];;
            }
        } else if ($article['media_count'] > 0 && $article['nsfw'] && !$nsfw_consent) {
            //nsfw同意が取れていない
            return Environment::$thumbs['nsfw'];
        } else {
            switch ($article['thumbnail_type']) {
                case 0: //自動サムネイル
                case 1: //1枚目がサムネイル
                case 2: //1枚目がサムネイル
                    break;

                case 3: //ユーザー指定
                    if ($article['thumbnail'] != '') {
                        //ユーザーサムネイル
                        return SITE_URL . "/user_resource/thumbnails/{$article['thumbnail']}";
                    } else if ($article['media_count'] == 0) {
                        if ($article['nsfw']) {
                            return Environment::$thumbs['nsfw-text'];
                        } else {
                            return Environment::$thumbs['thumb'];;
                        }
                    }
                    break;

                case 4: //サムネイルなし
                    if ($article['nsfw']) {
                        return Environment::$thumbs['nsfw-text'];
                    } else {
                        return Environment::$thumbs['thumb'];
                    }
                    break;
            }
            return  SITE_URL . "/images/{$article['media'][$idx]['sub_dir']}{$article['media'][$idx]['thumb_name']}";
        }
    }

    public static function SessionStart(bool $regenerate = false)
    {
        if (session_status() === PHP_SESSION_ACTIVE) {
            // セッション開始済み
            if ($regenerate) {
                session_regenerate_id(true);
            }
            return;
        }

        if (session_status() !== PHP_SESSION_NONE) {
            session_destroy();
        }

        $session_name = 'ppp_session_id';
        $secure = false; // HTTPSのみでのセッション使用を設定
        $httponly = true; // JavaScriptからのアクセスを防止

        // セッションの設定
        ini_set('session.use_only_cookies', 1); // セッションIDをクッキーのみに限定
        $cookie_params = session_get_cookie_params(); // 現在のクッキー設定を取得
        session_set_cookie_params([
            'lifetime' => 0, // ブラウザを閉じるとセッションが終了
            'path' => $cookie_params['path'],
            'domain' => $cookie_params['domain'],
            'secure' => $secure,
            'samesite' => 'lax',
            'httponly' => $httponly
        ]);

        session_name($session_name);
        session_start();
        if ($regenerate) {
            session_regenerate_id(true);
        }
    }

    public static function clearSession()
    {
        self::SessionStart();
        // セッション変数をすべて解除
        $_SESSION = array();

        // セッションクッキーを削除
        if (ini_get('session.use_cookies')) {
            $params = session_get_cookie_params();
            setcookie(
                session_name(),
                '',
                time() - 42000,
                $params['path'],
                $params['domain'],
                $params['secure'],
                $params['httponly']
            );
        }

        // セッションを破棄
        session_destroy();
    }


    public static $cookie = null;
    public static function SetCookie(string $name, string $value, $expires, bool $secure): void
    {
        $root = Environment::getRootPath();
        setcookie($name, $value, [
            'expires' => $expires,
            'path' => $root,
            'samesite' => 'Lax',
            'secure' => $secure,
            'httponly' => true,
        ]);
        if (self::$cookie == null) {
            self::$cookie = $_COOKIE;
        }
        self::$cookie[$name] = $value;
    }

    public static function GetCookie(string $name, string $default): string
    {
        if (self::$cookie == null) {
            self::$cookie = $_COOKIE;
        }
        if (isset(self::$cookie[$name])) {
            return self::$cookie[$name];
        }
        return $default;
    }

    public static function calcExpires($days, $houres, $minutes, $sec): int
    {
        $d = $days * 24 * 60 * 60;
        $h = $houres * 60 * 60;
        $m = $minutes * 60;
        $s = $sec;
        return  time() + ($d + $h + $m + $s);
    }

    static $time_start;
    public static function startStopWatch()
    {
        self::$time_start = microtime(true); //実行開始時間を記録する
    }

    public static function stopStopWatch($label)
    {

        $time_end = microtime(true);
        $time = $time_end - self::$time_start;
        echo $label;
        var_dump($time); //実行時間を出力する
        echo '<br>';
    }

    public static function isDirectoryEmpty($dir)
    {
        if (!is_readable($dir)) return false; // ディレクトリが読み取れない場合はfalseを返す
        $handle = opendir($dir);
        while (($entry = readdir($handle)) !== false) {
            if ($entry != '.' && $entry != '..') {
                closedir($handle);
                return false; // 隠しファイル（"."や".."）以外のエントリが見つかった場合はfalseを返す
            }
        }
        closedir($handle);
        return true; // 隠しファイル以外に何もない場合はtrueを返す
    }
}
