<?php

declare(strict_types=1);

namespace Flowaxy\Support\Helpers;

use function basename;
use function chgrp;
use function chmod;
use function chown;
use function copy;
use function dirname;
use function file_exists;
use function file_get_contents;
use function file_put_contents;
use function filemtime;
use function filesize;
use function finfo_close;
use function finfo_file;
use function finfo_open;
use function flock;
use function fopen;
use function fclose;
use function function_exists;
use function glob;
use function is_dir;
use function is_file;
use function is_readable;
use function is_writable;
use function mkdir;
use function mime_content_type;
use function pathinfo;
use function rename;
use function rmdir;
use function scandir;
use function stream_get_contents;
use function str_starts_with;
use function unlink;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use const DS;
use const FILEINFO_MIME_TYPE;
use const FILE_APPEND;
use const LOCK_EX;
use const LOCK_UN;
use const PATHINFO_EXTENSION;
use const PATHINFO_FILENAME;

// Хелпер для роботи з файлами
// Розширені методи для маніпуляції файлами та директоріями
final class FileHelper
{
    // Перевірити існування файлу або директорії
    public static function exists(string $path): bool
    {
        return file_exists($path);
    }

    // Отримати вміст файлу
    public static function get(string $path, bool $lock = false): string|false
    {
        if (!self::exists($path)) {
            return false;
        }

        if ($lock) {
            $handle = fopen($path, 'rb');
            if ($handle === false) {
                return false;
            }

            if (!flock($handle, LOCK_EX)) {
                fclose($handle);
                return false;
            }

            $contents = stream_get_contents($handle);
            flock($handle, LOCK_UN);
            fclose($handle);

            return $contents;
        }

        return file_get_contents($path);
    }

    // Записати вміст у файл
    public static function put(string $path, string $contents, bool $lock = false): int|false
    {
        self::makeDirectory(dirname($path), 0755, true);

        return file_put_contents($path, $contents, $lock ? LOCK_EX : 0);
    }

    // Додати вміст у кінець файлу
    public static function append(string $path, string $contents): int|false
    {
        return file_put_contents($path, $contents, FILE_APPEND | LOCK_EX);
    }

    // Видалити файл
    public static function delete(string|array $paths): bool
    {
        $paths = is_array($paths) ? $paths : [$paths];
        $success = true;

        foreach ($paths as $path) {
            if (self::exists($path)) {
                $success = @unlink($path) && $success;
            }
        }

        return $success;
    }

    // Копіювати файл
    public static function copy(string $from, string $to): bool
    {
        self::makeDirectory(dirname($to), 0755, true);

        return copy($from, $to);
    }

    // Перемістити файл
    public static function move(string $from, string $to): bool
    {
        self::makeDirectory(dirname($to), 0755, true);

        return rename($from, $to);
    }

    // Отримати розмір файлу
    public static function size(string $path): int|false
    {
        return filesize($path);
    }

    // Отримати MIME тип файлу
    public static function mimeType(string $path): string|false
    {
        if (function_exists('mime_content_type')) {
            return mime_content_type($path);
        }

        if (function_exists('finfo_file')) {
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $mimeType = finfo_file($finfo, $path);
            finfo_close($finfo);

            return $mimeType;
        }

        return false;
    }

    // Отримати розширення файлу
    public static function extension(string $path): string
    {
        return pathinfo($path, PATHINFO_EXTENSION);
    }

    // Отримати ім'я файлу
    public static function name(string $path): string
    {
        return pathinfo($path, PATHINFO_FILENAME);
    }

    // Отримати базове ім'я файлу
    public static function basename(string $path): string
    {
        return basename($path);
    }

    // Отримати директорію файлу
    public static function dirname(string $path): string
    {
        return dirname($path);
    }

    // Перевірити, чи є файлом
    public static function isFile(string $path): bool
    {
        return is_file($path);
    }

    // Перевірити, чи є директорією
    public static function isDirectory(string $path): bool
    {
        return is_dir($path);
    }

    // Створити директорію
    public static function makeDirectory(string $path, int $mode = 0755, bool $recursive = false): bool
    {
        if (self::isDirectory($path)) {
            return true;
        }

        return mkdir($path, $mode, $recursive);
    }

    // Видалити директорію
    public static function deleteDirectory(string $path, bool $preserve = false): bool
    {
        if (!self::isDirectory($path)) {
            return false;
        }

        $files = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
            RecursiveIteratorIterator::CHILD_FIRST
        );

        foreach ($files as $file) {
            if ($file->isDir()) {
                @rmdir($file->getRealPath());
            } else {
                @unlink($file->getRealPath());
            }
        }

        if (!$preserve) {
            @rmdir($path);
        }

        return true;
    }

    // Отримати файли за шаблоном
    public static function glob(string $pattern, int $flags = 0): array|false
    {
        return glob($pattern, $flags);
    }

    // Отримати всі файли в директорії
    public static function files(string $directory, bool $hidden = false): array
    {
        $files = [];

        if (self::isDirectory($directory)) {
            $items = scandir($directory);

            foreach ($items as $item) {
                if ($item === '.' || $item === '..') {
                    continue;
                }

                if (!$hidden && str_starts_with($item, '.')) {
                    continue;
                }

                $path = $directory . DS . $item;

                if (self::isFile($path)) {
                    $files[] = $path;
                }
            }
        }

        return $files;
    }

    // Отримати всі директорії в директорії
    public static function directories(string $directory): array
    {
        $directories = [];

        if (self::isDirectory($directory)) {
            $items = scandir($directory);

            foreach ($items as $item) {
                if ($item === '.' || $item === '..') {
                    continue;
                }

                $path = $directory . DS . $item;

                if (self::isDirectory($path)) {
                    $directories[] = $path;
                }
            }
        }

        return $directories;
    }

    // Отримати час останньої зміни
    public static function lastModified(string $path): int|false
    {
        return filemtime($path);
    }

    // Перевірити, чи доступний для читання
    public static function isReadable(string $path): bool
    {
        return is_readable($path);
    }

    // Перевірити, чи доступний для запису
    public static function isWritable(string $path): bool
    {
        return is_writable($path);
    }

    // Змінити права доступу
    public static function chmod(string $path, int $mode): bool
    {
        return chmod($path, $mode);
    }

    // Змінити власника
    public static function chown(string $path, string|int $user, string|int|null $group = null): bool
    {
        if ($group !== null) {
            return chown($path, $user) && chgrp($path, $group);
        }

        return chown($path, $user);
    }
}
