<?php
/**
 * A withdrawal declaration as submitted by the consumer.
 *
 * @package SkyServers\Retragere
 */

declare(strict_types=1);

namespace SkyServers\Retragere\Core;

/**
 * Immutable. The text produced by declarationText() is the document that
 * has to be reproduced in full inside the acknowledgement email.
 */
final class WithdrawalRequest
{
    public const TYPE_LIBERA   = 'libera';
    public const TYPE_PARTIALA = 'partiala';
    public const TYPE_TOTALA   = 'totala';

    /** @var array<string,string> */
    private $fields;

    /** @var RequestLine[] */
    private $lines;

    /** @var int */
    private $orderId;

    /** @var string */
    private $type;

    /**
     * @param array<string,string> $fields
     * @param RequestLine[]        $lines
     */
    private function __construct(array $fields, array $lines, int $orderId, string $type)
    {
        $this->fields  = $fields;
        $this->lines   = array_values($lines);
        $this->orderId = $orderId;
        $this->type    = $type;
    }

    /**
     * @param array<string,string> $fields        Clean fields from RequestValidator.
     * @param RequestLine[]        $lines         Selected order lines, may be empty.
     * @param int                  $orderId       0 when no order could be attached.
     * @param int                  $orderTotalQty Total units the order holds.
     */
    public static function create(array $fields, array $lines, int $orderId, int $orderTotalQty): self
    {
        $selected = 0;
        foreach ($lines as $line) {
            $selected += $line->qty();
        }

        if (0 === $orderId || 0 === $selected) {
            $type = self::TYPE_LIBERA;
        } elseif ($orderTotalQty > 0 && $selected >= $orderTotalQty) {
            $type = self::TYPE_TOTALA;
        } else {
            $type = self::TYPE_PARTIALA;
        }

        return new self($fields, $lines, $orderId, $type);
    }

    public function name(): string
    {
        return (string) ($this->fields['name'] ?? '');
    }

    public function email(): string
    {
        return (string) ($this->fields['email'] ?? '');
    }

    public function contractRef(): string
    {
        return (string) ($this->fields['contract_ref'] ?? '');
    }

    public function reason(): string
    {
        return (string) ($this->fields['reason'] ?? '');
    }

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

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

    /**
     * @return RequestLine[]
     */
    public function lines(): array
    {
        return $this->lines;
    }

    public function totalQty(): int
    {
        $total = 0;
        foreach ($this->lines as $line) {
            $total += $line->qty();
        }

        return $total;
    }

    /**
     * The declaration in the exact wording that gets stored and echoed back.
     */
    public function declarationText(): string
    {
        $parts = [];

        $parts[] = 'Prin prezenta declar că mă retrag din contractul încheiat la distanță '
            . 'având ca obiect vânzarea următoarelor bunuri.';
        $parts[] = '';
        $parts[] = 'Nume și prenume: ' . $this->name();
        $parts[] = 'Adresa de email: ' . $this->email();
        $parts[] = 'Identificarea contractului: ' . $this->contractRef();

        if ([] !== $this->lines) {
            $parts[] = '';
            $parts[] = 'Produse vizate de retragere:';
            foreach ($this->lines as $line) {
                $label = $line->name();

                // The SKU outlives catalogue edits: a renamed product still
                // resolves to the exact item that was returned.
                if ('' !== $line->sku()) {
                    $label .= ' (cod produs: ' . $line->sku() . ')';
                }

                $parts[] = '- ' . $label . ' x ' . $line->qty();
            }
        }

        if ('' !== $this->reason()) {
            $parts[] = '';
            $parts[] = 'Motiv (opțional, neobligatoriu prin lege): ' . $this->reason();
        }

        return implode("\n", $parts);
    }
}
