<?php

declare(strict_types=1);

namespace App\Business\MasterData;

use DOMDocument;
use DOMXPath;
use JsonException;
use RuntimeException;

/**
 * 读取内置中国省市区县数据集（JSON），供导入服务使用。
 */
final class DberpChinaRegionDatasetReader
{
    /**
     * @return list<array{code: string, name: string}>
     */
    public function loadRows(): array
    {
        $path = $this->resolveDataFilePath();

        if (! is_file($path)) {
            throw new RuntimeException('未找到内置省市区县数据文件：'.$path);
        }

        $raw = file_get_contents($path);

        if ($raw === false) {
            throw new RuntimeException('无法读取内置省市区县数据文件：'.$path);
        }

        try {
            $decoded = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
        } catch (JsonException $exception) {
            throw new RuntimeException('内置省市区县数据文件格式无效：'.$exception->getMessage(), 0, $exception);
        }

        if (! is_array($decoded) || $decoded === []) {
            throw new RuntimeException('内置省市区县数据文件为空或不是有效数组。');
        }

        $rows = [];

        foreach ($decoded as $index => $item) {
            if (! is_array($item)) {
                throw new RuntimeException('内置省市区县数据第 '.($index + 1).' 项格式无效。');
            }

            $code = isset($item['code']) ? trim((string) $item['code']) : '';
            $name = isset($item['name']) ? trim((string) $item['name']) : '';

            if (preg_match('/^\d{6}$/', $code) !== 1 || $name === '') {
                throw new RuntimeException('内置省市区县数据第 '.($index + 1).' 项缺少有效的 code/name。');
            }

            $rows[$code] = [
                'code' => $code,
                'name' => $name,
            ];
        }

        return array_values($rows);
    }

    public function resolveDataFilePath(): string
    {
        $configured = (string) config('dberp.regions.china_import.data_file', '');

        if ($configured !== '') {
            return $configured;
        }

        return database_path('data/china_divisions.json');
    }

    /**
     * 从民政部 HTML 页面解析行（仅用于维护人员刷新内置 JSON 数据集）。
     *
     * @return list<array{code: string, name: string}>
     */
    public function parseHtmlRows(string $html): array
    {
        $dom = new DOMDocument;
        $convertedHtml = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');

        libxml_use_internal_errors(true);
        $dom->loadHTML('<?xml encoding="UTF-8">'.$convertedHtml, LIBXML_NOERROR | LIBXML_NOWARNING);
        libxml_clear_errors();

        $rows = [];
        $xpath = new DOMXPath($dom);

        foreach ($xpath->query('//tr') as $row) {
            $cells = [];

            foreach ($row->childNodes as $cell) {
                if (! in_array($cell->nodeName, ['td', 'th'], true)) {
                    continue;
                }

                $cells[] = $this->normalizeCellText((string) $cell->textContent);
            }

            $codeIndex = null;
            $code = null;

            foreach ($cells as $index => $cell) {
                if (preg_match('/^\d{6}$/', $cell) === 1) {
                    $code = $cell;
                    $codeIndex = $index;

                    break;
                }
            }

            if ($code === null || $codeIndex === null) {
                continue;
            }

            $name = $this->resolveNameFromCells($cells, $codeIndex);

            if ($name === null) {
                continue;
            }

            $rows[$code] = [
                'code' => $code,
                'name' => rtrim($name, '*'),
            ];
        }

        if ($rows === []) {
            throw new RuntimeException('未能从民政部页面解析出地区数据。');
        }

        return array_values($rows);
    }

    /**
     * @param  list<array{code: string, name: string}>  $rows
     */
    public function writeRowsToFile(array $rows, ?string $path = null): string
    {
        $target = $path ?? $this->resolveDataFilePath();
        $directory = dirname($target);

        if (! is_dir($directory) && ! mkdir($directory, 0777, true) && ! is_dir($directory)) {
            throw new RuntimeException('无法创建数据目录：'.$directory);
        }

        $payload = json_encode($rows, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

        if ($payload === false) {
            throw new RuntimeException('无法序列化省市区县数据集。');
        }

        if (file_put_contents($target, $payload.PHP_EOL, LOCK_EX) === false) {
            throw new RuntimeException('无法写入省市区县数据文件：'.$target);
        }

        return $target;
    }

    private function normalizeCellText(string $value): string
    {
        $value = str_replace(["\xc2\xa0", "\xe3\x80\x80", ' ', '　'], ' ', $value);
        $value = preg_replace('/\s+/u', ' ', $value) ?? $value;

        return trim($value);
    }

    /**
     * @param  list<string>  $cells
     */
    private function resolveNameFromCells(array $cells, int $codeIndex): ?string
    {
        for ($index = $codeIndex + 1; $index < count($cells); $index++) {
            $value = $cells[$index];

            if ($value === '' || preg_match('/^\d{6}$/', $value) === 1) {
                continue;
            }

            return $value;
        }

        return null;
    }
}
