<?php

namespace Hilco\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Throwable;

/**
 * Model Class StagedOrder
 * @property integer id
 * @property string reference_number
 * @property integer division_id
 * @property string order_date
 * @property string order_due_date
 * @property string order_status
 * @property integer soldto_customer_id
 * @property integer billto_customer_id
 * @property integer customershippingaddress_id
 * @property string customer_po
 * @property string order_instructions
 * @property integer salesordersource_id
 * @property integer paymentterm_id
 * @property string currency_code
 * @property integer salesrep_id
 * @property integer plant_id
 * @property string ship_via
 * @property integer shippingpolicytype_id
 * @property integer deliverymethod_id
 * @property integer delivery_priority
 * @property integer deliveryterm_id
 * @property string comment_all
 * @property string comment_external
 * @property string comment_internal
 * @property string comment_display
 * @property string comment_picklist
 * @property string comment_deliverynote
 * @property string comment_manufacturing
 * @property string comment_invoice
 * @property float part_amount
 * @property float order_discount
 * @property float freight_amount
 * @property float tax_amount
 * @property float ins_amount
 * @property float order_amount
 * @property string email
 * @property integer webuser_id
 * @property string order_origin
 * @property integer createdby_id
 * @property integer lastmodifiedby_id
 * @property integer approver_webuser_id
 * @property string date_approved
 * @property string silo_approval_notes
 * @property integer websilo_id
 * @property mixed salesorder_id
 * @property string last_error_message
 * @property string last_exception
 * @property Division division
 * @property SalesRep salesRep
 * @property Customer soldToCustomer
 * @property Customer billToCustomer
 * @property Plant orderShipPlant
 * @property CustomerShippingAddress customerShippingAddress
 * @property CustomerShippingAddress shipToAddress
 * @property CustomerShippingAddress shipToAddressWithTrashed
 * @property DeliveryMethod deliveryMethod
 * @property DeliveryTerm deliveryTerm
 * @property ShippingPolicyType shippingPolicyType
 * @property PaymentTerm paymentTerms
 * @property Collection|StagedOrderLine[] stagedOrderLines
 * @property Collection|StagedOrderLine[] partLines
 * @property-read Collection|StagedOrderLine[] promoDiscountedOrderLines
 * @property-read Collection|StagedOrderLine[] rewardsExcludedOrderLines
 * @property SalesOrderSource salesOrderSource
 * @property Collection|CenposAuthorization[] cenposAuthorizations
 * @property Collection|Promotion_StagedOrder[] promotionStagedOrders
 * @property Order salesOrder
 * @property WebSilo webSilo
 * @method static Builder unprocessed()
 */
class StagedOrder extends StagingModel {
    protected $table = "StagedOrders";
    protected $fillable = [
        'reference_number',
        'division_id',
        'order_date',
        'order_due_date',
        'order_status',
        'soldto_customer_id',
        'billto_customer_id',
        'customershippingaddress_id',
        'customer_po',
        'order_instructions',
        'salesordersource_id',
        'paymentterm_id',
        'currency_code',
        'salesrep_id',
        'plant_id',
        'ship_via',
        'shippingpolicytype_id',
        'deliverymethod_id',
        'delivery_priority',
        'deliveryterm_id',
        'comment_all',
        'comment_external',
        'comment_internal',
        'comment_display',
        'comment_picklist',
        'comment_deliverynote',
        'comment_manufacturing',
        'comment_invoice',
        'part_amount',
        'order_discount',
        'freight_amount',
        'tax_amount',
        'ins_amount',
        'order_amount',
        'email',
        'webuser_id',
        'order_origin',
        'createdby_id',
        'lastmodifiedby_id',
        'approver_webuser_id',
        'date_approved',
        'silo_approval_notes',
        'websilo_id',
    ];
    const UNSUBMITTED = "UNSUBMITTED";
    const PENDING_APPROVAL = "PENDING_APPROVAL";
    const PENDING_WEB = "PENDING_WEB";
    const PENDING = "PENDING";
    const DENIED = "DENIED";
    const B2B_ORDER_ORIGIN = "B2B";

    protected static array $allowedCommentVisibilities = [
        "all",
        "deliverynote",
        "display",
        "external",
        "internal",
        "invoice",
        "manufacturing",
        "picklist",
    ];

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

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

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

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

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

    public function getCustomerPO(): ?string {
        return $this->customer_po;
    }

    public function getOrderInstructions(): ?string {
        return $this->order_instructions;
    }

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

    public function getFreightAmount(): ?float {
        return $this->freight_amount;
    }

    public function getInsuranceAmount(): ?float {
        return $this->ins_amount;
    }

    public function getPartAmount(): ?float {
        return $this->part_amount;
    }

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

    public function getOrderAmount(): ?float {
        return $this->order_amount;
    }

    public function getOrderDiscount(): ?float {
        return $this->order_discount;
    }

    public function getShippingNo(): string {
        return $this->customerShippingAddress->getAddrNo();
    }

    public function getShippingLine1(): string {
        return $this->customerShippingAddress->getAddressLine1();
    }

    public function getShippingLine2(): string {
        return $this->customerShippingAddress->getAddressLine2();
    }

    public function getShippingLine3(): string {
        return $this->customerShippingAddress->getAddressLine3();
    }

    public function getShippingCity(): string {
        return $this->customerShippingAddress->getCity();
    }

    public function getShippingState(): string {
        return $this->customerShippingAddress->getState();
    }

    public function getShippingZip(): string {
        return $this->customerShippingAddress->getPostalCode();
    }

    public function getShippingCountry(): string {
        return $this->customerShippingAddress->getCountryCode();
    }

    public function getShippingAttnOfName(): string {
        return $this->customerShippingAddress->getAttnOfName();
    }

    public function getShipVia(): ?string {
        return $this->ship_via;
    }

    public function getTaxAmount(): ?float {
        return $this->tax_amount;
    }

    public function getComment ($visibility): ?string {
        if (in_array($visibility, self::$allowedCommentVisibilities)) {
            $commentColumn = "comment_".$visibility;
            return $this->$commentColumn;
        }
        return null;
    }

    public function isWebApproved(): bool {
        return $this->order_status !== self::PENDING_WEB && $this->order_status !== self::DENIED;
    }

    /**
     * @param Throwable $throwable
     * @return void
     */
    public function recordLatestException (Throwable $throwable) {
        $this->update([
            'last_error_message' => $throwable->getMessage(),
            'last_exception' => $throwable->getTraceAsString(),
        ]);
    }

    public function getAppliedPromotions(): \Illuminate\Support\Collection {
        return Promotion::whereIn('id', $this->promotionStagedOrders->where('applied', '=', 1)->pluck('promotion_id')->toArray())->get();
    }

    public function approvalReasonStagedOrders(): HasMany {
        return $this->hasMany(ApprovalReason_StagedOrder::class, "stagedorder_id", "id");
    }

    public function approvedByUser() {
        return $this->belongsTo(WebUser::class, 'approver_webuser_id', 'id');
    }

    public function billToCustomer(): BelongsTo {
        return $this->belongsTo(Customer::class, 'billto_customer_id', 'id');
    }

    /**
     * @return HasMany
     */
    public function cenposAuthorizations(): HasMany {
        return $this->hasMany(CenposAuthorization::class, "stagedorder_id", "id");
    }

    public function customerShippingAddress(): BelongsTo {
        return $this->belongsTo(CustomerShippingAddress::class, 'customershippingaddress_id', 'id');
    }

    public function deliveryMethod(): BelongsTo {
        return $this->belongsTo(DeliveryMethod::class, 'deliverymethod_id', 'id');
    }

    public function deliveryTerm(): BelongsTo {
        return $this->belongsTo(DeliveryTerm::class, 'deliveryterm_id', 'id');
    }

    public function division(): BelongsTo {
        return $this->belongsTo(Division::class, 'division_id', 'id');
    }

    public function orderedByUser() {
        return $this->belongsTo(WebUser::class, 'webuser_id', 'id');
    }

    public function orderShipPlant(): BelongsTo {
        return $this->belongsTo(Plant::class, 'plant_id', 'id');
    }

    public function partLines(): HasMany {
        return $this->hasMany(StagedOrderLine::class, "stagedorder_id", "id");
    }

    public function paymentTerms(): BelongsTo {
        return $this->belongsTo(PaymentTerm::class, 'paymentterm_id', 'id');
    }

    public function promotionStagedOrders(): HasMany {
        return $this->hasMany(Promotion_StagedOrder::class, "stagedorder_id", "id");
    }

    public function salesOrderSource(): BelongsTo {
        return $this->belongsTo(SalesOrderSource::class, 'salesordersource_id', 'id');
    }

    /**
     * @return BelongsTo
     */
    public function salesOrder(): BelongsTo {
        return $this->belongsTo(Order::class, "salesorder_id", "id");
    }

    public function salesRep(): BelongsTo {
        return $this->belongsTo(SalesRep::class, 'salesrep_id', 'id');
    }

    public function shippingPolicyType(): BelongsTo {
        return $this->belongsTo(ShippingPolicyType::class, "shippingpolicytype_id", "id");
    }

    public function shipToAddress(): BelongsTo {
        return $this->belongsTo(CustomerShippingAddress::class, 'customershippingaddress_id', 'id');
    }

    public function shipToAddressWithTrashed() {
        return $this->belongsTo(CustomerShippingAddress::class, "customershippingaddress_id", "id")->withTrashed();
    }

    public function soldToCustomer(): BelongsTo {
        return $this->belongsTo(Customer::class, 'soldto_customer_id', 'id');
    }

    /**
     * @return HasMany
     */
    public function stagedOrderLines(): HasMany {
        return $this->hasMany(StagedOrderLine::class, "stagedorder_id", "id");
    }

    public function webSilo(): BelongsTo {
        return $this->belongsTo(WebSilo::class, 'websilo_id', 'id');
    }

    public function webUser(): BelongsTo {
        return $this->belongsTo(WebUser::class, 'webuser_id', 'id');
    }

    public function getHubSpotEventProperties(): array {
        $billTo = $this->billToCustomer;
        $shipToAddress = $this->shipToAddress;
        $soldTo = $this->soldToCustomer;
        return array_merge(
            [
                'bill_to_address_1'     => $billTo->bill_addr_1,
                'bill_to_address_2'     => $billTo->bill_addr_2,
                'bill_to_address_3'     => $billTo->bill_addr_3,
                'bill_to_city'          => $billTo->bill_city,
                'bill_to_country'       => $billTo->bill_country,
                'bill_to_name'          => $billTo->cust_name,
                'bill_to_number'        => $billTo->getCustNo(),
                'bill_to_state'         => $billTo->bill_state,
                'bill_to_zip'           => $billTo->bill_zip_postal,
                'currency_code'         => $this->currency_code,
                'customer_po'           => $this->customer_po,
                'discount_amount'       => $this->order_discount,
                'freight_amount'        => $this->freight_amount,
                'microsite_name'        => $soldTo->webSilo->name,
                'order_date'            => $this->order_date,
                'order_number'          => $this->getReferenceNumber(),
                'order_total'           => $this->order_amount,
                'origin'                => "Web",
                'ship_to_name'          => $shipToAddress->cust_name ?? '',
                'ship_to_number'        => $shipToAddress->addr_no,
                'ship_to_address_1'     => $shipToAddress->addr_1 ?? '',
                'ship_to_address_2'     => $shipToAddress->addr_2 ?? '',
                'ship_to_address_3'     => $shipToAddress->addr_3 ?? '',
                'ship_to_city'          => $shipToAddress->city ?? '',
                'ship_to_state'         => $shipToAddress->state ?? '',
                'ship_to_zip'           => $shipToAddress->postal_cd ?? '',
                'ship_to_country'       => $shipToAddress->country ?? '',
                'sold_to_address_1'     => $soldTo->addr_1,
                'sold_to_address_2'     => $soldTo->addr_2,
                'sold_to_address_3'     => $soldTo->addr_3,
                'sold_to_city'          => $soldTo->city,
                'sold_to_country'       => $soldTo->country,
                'sold_to_name'          => $soldTo->cust_name,
                'sold_to_number'        => $soldTo->getCustNo(),
                'sold_to_state'         => $soldTo->state,
                'sold_to_zip'           => $soldTo->zip_postal,
                'subtotal'              => $this->part_amount,
                'tax_amount'            => $this->tax_amount,
            ],
            $this->stagedOrderLines->first()->getHubSpotEventProperties("Placed Order"),
            $this->getItemNamesForHubSpot()
        );
    }

    private function getItemNamesForHubSpot(): array {
        $index = 0;
        $itemNames = [];
        foreach ($this->stagedOrderLines as $stagedOrderLine) {
            if ($index < 3) {
                $itemNames["item_names_$index"] = $stagedOrderLine->part->part_desc;
                $index++;
            } else {
                break;
            }
        }
        return $itemNames;
    }
}
