<?php
namespace PortalPeople\DisabledPostcodes\Model;

use Magento\Framework\App\ResourceConnection;

class PostcodeMatcher
{
    private ResourceConnection $resource;
    private Config $config;

    public function __construct(ResourceConnection $resource, Config $config)
    {
        $this->resource = $resource;
        $this->config = $config;
    }

    public function normalizePostcode(string $postcode): string
    {
        $normalized = strtoupper(trim($postcode));
        $normalized = preg_replace('/\\s+/', '', $normalized) ?: '';
        return preg_replace('/[^A-Z0-9]/', '', $normalized) ?: '';
    }

    public function validatePostcode(string $postcode): void
    {
        $normalized = $this->normalizePostcode($postcode);
        if ($normalized === '' || strlen($normalized) > 16) {
            throw new \InvalidArgumentException('Postcode is invalid.');
        }
    }

    public function validateMatchType(string $matchType): string
    {
        $type = strtolower(trim($matchType));
        if (!in_array($type, ['exact', 'area'], true)) {
            throw new \InvalidArgumentException('Match type must be exact or area.');
        }
        return $type;
    }

    public function getRulesByStore(int $storeId): array
    {
        $connection = $this->resource->getConnection();
        $table = $this->resource->getTableName('portalpeople_disabled_postcode');

        $select = $connection->select()
            ->from($table, ['entity_id', 'postcode', 'normalized_postcode', 'match_type', 'description', 'store_id'])
            ->where('is_active = ?', 1)
            ->where('store_id IN (?)', [0, $storeId])
            ->order('store_id DESC');

        $rows = $connection->fetchAll($select);
        $exact = [];
        $areas = [];

        foreach ($rows as $row) {
            if ($row['match_type'] === 'exact') {
                $exact[] = $row['normalized_postcode'];
            } else {
                $areas[] = $row['normalized_postcode'];
            }
        }

        return [
            'exact' => array_values(array_unique($exact)),
            'areas' => array_values(array_unique($areas)),
        ];
    }

    public function checkPostcode(string $postcode, int $storeId): array
    {
        if (!$this->config->isEnabled($storeId)) {
            return [
                'is_excluded' => false,
                'normalized_postcode' => $this->normalizePostcode($postcode),
                'message' => '',
                'match_type' => null,
                'matched_rule' => null,
            ];
        }

        $normalized = $this->normalizePostcode($postcode);
        if ($normalized === '') {
            return [
                'is_excluded' => false,
                'normalized_postcode' => $normalized,
                'message' => '',
                'match_type' => null,
                'matched_rule' => null,
            ];
        }

        $rules = $this->getRulesByStore($storeId);
        if (in_array($normalized, $rules['exact'], true)) {
            return [
                'is_excluded' => true,
                'normalized_postcode' => $normalized,
                'message' => $this->config->getBlockedMessage($storeId),
                'match_type' => 'exact',
                'matched_rule' => $normalized,
            ];
        }

        foreach ($rules['areas'] as $area) {
            if ($area !== '' && strpos($normalized, $area) === 0) {
                return [
                    'is_excluded' => true,
                    'normalized_postcode' => $normalized,
                    'message' => $this->config->getBlockedMessage($storeId),
                    'match_type' => 'area',
                    'matched_rule' => $area,
                ];
            }
        }

        return [
            'is_excluded' => false,
            'normalized_postcode' => $normalized,
            'message' => '',
            'match_type' => null,
            'matched_rule' => null,
        ];
    }
}
