<?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 App\Modules\Shipments\DeliveryStatusRepository;
use Throwable;

final class DeliveryStatusesController
{
    private const REDIRECT_STATUSES = '/settings/delivery-statuses?tab=statuses';

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

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

    public function index(Request $request): Response
    {
        $tab = (string) ($request->input('tab', 'statuses') ?? 'statuses');
        if (!in_array($tab, ['statuses', 'mapping'], true)) {
            $tab = 'statuses';
        }

        $data = [
            'title'          => 'Statusy przesyłek',
            'activeMenu'     => 'settings',
            'activeSettings' => 'delivery-statuses',
            'user'           => $this->auth->user(),
            'csrfToken'      => Csrf::token(),
            'tab'            => $tab,
            'statuses'       => $this->deliveryStatusRepository->getAll(),
            'errorMessage'   => (string) Flash::get('ds_error', ''),
            'successMessage' => (string) Flash::get('ds_success', ''),
        ];

        if ($tab === 'mapping') {
            $data = array_merge($data, $this->buildMappingData($request));
        }

        $html = $this->template->render('settings/delivery-statuses', $data, 'layouts/app');

        return Response::html($html);
    }

    public function create(Request $request): Response
    {
        return $this->renderForm(null);
    }

    public function edit(Request $request): Response
    {
        $id = max(0, (int) $request->input('id', 0));
        $row = null;
        foreach ($this->deliveryStatusRepository->getAll() as $candidate) {
            if ((int) ($candidate['id'] ?? 0) === $id) {
                $row = $candidate;
                break;
            }
        }

        if ($row === null) {
            Flash::set('ds_error', 'Status nie istnieje.');
            return Response::redirect(self::REDIRECT_STATUSES);
        }

        if ((int) ($row['is_system'] ?? 0) === 1) {
            Flash::set('ds_error', 'Statusów systemowych nie można edytować.');
            return Response::redirect(self::REDIRECT_STATUSES);
        }

        return $this->renderForm($row);
    }

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

        $key = trim((string) $request->input('key', ''));
        $labelPl = trim((string) $request->input('label_pl', ''));
        $color = trim((string) $request->input('color', '#6c757d'));
        $sortOrder = (int) $request->input('sort_order', 0);
        $isTerminal = $request->input('is_terminal', '') !== '' ? 1 : 0;

        $validationError = $this->validateFields($key, $labelPl, $color, true);
        if ($validationError !== null) {
            Flash::set('ds_error', $validationError);
            return Response::redirect(self::REDIRECT_STATUSES);
        }

        try {
            $this->deliveryStatusRepository->create([
                'key'        => $key,
                'label_pl'   => $labelPl,
                'color'      => $color,
                'sort_order' => $sortOrder,
                'is_terminal' => $isTerminal,
            ]);
            Flash::set('ds_success', 'Status dodany.');
        } catch (Throwable $exception) {
            Flash::set('ds_error', 'Błąd: ' . $exception->getMessage());
        }

        return Response::redirect(self::REDIRECT_STATUSES);
    }

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

        $id = max(0, (int) $request->input('id', 0));
        $labelPl = trim((string) $request->input('label_pl', ''));
        $color = trim((string) $request->input('color', '#6c757d'));
        $sortOrder = (int) $request->input('sort_order', 0);
        $isTerminal = $request->input('is_terminal', '') !== '' ? 1 : 0;

        $validationError = $this->validateFields('valid_key', $labelPl, $color, false);
        if ($validationError !== null) {
            Flash::set('ds_error', $validationError);
            return Response::redirect(self::REDIRECT_STATUSES);
        }

        try {
            $this->deliveryStatusRepository->update($id, [
                'label_pl'   => $labelPl,
                'color'      => $color,
                'sort_order' => $sortOrder,
                'is_terminal' => $isTerminal,
            ]);
            Flash::set('ds_success', 'Status zaktualizowany.');
        } catch (Throwable $exception) {
            Flash::set('ds_error', 'Błąd: ' . $exception->getMessage());
        }

        return Response::redirect(self::REDIRECT_STATUSES);
    }

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

        $id = max(0, (int) $request->input('id', 0));

        try {
            $this->deliveryStatusRepository->delete($id);
            Flash::set('ds_success', 'Status usunięty.');
        } catch (Throwable $exception) {
            Flash::set('ds_error', 'Błąd: ' . $exception->getMessage());
        }

        return Response::redirect(self::REDIRECT_STATUSES);
    }

    /**
     * @param array<string, mixed>|null $row
     */
    private function renderForm(?array $row): Response
    {
        $isEdit = $row !== null && isset($row['id']);

        $html = $this->template->render('settings/delivery-status-form', [
            'title'          => $isEdit ? 'Edytuj status przesyłki' : 'Nowy status przesyłki',
            'activeMenu'     => 'settings',
            'activeSettings' => 'delivery-statuses',
            'user'           => $this->auth->user(),
            'csrfToken'      => Csrf::token(),
            'row'            => $row,
            'isEdit'         => $isEdit,
            'errorMessage'   => (string) Flash::get('ds_error', ''),
        ], 'layouts/app');

        return Response::html($html);
    }

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

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

    private function validateFields(string $key, string $labelPl, string $color, bool $validateKey): ?string
    {
        if ($validateKey) {
            if ($key === '' || !preg_match('/^[a-z][a-z0-9_]{0,49}$/', $key)) {
                return 'Klucz statusu jest nieprawidłowy (tylko małe litery, cyfry, _, max 50 znaków, zaczyna się literą).';
            }
        }

        if ($labelPl === '' || mb_strlen($labelPl) > 100) {
            return 'Etykieta jest wymagana i nie może przekraczać 100 znaków.';
        }

        if (!preg_match('/^#[0-9a-fA-F]{6}$/', $color)) {
            return 'Kolor musi być w formacie #RRGGBB.';
        }

        return null;
    }

    /**
     * @return array<string, mixed>
     */
    private function buildMappingData(Request $request): array
    {
        $provider = strtolower(trim((string) $request->input('provider', 'inpost')));
        if (!isset(self::PROVIDERS[$provider])) {
            $provider = 'inpost';
        }

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

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

        $mappings = [];
        $knownRawStatuses = [];
        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,
            ];
            $knownRawStatuses[$rawStatus] = true;
        }

        foreach ($overrideMap as $rawStatus => $row) {
            if (isset($knownRawStatuses[$rawStatus])) {
                continue;
            }
            $mappings[] = [
                'raw_status'       => $rawStatus,
                'description'      => $row['description'],
                'normalized_status' => $row['normalized_status'],
                'is_custom'        => true,
            ];
            $knownRawStatuses[$rawStatus] = true;
        }

        $unmappedRawStatuses = $this->deliveryStatusMappingRepository->listUnmappedRawStatuses($provider, $knownRawStatuses);

        return [
            'provider'            => $provider,
            'providers'           => self::PROVIDERS,
            'mappings'            => $mappings,
            'unmappedRawStatuses' => $unmappedRawStatuses,
            'normalizedOptions'   => $this->deliveryStatusRepository->getAllAsOptions(),
        ];
    }
}
