<?php

namespace App\Services;

use App\Models\SupportCategoryModel;
use App\Models\SupportRequestModel;
use App\Models\UserModel;
use Throwable;

/**
 * Contact Support submissions. Snapshots are stored on support_requests and
 * never written back to the user profile.
 */
class SupportService
{
    public const MSG_SUCCESS          = 'Your support request has been submitted.';
    public const MSG_NAME_REQUIRED    = 'Please enter your profile name.';
    public const MSG_CATEGORY         = 'Please choose how we can help.';
    public const MSG_CATEGORY_INVALID = 'Please choose a valid support topic.';
    public const MSG_MESSAGE          = 'Please enter a message.';
    public const MSG_MESSAGE_LENGTH   = 'Message must be 4000 characters or fewer.';
    public const MSG_NO_CATEGORIES    = 'Support is temporarily unavailable. Please try again later.';
    public const MSG_FAIL             = 'We could not send your request. Please try again.';
    public const MSG_USER             = 'Please sign in.';

    public const MESSAGE_MAX = 4000;

    /** @var list<string> */
    public const APPROVED_TITLES = [
        'Technical Issue',
        'Account/Login Issue',
        'Task Management',
        'Notification Issue',
        'Feedback/Suggestion',
        'Other',
    ];

    public function __construct(
        private readonly SupportCategoryModel $categories = new SupportCategoryModel(),
        private readonly SupportRequestModel $requests = new SupportRequestModel(),
        private readonly UserModel $users = new UserModel(),
        private readonly ProfileService $profiles = new ProfileService(),
    ) {
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function activeCategories(): array
    {
        return $this->categories->activeOrdered();
    }

    /**
     * @param array<string, mixed> $user
     * @param array<string, mixed> $posted
     *
     * @return array{
     *     name_locked: bool,
     *     phone_locked: bool,
     *     email: string,
     *     profile_name: string,
     *     phone: string,
     *     support_category_id: string,
     *     message: string,
     *     categories: list<array<string, mixed>>,
     *     categories_available: bool
     * }
     */
    public function formState(array $user, array $posted = []): array
    {
        $savedName  = trim((string) ($user['profile_name'] ?? ''));
        $savedPhone = trim((string) ($user['phone'] ?? ''));
        $nameLocked = $savedName !== '';
        $phoneLocked = $savedPhone !== '';
        $categories = $this->activeCategories();

        return [
            'name_locked'          => $nameLocked,
            'phone_locked'         => $phoneLocked,
            'email'                => (string) ($user['email'] ?? ''),
            'profile_name'         => $nameLocked ? $savedName : $this->rawString($posted['profile_name'] ?? ''),
            'phone'                => $phoneLocked ? $savedPhone : $this->rawString($posted['phone'] ?? ''),
            'support_category_id'  => $this->rawString($posted['support_category_id'] ?? ''),
            'message'              => $this->rawString($posted['message'] ?? ''),
            'categories'           => $categories,
            'categories_available' => $categories !== [],
        ];
    }

    /**
     * @param array<string, mixed> $post
     *
     * @return array{ok: bool, message: string, errors?: array<string, string>, values?: array<string, mixed>, request?: array<string, mixed>}
     */
    public function submit(int $userId, array $post): array
    {
        $user = $this->users->find($userId);
        if ($user === null) {
            return ['ok' => false, 'message' => self::MSG_USER, 'errors' => ['form' => self::MSG_USER]];
        }

        $state  = $this->formState($user, $post);
        $errors = $this->validate($user, $post, $state);
        if ($errors !== []) {
            return [
                'ok'      => false,
                'message' => $errors['form'] ?? 'Please correct the highlighted fields.',
                'errors'  => $errors,
                'values'  => $state,
            ];
        }

        $name  = $state['name_locked'] ? trim((string) $user['profile_name']) : trim($this->rawString($post['profile_name'] ?? ''));
        $phone = $state['phone_locked']
            ? (trim((string) ($user['phone'] ?? '')) === '' ? null : trim((string) $user['phone']))
            : $this->normalizedPhone($this->rawString($post['phone'] ?? ''));
        $categoryId = (int) $this->rawString($post['support_category_id'] ?? '');
        $message    = trim($this->rawString($post['message'] ?? ''));

        try {
            $id = $this->requests->skipValidation(true)->insert([
                'user_id'             => $userId,
                'profile_name'        => $name,
                'email'               => (string) $user['email'],
                'phone'               => $phone,
                'support_category_id' => $categoryId,
                'message'             => $message,
                'status'              => 'open',
            ], true);
        } catch (Throwable $exception) {
            log_message('error', 'Support request insert failed: {0}', [$exception->getMessage()]);

            return [
                'ok'     => false,
                'message'=> self::MSG_FAIL,
                'errors' => ['form' => self::MSG_FAIL],
                'values' => $state,
            ];
        }

        if (! is_numeric($id) || (int) $id < 1) {
            return [
                'ok'     => false,
                'message'=> self::MSG_FAIL,
                'errors' => ['form' => self::MSG_FAIL],
                'values' => $state,
            ];
        }

        $row = $this->requests->find($id);

        return [
            'ok'      => true,
            'message' => self::MSG_SUCCESS,
            'request' => is_array($row) ? $row : [],
        ];
    }

    /**
     * @param array<string, mixed> $user
     * @param array<string, mixed> $post
     * @param array<string, mixed> $state
     *
     * @return array<string, string>
     */
    public function validate(array $user, array $post, array $state): array
    {
        $errors = [];

        if (! $state['categories_available']) {
            $errors['form']                 = self::MSG_NO_CATEGORIES;
            $errors['support_category_id']  = self::MSG_NO_CATEGORIES;

            return $errors;
        }

        if (! $state['name_locked']) {
            $nameRaw = $this->rawString($post['profile_name'] ?? '');
            $profileErrors = $this->profiles->validate($this->profiles->collect([
                'profile_name' => $nameRaw,
                'age_group'    => '',
                'gender'       => '',
                'occupation'   => '',
                'phone'        => '',
            ]));
            if (trim($nameRaw) === '') {
                $errors['profile_name'] = self::MSG_NAME_REQUIRED;
            } elseif (isset($profileErrors['profile_name'])) {
                $errors['profile_name'] = $profileErrors['profile_name'];
            }
        }

        if (! $state['phone_locked']) {
            $phoneRaw = $this->rawString($post['phone'] ?? '');
            $profileErrors = $this->profiles->validate($this->profiles->collect([
                'profile_name' => '',
                'age_group'    => '',
                'gender'       => '',
                'occupation'   => '',
                'phone'        => $phoneRaw,
            ]));
            if (isset($profileErrors['phone'])) {
                $errors['phone'] = $profileErrors['phone'];
            }
        }

        $categoryId = $this->rawString($post['support_category_id'] ?? '');
        if ($categoryId === '' || ! ctype_digit($categoryId)) {
            $errors['support_category_id'] = self::MSG_CATEGORY;
        } elseif ($this->activeCategory((int) $categoryId) === null) {
            $errors['support_category_id'] = self::MSG_CATEGORY_INVALID;
        }

        $messageRaw = $this->rawString($post['message'] ?? '');
        if (trim($messageRaw) === '') {
            $errors['message'] = self::MSG_MESSAGE;
        } elseif (mb_strlen(trim($messageRaw)) > self::MESSAGE_MAX) {
            $errors['message'] = self::MSG_MESSAGE_LENGTH;
        }

        return $errors;
    }

    public function activeCategory(int $categoryId): ?array
    {
        if ($categoryId < 1) {
            return null;
        }

        $row = $this->categories->find($categoryId);
        if ($row === null || (int) ($row['is_active'] ?? 0) !== 1) {
            return null;
        }

        return $row;
    }

    private function normalizedPhone(string $raw): ?string
    {
        $phone = trim($raw);

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

    private function rawString(mixed $value): string
    {
        if (! is_string($value) && ! is_numeric($value)) {
            return '';
        }

        return (string) $value;
    }
}
