<?php

declare(strict_types=1);

namespace App\Module\Transport\Domain\Entity;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use App\Module\Transport\Infrastructure\ApiPlatform\State\Processor\CarrierAddressProcessor;
use App\Shared\Domain\Attribute\Auditable;
use App\Shared\Domain\Contract\BlamableInterface;
use App\Shared\Domain\Contract\TimestampableInterface;
use App\Shared\Domain\Trait\TimestampableBlamableTrait;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Groups;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Adresse d'un transporteur (1:n) — onglet Adresse (M4). Jumelle de
 * SupplierAddress (M2), version simplifiee (pas de type d'adresse, pas de M2M
 * sites/categories sur l'adresse : les sites du M4 vivent dans l'onglet Prix).
 *
 * Lecture : proprietes en `carrier:item:read` (embarquees au detail du
 * transporteur). Ecriture : groupe `carrier:write:addresses`.
 *
 * Sous-ressource API (ERP-159, spec § 4.5) — jumelle de SupplierAddress (M2) /
 * ProviderAddress (M3), sans address_type ni M2M (les sites du M4 vivent dans
 * l'onglet Prix) :
 *  - POST /api/carriers/{carrierId}/addresses : creation rattachee au
 *    transporteur parent (Link toProperty 'carrier'), security
 *    transport.carriers.manage.
 *  - PATCH / DELETE /api/carrier_addresses/{id} : security
 *    transport.carriers.manage.
 *  - GET /api/carrier_addresses/{id} : lecture unitaire (security view) — la
 *    lecture courante reste via le parent. Pas de GET collection autonome.
 * Tout passe par le CarrierAddressProcessor (rattachement parent + RG-4.05).
 *
 * Regles de l'onglet Adresse :
 *  - RG-4.06 : code postal a 4 ou 5 chiffres (Assert\Regex ; pas de controle
 *    CP/ville serveur, l'autocomplete BAN est front).
 *  - RG-4.05 : si le transporteur est affrete (isChartered), l'adresse devient
 *    obligatoire (Pays / CP / Ville / Adresse) — validation conditionnelle portee
 *    par le CarrierAddressProcessor (le parent n'est pas disponible a la
 *    validation Symfony sur un POST sous-ressource en read:false).
 *  - RG-4.07 : masquage du bouton « Valider » si QUALIMAT = front ; le back
 *    accepte le PATCH normalement (aucune garde back specifique).
 *
 * Audite (#[Auditable]) + Timestampable / Blamable.
 */
#[ApiResource(
    operations: [
        new Get(
            security: "is_granted('transport.carriers.view')",
            normalizationContext: ['groups' => ['carrier:item:read', 'default:read']],
        ),
        new Post(
            uriTemplate: '/carriers/{carrierId}/addresses',
            uriVariables: [
                'carrierId' => new Link(fromClass: Carrier::class, toProperty: 'carrier'),
            ],
            // read:false : pas de stade lecture du parent. Le Link toProperty
            // resoudrait l'enfant (SELECT CarrierAddress ... WHERE carrier = :id)
            // et casse en NonUniqueResult des >= 2 enfants. Le parent est rattache
            // manuellement par CarrierAddressProcessor::linkParent (404 si absent).
            read: false,
            security: "is_granted('transport.carriers.manage')",
            normalizationContext: ['groups' => ['carrier:item:read', 'default:read']],
            denormalizationContext: ['groups' => ['carrier:write:addresses']],
            processor: CarrierAddressProcessor::class,
        ),
        new Patch(
            security: "is_granted('transport.carriers.manage')",
            normalizationContext: ['groups' => ['carrier:item:read', 'default:read']],
            denormalizationContext: ['groups' => ['carrier:write:addresses']],
            processor: CarrierAddressProcessor::class,
        ),
        new Delete(
            security: "is_granted('transport.carriers.manage')",
            processor: CarrierAddressProcessor::class,
        ),
    ],
)]
#[ORM\Entity]
#[ORM\Table(name: 'carrier_address')]
#[ORM\Index(name: 'idx_carrier_address_carrier', columns: ['carrier_id'])]
#[ORM\Index(name: 'idx_carrier_address_created_by', columns: ['created_by'])]
#[ORM\Index(name: 'idx_carrier_address_updated_by', columns: ['updated_by'])]
#[Auditable]
class CarrierAddress implements TimestampableInterface, BlamableInterface
{
    use TimestampableBlamableTrait;

    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    #[Groups(['carrier:item:read'])]
    private ?int $id = null;

    #[ORM\ManyToOne(targetEntity: Carrier::class, inversedBy: 'addresses')]
    #[ORM\JoinColumn(name: 'carrier_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
    private ?Carrier $carrier = null;

    #[ORM\Column(length: 80, options: ['default' => 'France'])]
    #[Assert\Length(max: 80, maxMessage: 'Le pays ne peut dépasser {{ limit }} caractères.', normalizer: 'trim')]
    #[Groups(['carrier:item:read', 'carrier:write:addresses'])]
    private string $country = 'France';

    // RG-4.06 : code postal a 4 ou 5 chiffres (pas de controle CP/ville serveur,
    // l'autocomplete BAN est front). Le Regex borne deja la longueur (<= 5) : pas
    // de Length redondant (whitelist EXCLUDED_LENGTH_MIRROR). Nullable : obligatoire
    // seulement si affrete (RG-4.05, garde CarrierAddressProcessor).
    #[ORM\Column(name: 'postal_code', length: 20, nullable: true)]
    #[Assert\Regex(pattern: '/^[0-9]{4,5}$/', message: 'Le code postal doit comporter 4 ou 5 chiffres.')]
    #[Groups(['carrier:item:read', 'carrier:write:addresses'])]
    private ?string $postalCode = null;

    #[ORM\Column(length: 120, nullable: true)]
    #[Assert\Length(max: 120, maxMessage: 'La ville ne peut dépasser {{ limit }} caractères.', normalizer: 'trim')]
    #[Groups(['carrier:item:read', 'carrier:write:addresses'])]
    private ?string $city = null;

    #[ORM\Column(length: 255, nullable: true)]
    #[Assert\Length(max: 255, maxMessage: 'L\'adresse ne peut dépasser {{ limit }} caractères.', normalizer: 'trim')]
    #[Groups(['carrier:item:read', 'carrier:write:addresses'])]
    private ?string $street = null;

    #[ORM\Column(name: 'street_complement', length: 255, nullable: true)]
    #[Assert\Length(max: 255, maxMessage: 'Le complément d\'adresse ne peut dépasser {{ limit }} caractères.', normalizer: 'trim')]
    #[Groups(['carrier:item:read', 'carrier:write:addresses'])]
    private ?string $streetComplement = null;

    #[ORM\Column(options: ['default' => 0])]
    private int $position = 0;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getCarrier(): ?Carrier
    {
        return $this->carrier;
    }

    public function setCarrier(?Carrier $carrier): static
    {
        $this->carrier = $carrier;

        return $this;
    }

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

    public function setCountry(string $country): static
    {
        $this->country = $country;

        return $this;
    }

    public function getPostalCode(): ?string
    {
        return $this->postalCode;
    }

    public function setPostalCode(?string $postalCode): static
    {
        $this->postalCode = $postalCode;

        return $this;
    }

    public function getCity(): ?string
    {
        return $this->city;
    }

    public function setCity(?string $city): static
    {
        $this->city = $city;

        return $this;
    }

    public function getStreet(): ?string
    {
        return $this->street;
    }

    public function setStreet(?string $street): static
    {
        $this->street = $street;

        return $this;
    }

    public function getStreetComplement(): ?string
    {
        return $this->streetComplement;
    }

    public function setStreetComplement(?string $streetComplement): static
    {
        $this->streetComplement = $streetComplement;

        return $this;
    }

    public function getPosition(): int
    {
        return $this->position;
    }

    public function setPosition(int $position): static
    {
        $this->position = $position;

        return $this;
    }
}
