<?php

declare(strict_types=1);

namespace PortalPeople\palletforce\services;

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

/** Reconstructs Palletforce fixed-layout PDF rows into canonical importer rows. */
final class PalletforcePdfRateParser
{
    private const PRICE_COLUMNS = ['Qtr', 'Half', 'Full', '2', '3', '4', '5', '5+ per pallet'];

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

        foreach ($lines as $line) {
            if ($this->isServiceLine($line)) {
                if ($activeService !== null) {
                    $row = $this->parseServiceBlock(
                        $activeMetadata,
                        $activeLines,
                        $activeService,
                        $provider
                    );
                    if ($row !== null) {
                        $rows[] = $row;
                    }
                }
                $activeMetadata = $metadata;
                $activeService = rtrim($line);
                $activeLines = [$activeService];
                continue;
            }

            if (trim($line) !== '') {
                $line = rtrim($line);
                if (str_contains($line, '£') || stripos($line, 'p.o.a') !== false) {
                    $activeLines[] = $line;
                } else {
                    $metadata[] = $line;
                }
            }
        }

        if ($activeService !== null) {
            $row = $this->parseServiceBlock($activeMetadata, $activeLines, $activeService, $provider);
            if ($row !== null) {
                $rows[] = $row;
            }
        }

        return $rows;
    }

    private function isServiceLine(string $line): bool
    {
        return preg_match('/\b(24hr|48hr|72hr)\b/i', $line) === 1;
    }

    /**
     * @param list<string> $metadata
     * @param list<string> $priceLines
     **/
    private function parseServiceBlock(
        array $metadata,
        array $priceLines,
        string $serviceLine,
        string $provider
    ): ?array {
        $band = '';
        $bandIndex = null;
        foreach ($metadata as $index => $line) {
            if (preg_match('/^\s*(Local|\d{2})\b/i', $line, $match) === 1) {
                $band = $match[1];
                $bandIndex = $index;
            }
        }
        if ($band === '' || $bandIndex === null) {
            return null;
        }

        $postcodeLines = [];
        foreach (array_slice($metadata, $bandIndex + 1) as $line) {
            $value = trim(preg_replace('/\s+/', ' ', $line) ?? '');
            if ($value !== '' && !preg_match('/^(?:Band|Postcodes|Service|Qtr|Half|Full|per pallet)/i', $value)) {
                $postcodeLines[] = $value;
            }
        }
        $postcode = implode(' ', array_values(array_unique($postcodeLines)));
        if ($postcode === '') {
            return null;
        }

        $priceText = implode(' ', $priceLines);
        $isPoa = stripos($priceText, 'p.o.a') !== false || stripos($priceText, 'poa') !== false;
        preg_match_all('/\b(\d+\.\s*\d{2})\b/', $priceText, $matches);
        $prices = array_map(
            static fn (string $value): float => (float) str_replace(' ', '', $value),
            $matches[1] ?? []
        );
        if (!$isPoa && count($prices) < count(self::PRICE_COLUMNS)) {
            return null;
        }

        $service = preg_match('/\b(24hr|48hr|72hr)\b/i', $serviceLine, $match)
            ? strtolower($match[1])
            : '';
        $row = [
            'Provider' => $provider,
            'Band' => $band,
            'PostCode' => $postcode,
            'Service' => ColumnMapForm::normalizeType($service),
            'source_service' => $service,
            'requires_review' => $isPoa,
        ];
        foreach (self::PRICE_COLUMNS as $index => $column) {
            $row[$column] = $isPoa ? '' : ($prices[$index] ?? '');
        }

        return $row;
    }

    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]);
    }
}
