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

namespace Simplyin\Payment\Controller\Api;

use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\App\CsrfAwareActionInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Controller\Result\JsonFactory;
use Simplyin\Payment\Helper\ApiClient;
use Psr\Log\LoggerInterface;

/**
 * Update address controller
 *
 * Updates existing address in user's SimplyIN account
 */
class UpdateAddress implements HttpPostActionInterface, CsrfAwareActionInterface
{
    use CsrfAwareApiTrait;

    /**
     * @var JsonFactory
     */
    private $resultJsonFactory;

    /**
     * @var ApiClient
     */
    private $apiClient;

    /**
     * @var RequestInterface
     */
    private $request;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * Constructor
     *
     * @param JsonFactory $resultJsonFactory
     * @param ApiClient $apiClient
     * @param RequestInterface $request
     * @param LoggerInterface $logger
     */
    public function __construct(
        JsonFactory $resultJsonFactory,
        ApiClient $apiClient,
        RequestInterface $request,
        LoggerInterface $logger
    ) {
        $this->resultJsonFactory = $resultJsonFactory;
        $this->apiClient = $apiClient;
        $this->request = $request;
        $this->logger = $logger;
    }

    /**
     * Execute update address action
     *
     * @return \Magento\Framework\Controller\Result\Json
     */
    public function execute()
    {
        $result = $this->resultJsonFactory->create();
        
        try {
            $data = json_decode($this->request->getContent(), true);
            $authToken = $data['authToken'] ?? '';
            $addressId = $data['addressId'] ?? '';
            $address = $data['address'] ?? [];
            $addressType = $data['addressType'] ?? 'shippingAddresses';

            $validationError = $this->validateRequest($authToken, $addressId, $address);
            if ($validationError !== null) {
                return $result->setData($validationError);
            }

            $userDataResponse = $this->apiClient->getUserData($authToken);
            if (!$userDataResponse['success']) {
                return $result->setData([
                    'success' => false,
                    'message' => __('Failed to get user data')
                ]);
            }

            // Handle nested data structure: API returns { data: { ... } }
            $userData = $userDataResponse['data'];
            if (isset($userData['data'])) {
                $userData = $userData['data'];
            }
            
            $updatedAddress = null;
            $userData = $this->applyAddressUpdate($userData, $addressType, $addressId, $address, $updatedAddress);

            if ($userData === null) {
                return $result->setData([
                    'success' => false,
                    'message' => __('Address not found')
                ]);
            }

            $updateResponse = $this->apiClient->updateUserData(['userData' => $userData], $authToken);

            if ($updateResponse['success']) {
                return $result->setData([
                    'success' => true,
                    'message' => __('Address updated successfully'),
                    'address' => $updatedAddress
                ]);
            } else {
                return $result->setData([
                    'success' => false,
                    'message' => $updateResponse['error'] ?? __('Failed to update address')
                ]);
            }
        } catch (\Exception $e) {
            $this->logger->error('SimplyIN UpdateAddress: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString()
            ]);
            return $result->setData([
                'success' => false,
                'message' => __('An unexpected error occurred. Please try again.')
            ]);
        }
    }

    /**
     * Validate the incoming update-address request.
     *
     * @param string $authToken
     * @param string $addressId
     * @param array $address
     * @return array|null Error response payload, or null when the request is valid
     */
    private function validateRequest($authToken, $addressId, array $address): ?array
    {
        if (empty($authToken) || empty($addressId) || empty($address)) {
            return [
                'success' => false,
                'message' => __('Auth token, address ID and address data are required')
            ];
        }

        $validationErrors = $this->validateAddress($address);
        if (!empty($validationErrors)) {
            return [
                'success' => false,
                'message' => __('Validation failed'),
                'errors' => $validationErrors
            ];
        }

        return null;
    }

    /**
     * Validate address data
     *
     * @param array $address
     * @return array
     */
    private function validateAddress($address)
    {
        $errors = [];

        $this->validateNameFields($address, $errors);
        $this->validateLocationFields($address, $errors);

        return $errors;
    }

    /**
     * Validate name-related address fields
     *
     * @param array $address
     * @param array $errors
     * @return void
     */
    private function validateNameFields($address, array &$errors)
    {
        if (isset($address['name']) && empty($address['name'])) {
            $errors['name'] = __('First name cannot be empty');
        }

        if (isset($address['surname']) && empty($address['surname'])) {
            $errors['surname'] = __('Last name cannot be empty');
        }

        if (isset($address['street']) && empty($address['street'])) {
            $errors['street'] = __('Street address cannot be empty');
        }
    }

    /**
     * Validate location-related address fields
     *
     * @param array $address
     * @param array $errors
     * @return void
     */
    private function validateLocationFields($address, array &$errors)
    {
        if (isset($address['city']) && empty($address['city'])) {
            $errors['city'] = __('City cannot be empty');
        }

        if (isset($address['postalCode']) && empty($address['postalCode'])) {
            $errors['postalCode'] = __('Postal code cannot be empty');
        }

        if (isset($address['country']) && empty($address['country'])) {
            $errors['country'] = __('Country cannot be empty');
        }
    }

    /**
     * Find and update an address in the user's address book.
     *
     * @param array $userData
     * @param string $addressType
     * @param mixed $addressId
     * @param array $address
     * @param array|null $updatedAddress Out-param: the merged address when found
     * @return array|null Updated user data, or null when the address was not found
     */
    private function applyAddressUpdate(array $userData, string $addressType, $addressId, array $address, &$updatedAddress): ?array
    {
        if (isset($userData[$addressType])) {
            foreach ($userData[$addressType] as $key => $existingAddress) {
                // Use loose comparison like PrestaShop plugin
                if (isset($existingAddress['_id']) && $existingAddress['_id'] == $addressId) {
                    // phpcs:ignore Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge -- runs once; immediate return after match, not iterative accumulation
                    $userData[$addressType][$key] = array_merge($existingAddress, $address);
                    $updatedAddress = $userData[$addressType][$key];
                    return $userData;
                }
            }
        }

        return null;
    }
}
