<?php
declare(strict_types=1);

namespace App\Modules\Settings;

use App\Core\Http\Request;
use App\Core\Http\Response;
use App\Core\I18n\Translator;
use App\Core\Security\Csrf;
use App\Core\Support\Flash;
use App\Core\View\Template;
use App\Modules\Auth\AuthService;
use App\Modules\Shipments\DeliveryStatus;
use App\Modules\Shipments\DeliveryStatusMappingRepository;
use Throwable;

final class DeliveryStatusMappingController
{
    private const REDIRECT_PATH = '/settings/delivery-status-mappings';

    private const PROVIDERS = [
        'inpost' => 'InPost',
        'apaczka' => 'Apaczka',
        'allegro_wza' => 'Allegro',
    ];

    public function __construct(
        private readonly Template $template,
        private readonly Translator $translator,
        private readonly AuthService $auth,
        private readonly DeliveryStatusMappingRepository $repository
    ) {
    }

    public function index(Request $request): Response
    {
        $provider = strtolower(trim((string) $request->input('provider', 'inpost')));
        if (!isset(self::PROVIDERS[$provider])) {
            $provider = 'inpost';
        }

        $defaults = DeliveryStatus::getDefaultMappings($provider);
        $overrides = $this->repository->listByProvider($provider);

        $overrideMap = [];
        foreach ($overrides as $row) {
            $overrideMap[$row['raw_status']] = $row;
        }

        $mappings = [];
        foreach ($defaults as $rawStatus => $default) {
            $isCustom = isset($overrideMap[$rawStatus]);
            $mappings[] = [
                'raw_status' => $rawStatus,
                'description' => $isCustom ? $overrideMap[$rawStatus]['description'] : $default['description'],
                'normalized_status' => $isCustom ? $overrideMap[$rawStatus]['normalized_status'] : $default['normalized'],
                'is_custom' => $isCustom,
            ];
        }

        $html = $this->template->render('settings/delivery-status-mappings', [
            'title' => 'Mapowanie statusów dostawy',
            'activeMenu' => 'settings',
            'activeSettings' => 'delivery-status-mappings',
            'user' => $this->auth->user(),
            'csrfToken' => Csrf::token(),
            'provider' => $provider,
            'providers' => self::PROVIDERS,
            'mappings' => $mappings,
            'normalizedOptions' => DeliveryStatus::LABEL_PL,
            'errorMessage' => (string) Flash::get('dsm_error', ''),
            'successMessage' => (string) Flash::get('dsm_success', ''),
        ], 'layouts/app');

        return Response::html($html);
    }

    public function save(Request $request): Response
    {
        $csrfError = $this->validateCsrf((string) $request->input('_token', ''));
        if ($csrfError !== null) {
            return $csrfError;
        }

        $provider = strtolower(trim((string) $request->input('provider', '')));
        $rawStatus = trim((string) $request->input('raw_status', ''));
        $normalizedStatus = trim((string) $request->input('normalized_status', ''));
        $description = trim((string) $request->input('description', ''));

        if ($provider === '' || $rawStatus === '') {
            Flash::set('dsm_error', 'Brakuje wymaganych pól.');
            return Response::redirect(self::REDIRECT_PATH . '?provider=' . rawurlencode($provider));
        }

        if (!in_array($normalizedStatus, DeliveryStatus::ALL_STATUSES, true)) {
            Flash::set('dsm_error', 'Nieprawidłowy status znormalizowany.');
            return Response::redirect(self::REDIRECT_PATH . '?provider=' . rawurlencode($provider));
        }

        try {
            $this->repository->upsertMapping($provider, $rawStatus, $normalizedStatus, $description);
            Flash::set('dsm_success', 'Mapowanie zapisane.');
        } catch (Throwable $exception) {
            Flash::set('dsm_error', 'Błąd zapisu: ' . $exception->getMessage());
        }

        return Response::redirect(self::REDIRECT_PATH . '?provider=' . rawurlencode($provider));
    }

    public function saveBulk(Request $request): Response
    {
        $csrfError = $this->validateCsrf((string) $request->input('_token', ''));
        if ($csrfError !== null) {
            return $csrfError;
        }

        $provider = strtolower(trim((string) $request->input('provider', '')));
        $rawStatuses = $request->input('raw_status', []);
        $normalizedStatuses = $request->input('normalized_status', []);
        $descriptions = $request->input('description', []);

        if (!is_array($rawStatuses) || !is_array($normalizedStatuses) || !is_array($descriptions)) {
            Flash::set('dsm_error', 'Nieprawidłowe dane formularza.');
            return Response::redirect(self::REDIRECT_PATH . '?provider=' . rawurlencode($provider));
        }

        try {
            $changed = 0;
            $defaults = DeliveryStatus::getDefaultMappings($provider);

            foreach ($rawStatuses as $index => $rawStatus) {
                $rawStatus = trim((string) $rawStatus);
                if ($rawStatus === '') {
                    continue;
                }

                $normalizedStatus = trim((string) ($normalizedStatuses[$index] ?? ''));
                $description = trim((string) ($descriptions[$index] ?? ''));

                if (!in_array($normalizedStatus, DeliveryStatus::ALL_STATUSES, true)) {
                    continue;
                }

                $default = $defaults[$rawStatus] ?? null;
                $isDefault = $default !== null
                    && $normalizedStatus === $default['normalized']
                    && $description === $default['description'];

                if ($isDefault) {
                    $this->repository->deleteMapping($provider, $rawStatus);
                } else {
                    $this->repository->upsertMapping($provider, $rawStatus, $normalizedStatus, $description);
                    $changed++;
                }
            }

            Flash::set('dsm_success', 'Zapisano zmiany (' . $changed . ' niestandardowych mapowań).');
        } catch (Throwable $exception) {
            Flash::set('dsm_error', 'Błąd zapisu: ' . $exception->getMessage());
        }

        return Response::redirect(self::REDIRECT_PATH . '?provider=' . rawurlencode($provider));
    }

    public function reset(Request $request): Response
    {
        $csrfError = $this->validateCsrf((string) $request->input('_token', ''));
        if ($csrfError !== null) {
            return $csrfError;
        }

        $provider = strtolower(trim((string) $request->input('provider', '')));
        $rawStatus = trim((string) $request->input('raw_status', ''));

        if ($provider === '' || $rawStatus === '') {
            Flash::set('dsm_error', 'Brakuje wymaganych pól.');
            return Response::redirect(self::REDIRECT_PATH . '?provider=' . rawurlencode($provider));
        }

        try {
            $this->repository->deleteMapping($provider, $rawStatus);
            Flash::set('dsm_success', 'Przywrócono domyślne mapowanie.');
        } catch (Throwable $exception) {
            Flash::set('dsm_error', 'Błąd resetu: ' . $exception->getMessage());
        }

        return Response::redirect(self::REDIRECT_PATH . '?provider=' . rawurlencode($provider));
    }

    public function resetAll(Request $request): Response
    {
        $csrfError = $this->validateCsrf((string) $request->input('_token', ''));
        if ($csrfError !== null) {
            return $csrfError;
        }

        $provider = strtolower(trim((string) $request->input('provider', '')));
        if ($provider === '') {
            Flash::set('dsm_error', 'Brakuje wymaganych pól.');
            return Response::redirect(self::REDIRECT_PATH);
        }

        try {
            $this->repository->deleteAllByProvider($provider);
            Flash::set('dsm_success', 'Przywrócono wszystkie domyślne mapowania.');
        } catch (Throwable $exception) {
            Flash::set('dsm_error', 'Błąd resetu: ' . $exception->getMessage());
        }

        return Response::redirect(self::REDIRECT_PATH . '?provider=' . rawurlencode($provider));
    }

    private function validateCsrf(string $token): ?Response
    {
        if (Csrf::validate($token)) {
            return null;
        }

        Flash::set('dsm_error', $this->translator->get('auth.errors.csrf_expired'));
        return Response::redirect(self::REDIRECT_PATH);
    }
}
