<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Support\Facades\DB;

/**
 * Class BinaryTextCast
 *
 * Пользовательский класс для приведения типов, который обеспечивает преобразование
 * текстовых данных, хранящихся в бинарном формате (VARBINARY) в базе данных,
 * в строку UTF-8 для использования в приложении, и обратно.
 *
 * Это необходимо для корректной работы с данными, которые хранятся в кодировке (Windows-1251),
 * отличной от используемой в приложении.
 *
 * @package App\Casts
 */
class BinaryTextCast implements CastsAttributes
{
    /**
     * Преобразует значение из базы данных для использования в приложении.
     *
     * Этот метод вызывается, когда мы получаем значение атрибута из модели Eloquent.
     * Он обрабатывает бинарные данные, конвертируя их из Windows-1251 в UTF-8.
     *
     * @param \\Illuminate\\Database\\Eloquent\\Model $model
     * @param string $key
     * @param mixed $value
     * @param array $attributes
     * @return string|null
     */
    public function get($model, string $key, $value, array $attributes)
    {
        // Если значение из БД равно null, возвращаем null
        if (is_null($value)) {
            return null;
        }

        // Проверяем, является ли значение шестнадцатеричной строкой (например, '0x...').
        // Некоторые драйверы баз данных могут возвращать бинарные данные в таком формате.
        if (ctype_xdigit($value) && str_starts_with(strtolower($value), '0x')) {
            // Если да, убираем префикс '0x' и конвертируем из hex в бинарные данные.
            $binary_data = hex2bin(substr($value, 2));
        } else {
            // В противном случае, считаем, что значение уже является бинарными данными.
            $binary_data = $value;
        }

        // Конвертируем бинарные данные из кодировки Windows-1251 в UTF-8
        // и удаляем пробельные символы в конце строки.
        return rtrim(mb_convert_encoding($binary_data, 'UTF-8', 'Windows-1251'));
    }

    
    /**
     * Преобразует значение из приложения для сохранения в базу данных.
     *
     * Этот метод вызывается, когда мы устанавливаем значение атрибута в модели Eloquent.
     * Он конвертирует строку из UTF-8 в Windows-1251 и подготавливает ее
     * для сохранения в поле типа VARBINARY.
     *
     * @param \\Illuminate\\Database\\Eloquent\\Model $model
     * @param string $key
     * @param mixed $value
     * @param array $attributes
     * @return array
     */
    public function set($model, string $key, $value, array $attributes)
    {
        // Если устанавливаемое значение равно null, сохраняем null в БД.
        if (is_null($value)) {
            return [$key => null];
        }

        // Конвертируем строку из UTF-8 в Windows-1251.
        $win1251_string = mb_convert_encoding($value, 'Windows-1251', 'UTF-8');
        
        // Преобразуем строку в ее шестнадцатеричное представление.
        $hex_string = bin2hex($win1251_string);

        // Формируем бинарный литерал для SQL-запроса (например, 0x...).
        $binary_literal = '0x' . $hex_string;

        // Возвращаем массив, где ключ - это имя атрибута, а значение -
        // сырое SQL-выражение для преобразования hex-строки в VARBINARY(MAX).
        // Это специфично для MS SQL Server.
        return [
            $key => DB::raw("CONVERT(VARBINARY(MAX), " . $binary_literal . ")")
        ];
    }


}