<?php

namespace Drupal\address_tr\Service;

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

/**
 * Service for loading Turkish address subdivision data from JSON files.
 *
 * Handles loading of provinces, districts, and neighbourhoods from the
 * module's data directory structure.
 *
 * @package Drupal\address_tr\Service
 */
class SubdivisionLoaderService
{

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

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

    /**
     * Constructs a SubdivisionLoaderService 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;
    }

    /**
     * Loads subdivision data based on parent hierarchy.
     *
     * @param array $parents
     *   Array of parent codes (e.g., ['TR'] or ['TR', 'TR-34']).
     *
     * @return array|null
     *   The subdivision data array, or NULL if not found.
     */
    public function loadSubdivisions(array $parents)
    {
        $level = count($parents);
        $filepath = $this->getFilePath($level, $parents);

        if ($filepath === NULL) {
            $this->logger->warning('Unable to determine file path for subdivision level @level with parents: @parents', [
                '@level' => $level,
                '@parents' => implode(', ', $parents),
            ]);
            return NULL;
        }

        if (!file_exists($filepath) || !is_readable($filepath)) {
            $this->logger->notice('Subdivision file not found: @filepath', [
                '@filepath' => $filepath,
            ]);
            return NULL;
        }

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

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

        return $definitions;
    }

    /**
     * Builds the file path for subdivision data based on hierarchy level.
     *
     * @param int $level
     *   The subdivision level (1 = provinces, 2 = districts, 3 = neighbourhoods).
     * @param array $parents
     *   Array of parent codes.
     *
     * @return string|null
     *   The file path, or NULL if the level is invalid.
     */
    protected function getFilePath(int $level, array $parents)
    {
        if (empty($parents) || $parents[0] !== 'TR') {
            return NULL;
        }

        $module_path = $this->extensionPath->getPath('module', 'address_tr');
        $country_code = $parents[0];

        switch ($level) {
            case 1:
                // Country level - load provinces.
                return $module_path . '/data/' . $country_code . '/provinces.json';

            case 2:
                // District level - load districts for a province.
                if (!isset($parents[1])) {
                    return NULL;
                }
                $province_code = $parents[1];
                if (!$this->isValidPathSegment($province_code)) {
                    $this->logger->warning('Invalid province code detected: @code', ['@code' => $province_code]);
                    return NULL;
                }
                return $module_path . '/data/' . $country_code . '/districts/' . $province_code . '.json';

            case 3:
                // Neighbourhood level - load neighbourhoods for a district.
                if (!isset($parents[1]) || !isset($parents[2])) {
                    return NULL;
                }
                $province_code = $parents[1];
                $district_id = $parents[2];

                if (!$this->isValidPathSegment($province_code)) {
                    $this->logger->warning('Invalid province code detected: @code', ['@code' => $province_code]);
                    return NULL;
                }

                // The district ID from parents might be:
                // 1. Just the key: "KADIKOY"
                // 2. Or the full ID: "TR-34-KADIKOY"
                // Extract just the district name (the part after the last dash,
                // or the whole thing if no dashes).
                $district_name = $district_id;
                if (strpos($district_id, '-') !== FALSE) {
                    // Has dashes, extract the last part.
                    $parts = explode('-', $district_id);
                    $district_name = end($parts);
                }

                if (!$this->isValidPathSegment($district_name)) {
                    $this->logger->warning('Invalid district name detected: @name', ['@name' => $district_name]);
                    return NULL;
                }

                $district_name_upper = strtoupper($district_name);
                return $module_path . '/data/' . $country_code . '/neighbourhoods/' . $province_code . '/' . $district_name_upper . '.json';

            default:
                return NULL;
        }
    }

    /**
     * Validates a path segment to prevent directory traversal.
     *
     * @param string $segment
     *   The path segment to validate.
     *
     * @return bool
     *   TRUE if valid, FALSE otherwise.
     */
    protected function isValidPathSegment($segment)
    {
        // Allow alphanumeric, dashes, and underscores.
        return preg_match('/^[a-zA-Z0-9\-_]+$/', $segment);
    }
}
