<?php

namespace Drupal\address_tr\Service;

use Drupal\Core\Extension\ExtensionPathResolver;
use Psr\Log\LoggerInterface;

/**
 * Service for resolving Turkish address codes to human-readable labels.
 *
 * Converts stored machine values to their display labels:
 * - TR-34 → İstanbul
 * - KADIKOY → Kadıköy
 * - RASIMPASA-MAHALLESI → Rasimpaşa Mahallesi
 *
 * @package Drupal\address_tr\Service
 */
class AddressLabelResolver
{

    /**
     * The extension path resolver service.
     *
     * @var \Drupal\Core\Extension\ExtensionPathResolver
     */
    protected $extensionPath;

    /**
     * The logger service.
     *
     * @var \Psr\Log\LoggerInterface
     */
    protected $logger;

    /**
     * Cache for loaded subdivision data.
     *
     * @var array
     */
    protected $cache = [];

    /**
     * Constructs an AddressLabelResolver object.
     *
     * @param \Drupal\Core\Extension\ExtensionPathResolver $extension_path
     *   The extension path resolver service.
     * @param \Psr\Log\LoggerInterface $logger
     *   The logger service.
     */
    public function __construct(ExtensionPathResolver $extension_path, LoggerInterface $logger)
    {
        $this->extensionPath = $extension_path;
        $this->logger = $logger;
    }

    /**
     * Gets the label for a Turkish province code.
     *
     * @param string $province_code
     *   The province code (e.g., TR-34).
     *
     * @return string
     *   The province label (e.g., İstanbul), or the original code if not found.
     */
    public function getProvinceLabel(string $province_code): string
    {
        if (empty($province_code)) {
            return '';
        }

        $provinces = $this->loadProvinces();
        if (isset($provinces[$province_code])) {
            return $provinces[$province_code]['name'] ?? $provinces[$province_code]['local_name'] ?? $province_code;
        }

        return $province_code;
    }

    /**
     * Gets the label for a Turkish district code.
     *
     * @param string $province_code
     *   The province code (e.g., TR-34).
     * @param string $district_code
     *   The district code (e.g., KADIKOY).
     *
     * @return string
     *   The district label (e.g., Kadıköy), or the original code if not found.
     */
    public function getDistrictLabel(string $province_code, string $district_code): string
    {
        if (empty($district_code)) {
            return '';
        }

        $districts = $this->loadDistricts($province_code);
        if (isset($districts[$district_code])) {
            return $districts[$district_code]['name'] ?? $districts[$district_code]['local_name'] ?? $district_code;
        }

        return $district_code;
    }

    /**
     * Gets the label for a Turkish neighbourhood code.
     *
     * @param string $province_code
     *   The province code (e.g., TR-34).
     * @param string $district_code
     *   The district code (e.g., KADIKOY).
     * @param string $neighbourhood_code
     *   The neighbourhood code (e.g., RASIMPASA-MAHALLESI).
     *
     * @return string
     *   The neighbourhood label (e.g., Rasimpaşa Mahallesi), or the original code if not found.
     */
    public function getNeighbourhoodLabel(string $province_code, string $district_code, string $neighbourhood_code): string
    {
        if (empty($neighbourhood_code)) {
            return '';
        }

        $neighbourhoods = $this->loadNeighbourhoods($province_code, $district_code);
        if (isset($neighbourhoods[$neighbourhood_code])) {
            return $neighbourhoods[$neighbourhood_code]['name'] ?? $neighbourhood_code;
        }

        return $neighbourhood_code;
    }

    /**
     * Resolves all address field values to their labels.
     *
     * @param array $address
     *   Array with keys: administrative_area, locality, dependent_locality.
     *
     * @return array
     *   Array with the same keys but values replaced with labels.
     */
    public function resolveLabels(array $address): array
    {
        $province_code = $address['administrative_area'] ?? '';
        $district_code = $address['locality'] ?? '';
        $neighbourhood_code = $address['dependent_locality'] ?? '';

        return [
            'administrative_area' => $this->getProvinceLabel($province_code),
            'locality' => $this->getDistrictLabel($province_code, $district_code),
            'dependent_locality' => $this->getNeighbourhoodLabel($province_code, $district_code, $neighbourhood_code),
        ];
    }

    /**
     * Loads provinces data.
     *
     * @return array
     *   Array of province subdivisions keyed by code.
     */
    protected function loadProvinces(): array
    {
        $cache_key = 'provinces';
        if (isset($this->cache[$cache_key])) {
            return $this->cache[$cache_key];
        }

        $module_path = $this->extensionPath->getPath('module', 'address_tr');
        $filepath = $module_path . '/data/TR/provinces.json';

        $data = $this->loadJsonFile($filepath);
        $this->cache[$cache_key] = $data['subdivisions'] ?? [];

        return $this->cache[$cache_key];
    }

    /**
     * Loads districts data for a province.
     *
     * @param string $province_code
     *   The province code (e.g., TR-34).
     *
     * @return array
     *   Array of district subdivisions keyed by code.
     */
    protected function loadDistricts(string $province_code): array
    {
        if (empty($province_code)) {
            return [];
        }

        $cache_key = 'districts:' . $province_code;
        if (isset($this->cache[$cache_key])) {
            return $this->cache[$cache_key];
        }

        $module_path = $this->extensionPath->getPath('module', 'address_tr');
        $filepath = $module_path . '/data/TR/districts/' . $province_code . '.json';

        $data = $this->loadJsonFile($filepath);
        $this->cache[$cache_key] = $data['subdivisions'] ?? [];

        return $this->cache[$cache_key];
    }

    /**
     * Loads neighbourhoods data for a district.
     *
     * @param string $province_code
     *   The province code (e.g., TR-34).
     * @param string $district_code
     *   The district code (e.g., KADIKOY).
     *
     * @return array
     *   Array of neighbourhood subdivisions keyed by code.
     */
    protected function loadNeighbourhoods(string $province_code, string $district_code): array
    {
        if (empty($province_code) || empty($district_code)) {
            return [];
        }

        $cache_key = 'neighbourhoods:' . $province_code . ':' . $district_code;
        if (isset($this->cache[$cache_key])) {
            return $this->cache[$cache_key];
        }

        $module_path = $this->extensionPath->getPath('module', 'address_tr');
        $district_upper = strtoupper($district_code);
        $filepath = $module_path . '/data/TR/neighbourhoods/' . $province_code . '/' . $district_upper . '.json';

        $data = $this->loadJsonFile($filepath);
        $this->cache[$cache_key] = $data['subdivisions'] ?? [];

        return $this->cache[$cache_key];
    }

    /**
     * Loads and parses a JSON file.
     *
     * @param string $filepath
     *   The path to the JSON file.
     *
     * @return array
     *   The parsed JSON data, or empty array on failure.
     */
    protected function loadJsonFile(string $filepath): array
    {
        if (!file_exists($filepath)) {
            return [];
        }

        $raw_content = file_get_contents($filepath);
        if ($raw_content === FALSE) {
            $this->logger->error('Failed to read file: @filepath', ['@filepath' => $filepath]);
            return [];
        }

        $data = json_decode($raw_content, TRUE);
        if ($data === NULL && json_last_error() !== JSON_ERROR_NONE) {
            $this->logger->error('Failed to decode JSON from file: @filepath', ['@filepath' => $filepath]);
            return [];
        }

        return $data;
    }
}
