<?php

/**
 * Copyright 2024-2025 Simply.IN Sp. z o.o.
 *
 * NOTICE OF LICENSE
 *
 * Licensed under the EUPL-1.2 or later.
 * You may not use this work except in compliance with the Licence.
 *
 * Copy of the Licence is available at:
 * https://joinup.ec.europa.eu/software/page/eupl
 * It is bundled with this package in the file LICENSE.txt
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the Licence is distributed on an as is basis,
 * without warranties or conditions of any kind, either express or implied.
 * Check the Licence for the specific language governing permissions
 * and limitations under the License.
 *
 * @author   Simply.IN Sp. z o.o.
 * @copyright 2024-2025 Simply.IN Sp. z o.o.
 * @license   https://joinup.ec.europa.eu/software/page/eupl
 */

declare(strict_types=1);

namespace SimplyIn\Ajax\Service;

use Address;
use Configuration;
use Country;
use Db;
use PrestaShopLogger;
use Throwable;

if (!defined('_PS_VERSION_')) {
    exit;
}

class AddressService
{
    public function __construct(private readonly Db $db)
    {
    }

    public function getDefaultCountryIso(): string
    {
        $country = new Country((int) Configuration::get('PS_COUNTRY_DEFAULT'));

        return (string) $country->iso_code;
    }

    /**
     * @param array<string, mixed> $addressData
     */
    public function findExistingAddressId(int $idCustomer, array $addressData): false|int
    {
        $sql = 'SELECT id_address, alias, firstname, lastname, address1, postcode, city,
                       id_country, vat_number, company
                FROM ' . _DB_PREFIX_ . 'address
                WHERE id_customer = ' . $idCustomer . '
                    AND deleted = 0';
        $result = $this->db->executeS($sql);
        if (!is_array($result) || $result === []) {
            return false;
        }

        $taxId = (string) ($addressData['taxId'] ?? '');
        // Compare the same string the write path stores, instead of splitting address1 back into
        // street/apartment: that split assumed a single-word street, so "Prosta 16" (street with
        // the number already in it) or any two-word street never matched and every selection
        // inserted another copy of the same address.
        $candidateAddress1 = $this->composeAddressLine($addressData);
        foreach ($result as $row) {
            if (
                strcasecmp((string) $row['alias'], (string) ($addressData['addressName'] ?? '')) === 0
                && strcasecmp((string) $row['firstname'], (string) ($addressData['name'] ?? '')) === 0
                && strcasecmp((string) $row['lastname'], (string) ($addressData['surname'] ?? '')) === 0
                && strcasecmp((string) $row['address1'], $candidateAddress1) === 0
                && strcasecmp((string) $row['postcode'], (string) ($addressData['postalCode'] ?? '')) === 0
                && strcasecmp((string) $row['city'], (string) ($addressData['city'] ?? '')) === 0
                && (int) $row['id_country'] === (int) ($addressData['country'] ?? 0)
                && strcasecmp((string) $row['vat_number'], $taxId) === 0
                && strcasecmp((string) $row['company'], (string) ($addressData['companyName'] ?? '')) === 0
            ) {
                return (int) $row['id_address'];
            }
        }

        return false;
    }

    /**
     * The single definition of how street + apartment become PrestaShop's address1 — used both
     * when writing a new address and when looking for an existing one, so the two can never drift.
     *
     * @param array<string, mixed> $addressData
     */
    private function composeAddressLine(array $addressData): string
    {
        $apartment = trim((string) ($addressData['appartmentNumber'] ?? ''));
        $apartmentPart = $apartment !== '' ? ' ' . $apartment : '';

        return substr(((string) ($addressData['street'] ?? '')) . $apartmentPart, 0, 128);
    }

    /**
     * @param array<string, mixed> $addressData
     */
    public function createPsAddress(int $idCustomer, array $addressData, string $phone): false|int
    {
        $addressData['country'] = Country::getByIso('PL');
        $existing = $this->findExistingAddressId($idCustomer, $addressData);
        if ($existing) {
            return $existing;
        }

        $address = new Address();
        $address->id_customer = $idCustomer;
        $address->alias = substr((string) ($addressData['addressName'] ?? ''), 0, 32);
        $address->firstname = substr((string) ($addressData['name'] ?? ''), 0, 255);
        $address->lastname = substr((string) ($addressData['surname'] ?? ''), 0, 255);
        $address->address1 = $this->composeAddressLine($addressData);
        $address->postcode = (string) ($addressData['postalCode'] ?? '');
        $address->city = substr((string) ($addressData['city'] ?? ''), 0, 64);
        $address->id_country = (int) $addressData['country'];
        $address->vat_number = (string) ($addressData['taxId'] ?? '');
        $address->company = (string) ($addressData['companyName'] ?? '');
        $address->phone = $phone;

        return $address->save() ? (int) $address->id : false;
    }

    public function updateDeliveryAddressOnCartLines(int $idAddressDelivery, int $idCart): void
    {
        $this->db->execute(
            'UPDATE `' . _DB_PREFIX_ . 'cart_product`
             SET id_address_delivery = ' . $idAddressDelivery . '
             WHERE id_cart = ' . $idCart,
        );
        $this->db->execute(
            'UPDATE `' . _DB_PREFIX_ . 'customization`
             SET id_address_delivery = ' . $idAddressDelivery . '
             WHERE id_cart = ' . $idCart,
        );
    }

    public function deleteOtherGuestAddresses(int $idCustomer, int $keepInvoiceId, int $keepDeliveryId): void
    {
        $sql = 'SELECT id_address FROM `' . _DB_PREFIX_ . 'address`
                WHERE id_customer = ' . $idCustomer . '
                    AND id_address != ' . $keepInvoiceId . '
                    AND id_address != ' . $keepDeliveryId;
        $rows = $this->db->executeS($sql);
        if (!is_array($rows)) {
            return;
        }
        foreach ($rows as $row) {
            try {
                (new Address((int) $row['id_address']))->delete();
            } catch (Throwable $e) {
                PrestaShopLogger::addLog(
                    'SimplyIn: failed to delete guest address ' . $row['id_address'] . ' — ' . $e->getMessage(),
                    2,
                    null,
                    'SimplyIn',
                    null,
                    true,
                );
            }
        }
    }
}
