<?php

/**
 * Copyright since 2007 PrestaShop SA and Contributors
 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is bundled with this package in the file LICENSE.md.
 * It is also available through the world-wide-web at this URL:
 * https://opensource.org/licenses/OSL-3.0
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
 * versions in the future. If you wish to customize PrestaShop for your
 * needs please refer to https://devdocs.prestashop.com/ for more information.
 *
 * @author    PrestaShop SA and Contributors <contact@prestashop.com>
 * @copyright Since 2007 PrestaShop SA and Contributors
 * @license   https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
 */

namespace PrestaShop\Module\PsOnePageCheckout\Form;

use Address;
use Country;
use Symfony\Contracts\Translation\TranslatorInterface;

class OnePageCheckoutFormatter implements \FormFormatterInterface
{
    use AddressFieldsFormatTrait;

    public const FIELD_GROUP_CUSTOMER = 'customer';
    public const FIELD_GROUP_ADDRESS = 'address';

    protected $country;
    protected $translator;
    protected $availableCountries;
    protected $definition;
    /**
     * @var array<string, string>
     */
    private array $fieldGroups = [];

    /**
     * Separate country for the billing (invoice) address section.
     * Null means billing uses the same country as delivery.
     *
     * @var \Country|null
     */
    protected $invoiceCountry;

    public function __construct(
        \Country $country,
        TranslatorInterface $translator,
        array $availableCountries,
    ) {
        $this->country = $country;
        $this->translator = $translator;
        $this->availableCountries = $availableCountries;
        $this->definition = \Address::$definition['fields'];
    }

    public function setCountry(\Country $country)
    {
        $this->country = $country;

        return $this;
    }

    public function getCountry()
    {
        return $this->country;
    }

    /**
     * Set the billing country for invoice field structure generation.
     *
     * @param \Country $country
     *
     * @return self
     */
    public function setInvoiceCountry(\Country $country)
    {
        $this->invoiceCountry = $country;

        return $this;
    }

    public function getFormat()
    {
        $format = [];
        $this->fieldGroups = [];

        // Identity section: email only
        $format['email'] = (new \FormField())
            ->setName('email')
            ->setType('email')
            ->setLabel(
                $this->translator->trans(
                    'Email',
                    [],
                    'Shop.Forms.Labels'
                )
            )
            ->setRequired(true);

        // Opt-in for partner offers (if enabled)
        if (\Configuration::get('PS_CUSTOMER_OPTIN')) {
            $customer = new \Customer();
            $format['optin'] = (new \FormField())
                ->setName('optin')
                ->setType('checkbox')
                ->setLabel(
                    $this->translator->trans(
                        'Receive offers from our partners',
                        [],
                        'Shop.Theme.Customeraccount'
                    )
                )
                ->setRequired($customer->isFieldRequired('optin'));
        }

        $additionalCustomerFormFields = \Hook::exec('additionalCustomerFormFields', ['fields' => &$format], null, true);
        if (is_array($additionalCustomerFormFields)) {
            foreach ($additionalCustomerFormFields as $moduleName => $additionalFormFields) {
                if (!is_array($additionalFormFields)) {
                    continue;
                }

                foreach ($additionalFormFields as $formField) {
                    if (!$formField instanceof \FormField) {
                        continue;
                    }

                    $fieldKey = $moduleName . '_' . $formField->getName();
                    $formField->moduleName = $moduleName;
                    $this->fieldGroups[$fieldKey] = self::FIELD_GROUP_CUSTOMER;
                    $format[$fieldKey] = $formField;
                }
            }
        }

        // Use same address for delivery and invoice
        $format['use_same_address'] = (new \FormField())
            ->setName('use_same_address')
            ->setType('checkbox')
            ->setLabel(
                $this->translator->trans('Use the same address for invoice', [], 'Modules.Onepagecheckout.Shop')
            )
            ->setValue(true);

        // Hidden field to preserve delivery address ID when editing
        $format['id_address_delivery'] = (new \FormField())
            ->setName('id_address_delivery')
            ->setType('hidden');

        // Hidden field to preserve invoice address ID when editing
        $format['id_address_invoice'] = (new \FormField())
            ->setName('id_address_invoice')
            ->setType('hidden');

        // Delivery address fields
        $format = array_merge($format, $this->getAddressFieldsFormat('', false));

        // Invoice address fields (prefixed with invoice_)
        if ($this->invoiceCountry !== null) {
            $deliveryCountry = $this->getCountry();
            $this->setCountry($this->invoiceCountry);
            $format = array_merge($format, $this->getAddressFieldsFormat('invoice_', false));
            $this->setCountry($deliveryCountry);
        } else {
            $format = array_merge($format, $this->getAddressFieldsFormat('invoice_', false));
        }

        $format = $this->sortAddressFields($format);

        // Add constraints and max length
        $format = $this->addConstraints(
            $this->addMaxLength($format)
        );

        $additionalAddressFormFields = \Hook::exec('additionalCustomerAddressFields', ['fields' => &$format], null, true);
        if (is_array($additionalAddressFormFields)) {
            foreach ($additionalAddressFormFields as $moduleName => $additionalFormFields) {
                if (!is_array($additionalFormFields)) {
                    continue;
                }

                foreach ($additionalFormFields as $formField) {
                    if (!$formField instanceof \FormField) {
                        continue;
                    }

                    $fieldKey = $moduleName . '_' . $formField->getName();
                    $formField->moduleName = $moduleName;
                    $this->fieldGroups[$fieldKey] = self::FIELD_GROUP_ADDRESS;
                    $format[$fieldKey] = $formField;

                    $invoiceField = clone $formField;
                    $invoiceField->setName('invoice_' . $formField->getName());
                    $invoiceFieldKey = 'invoice_' . $fieldKey;
                    $this->fieldGroups[$invoiceFieldKey] = self::FIELD_GROUP_ADDRESS;
                    $format[$invoiceFieldKey] = $invoiceField;
                }
            }
        }

        return $format;
    }

    /**
     * Get base field name for definition lookup (strip invoice_ prefix)
     *
     * @param string $name
     *
     * @return string
     */
    protected function getDefinitionKey($name)
    {
        return strpos($name, 'invoice_') === 0 ? substr($name, 8) : $name;
    }

    /**
     * @param array<string, \FormField> $fields
     *
     * @return array<string, \FormField>
     */
    protected function sortAddressFields(array $fields): array
    {
        $customOrder = [
            'id_country' => 1,
            'alias' => 2,
            'firstname' => 3,
            'lastname' => 4,
            'company' => 5,
            'vat_number' => 6,
            'address1' => 7,
            'address2' => 8,
            'city' => 9,
            'postcode' => 10,
            'id_state' => 11,
            'phone' => 12,
        ];

        uksort($fields, static function (string $keyA, string $keyB) use ($customOrder): int {
            $baseA = str_replace('invoice_', '', $keyA);
            $baseB = str_replace('invoice_', '', $keyB);

            $positionA = $customOrder[$baseA] ?? 999;
            $positionB = $customOrder[$baseB] ?? 999;

            return $positionA <=> $positionB;
        });

        return $fields;
    }

    public function getFieldGroup(string $key): ?string
    {
        return $this->fieldGroups[$key] ?? null;
    }
}
