<?php

namespace App\Billing\Entity;

use App\Billing\Repository\InvoiceRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;

#[ORM\Entity(repositoryClass: InvoiceRepository::class)]
#[ORM\Table(name: 'invoices')]
#[ORM\Index(columns: ['entity_type', 'entity_id'], name: 'idx_invoice_tenant')]
#[ORM\Index(columns: ['patient_session_id'], name: 'idx_invoice_session')]
class Invoice
{
    public const STATUS_DRAFT     = 'draft';
    public const STATUS_FINALIZED = 'finalized';
    public const STATUS_PAID      = 'paid';
    public const STATUS_VOID      = 'void';

    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;

    #[ORM\Column(type: 'string', length: 36, unique: true)]
    private string $uuid;

    #[ORM\Column(name: 'entity_type', type: 'string', length: 10)]
    private string $entityType;

    #[ORM\Column(name: 'entity_id', type: 'integer')]
    private int $entityId;

    #[ORM\Column(name: 'patient_session_id', type: 'integer', nullable: true)]
    private ?int $patientSessionId = null;

    #[ORM\Column(name: 'patient_record_id', type: 'integer', nullable: true)]
    private ?int $patientRecordId = null;

    #[ORM\Column(name: 'base_insurance_id', type: 'integer', nullable: true)]
    private ?int $baseInsuranceId = null;

    #[ORM\Column(name: 'supplementary_insurance_id', type: 'integer', nullable: true)]
    private ?int $supplementaryInsuranceId = null;

    #[ORM\Column(name: 'total_rials', type: 'integer')]
    private int $totalRials = 0;

    #[ORM\Column(name: 'base_insurance_rials', type: 'integer')]
    private int $baseInsuranceRials = 0;

    #[ORM\Column(name: 'supplementary_rials', type: 'integer')]
    private int $supplementaryRials = 0;

    #[ORM\Column(name: 'patient_rials', type: 'integer')]
    private int $patientRials = 0;

    #[ORM\Column(type: 'string', length: 15)]
    private string $status = self::STATUS_DRAFT;

    #[ORM\Column(name: 'issued_at', type: 'integer')]
    private int $issuedAt;

    #[ORM\Column(name: 'created_at', type: 'integer')]
    private int $createdAt;

    #[ORM\Column(name: 'updated_at', type: 'integer')]
    private int $updatedAt;

    #[ORM\OneToMany(targetEntity: InvoiceItem::class, mappedBy: 'invoice', cascade: ['persist', 'remove'], orphanRemoval: true)]
    private Collection $items;

    public function __construct(string $entityType, int $entityId)
    {
        $this->uuid       = Uuid::v4()->toRfc4122();
        $this->entityType = $entityType;
        $this->entityId   = $entityId;
        $this->issuedAt   = time();
        $this->createdAt  = time();
        $this->updatedAt  = time();
        $this->items      = new ArrayCollection();
    }

    public function getId(): ?int           { return $this->id; }
    public function getUuid(): string       { return $this->uuid; }
    public function getEntityType(): string { return $this->entityType; }
    public function getEntityId(): int      { return $this->entityId; }
    public function getStatus(): string     { return $this->status; }
    public function getBaseInsuranceId(): ?int          { return $this->baseInsuranceId; }
    public function getSupplementaryInsuranceId(): ?int { return $this->supplementaryInsuranceId; }
    public function getTotalRials(): int    { return $this->totalRials; }
    public function getPatientRials(): int  { return $this->patientRials; }
    /** @return Collection<int, InvoiceItem> */
    public function getItems(): Collection  { return $this->items; }

    public function setPatientSessionId(?int $v): self { $this->patientSessionId = $v; return $this; }
    public function setPatientRecordId(?int $v): self  { $this->patientRecordId = $v; return $this; }
    public function setBaseInsuranceId(?int $v): self          { $this->baseInsuranceId = $v; return $this; }
    public function setSupplementaryInsuranceId(?int $v): self { $this->supplementaryInsuranceId = $v; return $this; }

    public function addItem(InvoiceItem $item): self
    {
        $this->items->add($item);
        $item->attachTo($this);
        return $this;
    }

    public function recalculateTotals(): void
    {
        $total = $base = $supp = $patient = 0;
        foreach ($this->items as $item) {
            $total   += $item->getTotalRials();
            $base    += $item->getBaseInsuranceRials();
            $supp    += $item->getSupplementaryRials();
            $patient += $item->getPatientRials();
        }
        $this->totalRials         = $total;
        $this->baseInsuranceRials = $base;
        $this->supplementaryRials = $supp;
        $this->patientRials       = $patient;
        $this->updatedAt          = time();
    }

    public function finalize(): void
    {
        if ($this->status === self::STATUS_DRAFT) {
            $this->status   = self::STATUS_FINALIZED;
            $this->updatedAt = time();
        }
    }

    public function toArray(): array
    {
        return [
            'uuid'                       => $this->uuid,
            'entity_type'                => $this->entityType,
            'entity_id'                  => $this->entityId,
            'patient_session_id'         => $this->patientSessionId,
            'patient_record_id'          => $this->patientRecordId,
            'base_insurance_id'          => $this->baseInsuranceId,
            'supplementary_insurance_id' => $this->supplementaryInsuranceId,
            'total_rials'                => $this->totalRials,
            'base_insurance_rials'       => $this->baseInsuranceRials,
            'supplementary_rials'        => $this->supplementaryRials,
            'patient_rials'              => $this->patientRials,
            'status'                     => $this->status,
            'issued_at'                  => $this->issuedAt,
            'items'                      => array_map(fn(InvoiceItem $i) => $i->toArray(), $this->items->toArray()),
        ];
    }
}
