<?php
/**
 * Copyright © Simplyin. All rights reserved.
 */
declare(strict_types=1);

namespace Simplyin\Payment\Model\Resolver;

use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Simplyin\Payment\Helper\ApiClient;
use Simplyin\Payment\Service\ParcelLockerFilter;
use Psr\Log\LoggerInterface;

/**
 * Resolver: simplyinGetParcelLockers query
 */
class GetParcelLockers extends AbstractResolver
{
    /**
     * @var ApiClient
     */
    private ApiClient $apiClient;

    /**
     * @var ParcelLockerFilter
     */
    private ParcelLockerFilter $parcelLockerFilter;

    /**
     * @param ApiClient $apiClient
     * @param LoggerInterface $logger
     * @param ParcelLockerFilter $parcelLockerFilter
     */
    public function __construct(ApiClient $apiClient, LoggerInterface $logger, ParcelLockerFilter $parcelLockerFilter)
    {
        parent::__construct($logger);
        $this->apiClient = $apiClient;
        $this->parcelLockerFilter = $parcelLockerFilter;
    }

    /**
     * @inheritdoc
     */
    public function resolve(Field $field, $context, ResolveInfo $info, ?array $value = null, ?array $args = null): array
    {
        $authToken = trim($args['auth_token'] ?? '');
        if (empty($authToken)) {
            throw new GraphQlInputException(__('auth_token is required.'));
        }

        $action            = $args['action'] ?? 'getUserLockers';
        $availableProviders = $this->apiClient->getAvailableParcelLockerProviders();

        try {
            if ($action === 'getClosest') {
                return $this->getClosest($args, $authToken, $availableProviders);
            }
            return $this->getUserLockers($authToken, $availableProviders);
        } catch (\Exception $e) {
            $this->logger->error('SimplyIN GraphQL GetParcelLockers: ' . $e->getMessage());
            return $this->errorOutput('An unexpected error occurred. Please try again.');
        }
    }

    /**
     * Get user's saved parcel lockers
     *
     * @param string $authToken
     * @param array $availableProviders
     * @return array
     */
    private function getUserLockers(string $authToken, array $availableProviders): array
    {
        $response = $this->apiClient->getUserData($authToken);
        if (!$response['success']) {
            return $this->errorOutput($response['error'] ?? 'Failed to get user data');
        }

        $raw = $response['data'];
        if (isset($raw['data'])) {
            $raw = $raw['data'];
        }

        $lockers  = $raw['parcelLockers'] ?? [];
        $filtered = $this->parcelLockerFilter->filterByProviders($lockers, $availableProviders);

        return $this->buildOutput($filtered, $availableProviders);
    }

    /**
     * Get closest parcel lockers by coordinates
     *
     * @param array $args
     * @param string $authToken
     * @param array $availableProviders
     * @return array
     */
    private function getClosest(array $args, string $authToken, array $availableProviders): array
    {
        $coords = $args['coordinates'] ?? [];
        $lat    = $coords['lat'] ?? null;
        $lng    = $coords['lng'] ?? null;

        if ($lat === null || $lng === null) {
            return $this->errorOutput('coordinates (lat, lng) are required for getClosest action');
        }

        // Fail-closed short-circuit: with nothing configured the result is empty
        // regardless of the backend, so skip the API call entirely.
        if (empty($availableProviders)) {
            return $this->buildOutput([], $availableProviders);
        }

        $providers = !empty($args['providers']) ? $args['providers'] : $availableProviders;
        $response  = $this->apiClient->getClosestParcelLockers(
            (float)$lat,
            (float)$lng,
            $providers,
            (int)($args['limit'] ?? 20),
            (int)($args['radius'] ?? 5000),
            $authToken
        );

        if (!$response['success']) {
            return $this->errorOutput($response['error'] ?? 'Failed to get closest parcel lockers');
        }

        // Filter the backend result down to the configured providers (defense in
        // depth beyond acceptedParcelLockerProviders) — mirrors the saved-lockers
        // path and the WooCommerce/PrestaShop behaviour.
        $filtered = $this->parcelLockerFilter->filterByProviders($response['data'] ?? [], $availableProviders);

        return $this->buildOutput($filtered, $availableProviders);
    }

    /**
     * Build the GraphQL output array from raw locker data
     *
     * @param array $lockers
     * @param array $availableProviders
     * @return array
     */
    private function buildOutput(array $lockers, array $availableProviders): array
    {
        $machines = [];
        $points   = [];

        foreach ($lockers as $locker) {
            if (!is_array($locker)) {
                continue;
            }
            $item = [
                'locker_id'     => $locker['lockerId'] ?? $locker['_id'] ?? null,
                'provider_name' => $locker['providerName'] ?? null,
                'name'          => $locker['name'] ?? null,
                'address'       => $locker['address'] ?? null,
                'city'          => $locker['city'] ?? null,
                'postal_code'   => $locker['postalCode'] ?? null,
                'service_type'  => $locker['service_type'] ?? null,
                'raw_json'      => json_encode($locker),
            ];

            if (($locker['service_type'] ?? '') === 'parcel_machine') {
                $machines[] = $item;
            } else {
                $points[] = $item;
            }
        }

        return [
            'success'             => true,
            'message'             => 'Parcel lockers retrieved successfully',
            'parcel_machines'     => $machines,
            'pickup_points'       => $points,
            'available_providers' => $availableProviders,
        ];
    }
}
