<?php
declare(strict_types=1);

namespace App\Http;

final class QuestionSerializer
{
    public function question(array $row, array $answers = []): array
    {
        $anonymous = (int) ($row['is_anonymous'] ?? 0) === 1;
        $question = [
            'id' => (int) ($row['id'] ?? 0),
            'title' => $this->text($row['title'] ?? ''),
            'body' => $this->text($row['body'] ?? ''),
            'slug' => $this->nullableText($row['slug'] ?? null),
            'status' => $this->status($row['status'] ?? null),
            'content_locale' => $this->locale($row['content_locale'] ?? null),
            'is_anonymous' => $anonymous,
            'views_count' => (int) ($row['views_count'] ?? 0),
            'answers_count' => (int) ($row['answers_count'] ?? 0),
            'votes_count' => (int) ($row['votes_count'] ?? 0),
            'comments_count' => (int) ($row['comments_count'] ?? 0),
            'best_answer_id' => $this->nullableInt($row['best_answer_id'] ?? null),
            'meta_description' => $this->nullableText($row['meta_description'] ?? null),
            'created_at' => $this->nullableText($row['created_at'] ?? null),
            'updated_at' => $this->nullableText($row['updated_at'] ?? null),
            'published_at' => $this->nullableText($row['published_at'] ?? null),
            'answers' => array_map(fn (array $answer): array => $this->answer($answer), $answers),
        ];

        if ($anonymous) {
            $question['author'] = ['name' => 'Anonymous'];
        } else {
            $author = ['name' => $this->text($row['author_name'] ?? $row['user_name'] ?? 'Quaeriva user')];
            $userId = $this->nullableInt($row['user_id'] ?? null);

            if ($userId !== null) {
                $author['id'] = $userId;
                $question['user_id'] = $userId;
            }

            $question['author'] = $author;
        }

        $categoryId = $this->nullableInt($row['category_id'] ?? null);

        if ($categoryId !== null) {
            $question['category'] = [
                'id' => $categoryId,
                'name' => $this->text($row['category_name'] ?? ''),
                'slug' => $this->nullableText($row['category_slug'] ?? null),
            ];
        } else {
            $question['category'] = null;
        }

        return $question;
    }

    private function answer(array $row): array
    {
        $answer = [
            'id' => (int) ($row['id'] ?? 0),
            'body' => $this->text($row['body'] ?? ''),
            'votes_count' => (int) ($row['votes_count'] ?? 0),
            'is_best' => (int) ($row['is_best'] ?? 0) === 1,
            'created_at' => $this->nullableText($row['created_at'] ?? null),
            'updated_at' => $this->nullableText($row['updated_at'] ?? null),
        ];
        $userId = $this->nullableInt($row['user_id'] ?? null);

        if ($userId !== null) {
            $answer['author'] = [
                'id' => $userId,
                'name' => $this->text($row['user_name'] ?? $row['author_name'] ?? 'Quaeriva user'),
            ];
        } else {
            $answer['author'] = ['name' => 'Deleted user'];
        }

        return $answer;
    }

    private function text(mixed $value): string
    {
        $value = trim((string) $value);
        $cleaned = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $value);

        return $cleaned === null ? $value : $cleaned;
    }

    private function nullableText(mixed $value): ?string
    {
        $value = $this->text($value);

        return $value === '' ? null : $value;
    }

    private function nullableInt(mixed $value): ?int
    {
        if ($value === null || $value === '') {
            return null;
        }

        return (int) $value;
    }

    private function locale(mixed $value): string
    {
        if (!is_scalar($value)) return 'ar';
        $locale = strtolower(str_replace('_', '-', trim((string) $value)));
        return preg_match('/^[a-z]{2,3}(?:-[a-z0-9]{2,8})*$/', $locale) === 1 ? $locale : 'ar';
    }

    private function status(mixed $value): string
    {
        return in_array($value, ['pending', 'published', 'rejected', 'closed', 'duplicate'], true)
            ? (string) $value
            : 'published';
    }
}
