<?php

declare(strict_types=1);

namespace PortalPeople\upn\services;

use PortalPeople\shippingcosts\models\ColumnMapForm;
use RuntimeException;

/** Reconstructs the visually stacked UPN fixed-layout PDF into canonical rows. */
final class UpnPdfRateParser
{
    private const PRICE_COLUMNS = ['Micro', 'Quarter', 'Qtr+', 'Half', 'Euro', 'Full'];

    /** @return list<array<string, mixed>> */
    public function parse(string $markdown, string $provider = 'UPN'): array
    {
        $text = $this->extractText($markdown);
        $lines = preg_split('/\R/', $text) ?: [];
        $rows = [];
        $block = [];

        foreach ($lines as $line) {
            if (trim($line) !== '') {
                $block[] = rtrim($line);
            }

            if (preg_match('/\d+\s*-\s*\d+\s*days/i', $line) === 1) {
                $row = $this->parseBlock($block, $provider);
                if ($row !== null) {
                    $rows = array_merge($rows, $row);
                }
                $block = [];
            }
        }

        if ($block !== []) {
            $row = $this->parseBlock($block, $provider);
            if ($row !== null) {
                $rows = array_merge($rows, $row);
            }
        }

        return $rows;
    }

    /** @param list<string> $lines @return list<array<string, mixed>>|null */
    private function parseBlock(array $lines, string $provider): ?array
    {
        $zone = '';
        foreach ($lines as $line) {
            if (preg_match('/^\s*(\d{1,2})\b/', $line, $match) === 1) {
                $zone = $match[1];
            }
        }

        if ($zone === '') {
            return null;
        }

        $transit = '';
        foreach ($lines as $line) {
            if (preg_match('/(\d+\s*-\s*\d+\s*days)/i', $line, $match) === 1) {
                $transit = preg_replace('/\s+/', ' ', trim($match[1])) ?? trim($match[1]);
            }
        }

        $postcodes = $this->postcodes($lines);
        if ($postcodes === '') {
            return null;
        }

        preg_match_all('/\b(\d+\.\s*\d{2})\b/', implode(' ', $lines), $matches);
        $prices = array_map(
            static fn (string $value): float => (float) str_replace(' ', '', $value),
            $matches[1] ?? []
        );
        if (count($prices) < 6) {
            return null;
        }

        $result = [];
        $groups = count($prices) >= 12
            ? [['Next Day', array_slice($prices, 0, 6)], ['Eco', array_slice($prices, -6)]]
            : [['Eco', array_slice($prices, -6)]];

        foreach ($groups as [$service, $values]) {
            $row = [
                'Provider' => $provider,
                'Zone' => $zone,
                'Service' => $this->serviceLabel((string) $service, $transit),
                'TransitTime' => $transit,
                'PostCode' => $postcodes,
            ];
            foreach (self::PRICE_COLUMNS as $index => $column) {
                $row[$column] = $values[$index] ?? '';
            }
            $result[] = $row;
        }

        return $result;
    }

    /** @param list<string> $lines */
    private function postcodes(array $lines): string
    {
        $values = [];
        foreach ($lines as $line) {
            if (preg_match('/\d+\s*-\s*\d+\s*days/i', $line)) {
                continue;
            }
            if (str_contains($line, '£')) {
                $line = (string) preg_replace('/£.*$/', '', $line);
            }
            $value = trim(preg_replace('/^\s*\d{1,2}\s+/', '', $line) ?? $line);
            $value = trim(preg_replace('/\s+/', ' ', $value) ?? $value);
            $match = '/^(?:2026|ADD LOGO|working|Poppypac|Telephone|AHS|First Class|email|'
                . 'PRIORITY|STANDARD|Economy|Pallet|Distribution|Rates|ZONE|POST CODES|'
                . 'MICRO|QUARTER|QTR|HALF|EURO|FULL)/i';
            if ($value === '' || preg_match($match, $value) || preg_match('/[a-z]{3,}/', $value)) {
                continue;
            }
            if (preg_match('/[A-Z]{1,2}(?:\d|\b)/i', $value) === 1) {
                $values[] = $value;
            }
        }

        return implode(' ', array_values(array_unique($values)));
    }

    private function serviceLabel(string $service, string $transit): string
    {
        $label = ColumnMapForm::normalizeType($service);
        return $transit === '' ? $label : $label . ' (' . $transit . ')';
    }

    private function extractText(string $markdown): string
    {
        preg_match_all('/```text\n(.*?)\n```/s', $markdown, $matches);
        if (($matches[1] ?? []) === []) {
            throw new RuntimeException('No fixed-layout text blocks were found in the PDF Markdown.');
        }

        return implode("\n", $matches[1]);
    }
}
