<?php

/**
 * Хелпер для роботи з CSV
 * Методи для парсингу та генерації CSV
 *
 * @package Flowaxy\Support\Helpers
 * @version 1.0.0 Alpha prerelease
 */

declare(strict_types=1);

namespace Flowaxy\Support\Helpers;

final class CsvHelper
{
    /**
     * Парсити CSV рядок
     *
     * @param string $csv
     * @param string $delimiter
     * @param string $enclosure
     * @param string $escape
     * @return array<int, array<int, string>>
     */
    public static function parse(string $csv, string $delimiter = ',', string $enclosure = '"', string $escape = '\\'): array
    {
        $lines = str_getcsv($csv, "\n");
        $result = [];

        foreach ($lines as $line) {
            $result[] = str_getcsv($line, $delimiter, $enclosure, $escape);
        }

        return $result;
    }

    /**
     * Конвертувати CSV в масив
     *
     * @param string $csv
     * @param bool $hasHeaders
     * @param string $delimiter
     * @return array<int, array<string, mixed>>
     */
    public static function toArray(string $csv, bool $hasHeaders = true, string $delimiter = ','): array
    {
        $lines = self::parse($csv, $delimiter);
        if (empty($lines)) {
            return [];
        }

        if (!$hasHeaders) {
            return $lines;
        }

        $headers = array_shift($lines);
        $result = [];

        foreach ($lines as $line) {
            $row = [];
            foreach ($headers as $index => $header) {
                $row[$header] = $line[$index] ?? null;
            }
            $result[] = $row;
        }

        return $result;
    }

    /**
     * Конвертувати масив в CSV
     *
     * @param array<int, array<string|int, mixed>> $array
     * @param array<int, string>|null $headers
     * @param string $delimiter
     * @return string
     */
    public static function fromArray(array $array, ?array $headers = null, string $delimiter = ','): string
    {
        if (empty($array)) {
            return '';
        }

        $output = fopen('php://temp', 'r+');

        if ($headers !== null) {
            fputcsv($output, $headers, $delimiter);
        } elseif (!empty($array) && is_array($array[0])) {
            fputcsv($output, array_keys($array[0]), $delimiter);
        }

        foreach ($array as $row) {
            fputcsv($output, $row, $delimiter);
        }

        rewind($output);
        $csv = stream_get_contents($output);
        fclose($output);

        return $csv;
    }

    /**
     * Прочитати CSV з файлу
     *
     * @param string $path
     * @param bool $hasHeaders
     * @param string $delimiter
     * @return array<int, array<string, mixed>>
     */
    public static function readFile(string $path, bool $hasHeaders = true, string $delimiter = ','): array
    {
        if (!FileHelper::exists($path)) {
            return [];
        }

        $content = FileHelper::get($path);
        if ($content === false) {
            return [];
        }

        return self::toArray($content, $hasHeaders, $delimiter);
    }

    /**
     * Записати CSV у файл
     *
     * @param string $path
     * @param array<int, array<string|int, mixed>> $array
     * @param array<int, string>|null $headers
     * @param string $delimiter
     * @return bool
     */
    public static function writeFile(string $path, array $array, ?array $headers = null, string $delimiter = ','): bool
    {
        $csv = self::fromArray($array, $headers, $delimiter);

        return FileHelper::put($path, $csv) !== false;
    }

    /**
     * Отримати заголовки з CSV
     *
     * @param string $csv
     * @param string $delimiter
     * @return array<int, string>|false
     */
    public static function getHeaders(string $csv, string $delimiter = ','): array|false
    {
        $lines = self::parse($csv, $delimiter);
        if (empty($lines)) {
            return false;
        }

        return $lines[0];
    }

    /**
     * Встановити заголовки для CSV
     *
     * @param string $csv
     * @param array<int, string> $headers
     * @param string $delimiter
     * @return string
     */
    public static function setHeaders(string $csv, array $headers, string $delimiter = ','): string
    {
        $lines = self::parse($csv, $delimiter);
        array_unshift($lines, $headers);

        return self::fromArray($lines, null, $delimiter);
    }

    /**
     * Отримати рядок з CSV
     *
     * @param string $csv
     * @param int $index
     * @param bool $hasHeaders
     * @param string $delimiter
     * @return array<string|int, mixed>|false
     */
    public static function getRow(string $csv, int $index, bool $hasHeaders = true, string $delimiter = ','): array|false
    {
        $array = self::toArray($csv, $hasHeaders, $delimiter);

        return $array[$index] ?? false;
    }

    /**
     * Додати рядок до CSV
     *
     * @param string $csv
     * @param array<string|int, mixed> $row
     * @param string $delimiter
     * @return string
     */
    public static function addRow(string $csv, array $row, string $delimiter = ','): string
    {
        $array = self::toArray($csv, false, $delimiter);
        $array[] = $row;

        return self::fromArray($array, null, $delimiter);
    }

    /**
     * Валідувати CSV файл
     *
     * @param string $path
     * @return bool
     */
    public static function validate(string $path): bool
    {
        if (!FileHelper::exists($path)) {
            return false;
        }

        $content = FileHelper::get($path);
        if ($content === false) {
            return false;
        }

        // Перевіряємо, чи можна розпарсити CSV
        try {
            $lines = self::parse($content);
            return !empty($lines);
        } catch (\Exception $e) {
            return false;
        }
    }
}
