<?php

namespace App\Doctor\Entity;

use App\Appointment\Entity\WeeklySchedule;
use App\Auth\Entity\User;
use App\DoctorService\Entity\DoctorService;
use App\Location\Entity\City;
use App\Location\Entity\Province;
use App\Specialty\Entity\Specialty;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Doctor\Repository\DoctorRepository;
use Symfony\Component\Uid\Uuid;

#[ORM\Entity(repositoryClass: DoctorRepository::class)]
#[ORM\Table(name: 'doctors')]
#[ORM\UniqueConstraint(name: 'idx_doctors_user', columns: ['user_id'])]
#[ORM\UniqueConstraint(name: 'uniq_doctors_source_code', columns: ['source', 'medical_system_code'])]
#[ORM\Index(columns: ['active_doctor_appointment'], name: 'idx_doctors_active')]
#[ORM\Index(columns: ['owner_status'], name: 'idx_doctors_owner')]
class Doctor
{
    public const DEGREES = ['expert', 'general', 'specialist', 'subspecialistplus'];
    public const GENDERS = ['man', 'woman'];

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

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

    #[ORM\OneToOne(targetEntity: User::class)]
    #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false, onDelete: 'RESTRICT')]
    private User $user;

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

    #[ORM\Column(type: 'string', length: 10, nullable: true)]
    private ?string $gender = null;

    #[ORM\Column(name: 'medical_system_code', type: 'string', length: 25, nullable: true)]
    private ?string $medicalSystemCode = null;

    #[ORM\Column(name: 'mobile_number', type: 'string', length: 15, nullable: true)]
    private ?string $mobileNumber = null;

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

    #[ORM\Column(type: 'string', length: 30, nullable: true)]
    private ?string $degree = null;

    #[ORM\Column(type: 'text', nullable: true)]
    private ?string $info = null;

    #[ORM\Column(type: 'json', nullable: true)]
    private ?array $images = null;

    #[ORM\Column(name: 'social_media', type: 'json', nullable: true)]
    private ?array $socialMedia = null;

    #[ORM\Column(name: 'doctor_rate', type: 'float')]
    private float $doctorRate = 3.5;

    #[ORM\Column(name: 'doctor_rate_percentage', type: 'float')]
    private float $doctorRatePercentage = 60.0;

    #[ORM\Column(name: 'active_doctor_appointment', type: 'boolean')]
    private bool $activeDoctorAppointment = true;

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

    #[ORM\Column(name: 'notification_mobile', type: 'string', length: 15, nullable: true)]
    private ?string $notificationMobile = null;

    // ── Profile ownership (IRIMC import) ───────────────────────────────────────
    // owner_status: claimed | unclaimed | pending_transfer
    #[ORM\Column(name: 'owner_status', type: 'string', length: 20, options: ['default' => 'claimed'])]
    private string $ownerStatus = 'claimed';

    // source: manual | irimc
    #[ORM\Column(type: 'string', length: 20, options: ['default' => 'manual'])]
    private string $source = 'manual';

    // شناسه رکورد مبدأ (profile_url یا کد نظام پزشکی) برای idempotency و ممیزی
    #[ORM\Column(name: 'source_ref', type: 'string', length: 100, nullable: true)]
    private ?string $sourceRef = null;

    // شناسه کاربری که این پروفایلِ بدون‌مالک را وارد/مدیریت کرده (مثلاً کاربر سیستمی)
    #[ORM\Column(name: 'managed_by', type: 'integer', nullable: true)]
    private ?int $managedBy = null;

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

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

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

    #[ORM\ManyToMany(targetEntity: Specialty::class)]
    #[ORM\JoinTable(
        name: 'doctor_specialties',
        joinColumns: [new ORM\JoinColumn(name: 'doctor_id', referencedColumnName: 'id')],
        inverseJoinColumns: [new ORM\JoinColumn(name: 'specialty_id', referencedColumnName: 'id')]
    )]
    private Collection $specialties;

    #[ORM\ManyToMany(targetEntity: DoctorService::class)]
    #[ORM\JoinTable(
        name: 'doctor_expertise',
        joinColumns: [new ORM\JoinColumn(name: 'doctor_id', referencedColumnName: 'id')],
        inverseJoinColumns: [new ORM\JoinColumn(name: 'service_id', referencedColumnName: 'id')]
    )]
    private Collection $services;

    #[ORM\ManyToMany(targetEntity: Province::class)]
    #[ORM\JoinTable(
        name: 'doctor_provinces',
        joinColumns: [new ORM\JoinColumn(name: 'doctor_id', referencedColumnName: 'id')],
        inverseJoinColumns: [new ORM\JoinColumn(name: 'province_id', referencedColumnName: 'id')]
    )]
    private Collection $provinces;

    #[ORM\ManyToMany(targetEntity: City::class)]
    #[ORM\JoinTable(
        name: 'doctor_cities',
        joinColumns: [new ORM\JoinColumn(name: 'doctor_id', referencedColumnName: 'id')],
        inverseJoinColumns: [new ORM\JoinColumn(name: 'city_id', referencedColumnName: 'id')]
    )]
    private Collection $cities;

    #[ORM\OneToMany(targetEntity: DoctorAddress::class, mappedBy: 'doctor', cascade: ['remove'])]
    private Collection $addresses;

    public function __construct(User $user, string $name)
    {
        $this->uuid        = Uuid::v4()->toRfc4122();
        $this->user        = $user;
        $this->name        = $name;
        $this->createdAt   = time();
        $this->updatedAt   = time();
        $this->specialties = new ArrayCollection();
        $this->services    = new ArrayCollection();
        $this->provinces   = new ArrayCollection();
        $this->cities      = new ArrayCollection();
        $this->addresses   = new ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }
    public function getUuid(): string
    {
        return $this->uuid;
    }
    public function getUser(): User
    {
        return $this->user;
    }
    public function getName(): string
    {
        return $this->name;
    }
    public function getGender(): ?string
    {
        return $this->gender;
    }
    public function getMedicalSystemCode(): ?string
    {
        return $this->medicalSystemCode;
    }
    public function getMobileNumber(): ?string
    {
        return $this->mobileNumber;
    }
    public function getActivityTime(): ?int
    {
        return $this->activityTime;
    }
    public function getDegree(): ?string
    {
        return $this->degree;
    }
    public function getInfo(): ?string
    {
        return $this->info;
    }
    public function getImages(): ?array
    {
        return $this->images;
    }
    public function getSocialMedia(): ?array
    {
        return $this->socialMedia;
    }
    public function getDoctorRate(): float
    {
        return $this->doctorRate;
    }
    public function getDoctorRatePercentage(): float
    {
        return $this->doctorRatePercentage;
    }
    public function isActiveDoctorAppointment(): bool
    {
        return $this->activeDoctorAppointment;
    }
    public function getRepresentationId(): ?int
    {
        return $this->representationId;
    }
    public function getNotificationMobile(): ?string
    {
        return $this->notificationMobile;
    }
    public function getOwnerStatus(): string
    {
        return $this->ownerStatus;
    }
    public function getSource(): string
    {
        return $this->source;
    }
    public function getSourceRef(): ?string
    {
        return $this->sourceRef;
    }
    public function getManagedBy(): ?int
    {
        return $this->managedBy;
    }
    public function getClaimedAt(): ?int
    {
        return $this->claimedAt;
    }
    public function getCreatedAt(): int
    {
        return $this->createdAt;
    }
    public function getUpdatedAt(): int
    {
        return $this->updatedAt;
    }
    public function getSpecialties(): Collection
    {
        return $this->specialties;
    }
    public function getServices(): Collection
    {
        return $this->services;
    }
    public function getProvinces(): Collection
    {
        return $this->provinces;
    }
    public function getCities(): Collection
    {
        return $this->cities;
    }
    public function getAddresses(): Collection
    {
        return $this->addresses;
    }

    public function setName(string $v): self
    {
        $this->name = $v;
        return $this;
    }
    public function setGender(?string $v): self
    {
        $this->gender = $v;
        $this->touch();
        return $this;
    }
    public function setMedicalSystemCode(?string $v): self
    {
        $this->medicalSystemCode = $v;
        $this->touch();
        return $this;
    }
    public function setMobileNumber(?string $v): self
    {
        $this->mobileNumber = $v;
        $this->touch();
        return $this;
    }
    public function setActivityTime(?int $v): self
    {
        $this->activityTime = $v;
        $this->touch();
        return $this;
    }
    public function setDegree(?string $v): self
    {
        $this->degree = $v;
        $this->touch();
        return $this;
    }
    public function setInfo(?string $v): self
    {
        $this->info = $v;
        $this->touch();
        return $this;
    }
    public function setImages(?array $v): self
    {
        $this->images = $v;
        $this->touch();
        return $this;
    }
    public function setSocialMedia(?array $v): self
    {
        $this->socialMedia = $v;
        $this->touch();
        return $this;
    }
    public function setDoctorRate(float $v): self
    {
        $this->doctorRate = $v;
        $this->touch();
        return $this;
    }
    public function setDoctorRatePercentage(float $v): self
    {
        $this->doctorRatePercentage = $v;
        $this->touch();
        return $this;
    }
    public function setActiveDoctorAppointment(bool $v): self
    {
        $this->activeDoctorAppointment = $v;
        $this->touch();
        return $this;
    }
    public function setRepresentationId(?int $v): self
    {
        $this->representationId = $v;
        $this->touch();
        return $this;
    }
    public function setNotificationMobile(?string $v): self
    {
        $this->notificationMobile = $v;
        $this->touch();
        return $this;
    }
    public function setOwnerStatus(string $v): self
    {
        $this->ownerStatus = $v;
        $this->touch();
        return $this;
    }
    public function setSource(string $v): self
    {
        $this->source = $v;
        $this->touch();
        return $this;
    }
    public function setSourceRef(?string $v): self
    {
        $this->sourceRef = $v;
        $this->touch();
        return $this;
    }
    public function setManagedBy(?int $v): self
    {
        $this->managedBy = $v;
        $this->touch();
        return $this;
    }
    public function setClaimedAt(?int $v): self
    {
        $this->claimedAt = $v;
        $this->touch();
        return $this;
    }

    /**
     * انتقال مالکیت پروفایلِ بدون‌مالک به کاربر واقعی پزشک.
     * user_id را پر می‌کند، مدیریت سیستمی را برمی‌دارد و وضعیت را claimed می‌کند.
     */
    public function transferOwnershipTo(User $user): self
    {
        $this->user        = $user;
        $this->managedBy   = null;
        $this->ownerStatus = 'claimed';
        $this->claimedAt   = time();
        $this->touch();
        return $this;
    }

    /** امتیاز فقط برای پروفایل claimed واقعی است؛ unclaimed/pending_transfer مقدار پیش‌فرض جعلی دارد. */
    public function hasPublicRating(): bool
    {
        return $this->ownerStatus === 'claimed';
    }

    private function touch(): void
    {
        $this->updatedAt = time();
    }

    private const DAY_NAMES = ['شنبه', 'یکشنبه', 'دوشنبه', 'سه‌شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه'];

    private const APPOINTMENT_DISABLED_LABEL = 'نوبت‌دهی آنلاین غیرفعال است';

    private function computeScheduleFields(?WeeklySchedule $schedule): array
    {
        $parts = $this->computeScheduleParts($schedule);

        // Online booking enabled flag lives in the weekly schedule meta.
        // When disabled, free_turn reflects that while hours_of_work is kept.
        if ($schedule !== null && !$schedule->getMeta()['online_booking_enabled']) {
            return [
                'free_turn'     => self::APPOINTMENT_DISABLED_LABEL,
                'hours_of_work' => $parts['hours_of_work'],
                'has_schedule'  => false,
            ];
        }

        return $parts;
    }

    private function computeScheduleParts(?WeeklySchedule $schedule): array
    {
        if ($schedule === null) {
            return ['free_turn' => 'نوبت آزادی موجود نیست', 'hours_of_work' => 'برنامه کاری تنظیم نشده', 'has_schedule' => false];
        }

        $setting = $schedule->getSetting();

        // استخراج ساعت‌های هر روز — key: dayIdx، value: رشته ساعت‌ها یا null
        $dayTimes = [];
        for ($i = 0; $i < 7; $i++) {
            $activeSessions = array_values(array_filter(
                $setting[$i]['sessions'] ?? [],
                fn($s) => ($s['active'] ?? false) && !empty($s['start_time'])
            ));
            $dayTimes[$i] = empty($activeSessions)
                ? null
                : implode(' و ', array_map(fn($s) => $s['start_time'] . '–' . $s['end_time'], $activeSessions));
        }

        $hasAnyDay = array_filter($dayTimes) !== [];
        if (!$hasAnyDay) {
            return ['free_turn' => 'نوبت آزادی موجود نیست', 'hours_of_work' => 'برنامه کاری تنظیم نشده', 'has_schedule' => false];
        }

        // گروه‌بندی روزهای متوالی با ساعت یکسان
        // مثال: شنبه–پنجشنبه ۹–۱۳ و ۱۴–۱۸ | جمعه تعطیل
        $groups  = [];
        $current = ['start' => 0, 'times' => $dayTimes[0]];
        for ($i = 1; $i < 7; $i++) {
            if ($dayTimes[$i] === $current['times']) {
                continue; // ادامه همان گروه
            }
            if ($current['times'] !== null) {
                $groups[] = ['start' => $current['start'], 'end' => $i - 1, 'times' => $current['times']];
            }
            $current = ['start' => $i, 'times' => $dayTimes[$i]];
        }
        if ($current['times'] !== null) {
            $groups[] = ['start' => $current['start'], 'end' => 6, 'times' => $current['times']];
        }

        $parts = [];
        foreach ($groups as $g) {
            $parts[] = $g['start'] === $g['end']
                ? self::DAY_NAMES[$g['start']]
                : self::DAY_NAMES[$g['start']] . ' تا ' . self::DAY_NAMES[$g['end']];
        }

        // نزدیک‌ترین روز کاری — PHP date('w'): 0=Sun,6=Sat → ایندکس ایرانی: 0=Sat,...,6=Fri
        $phpDay  = (int) date('w');
        $iranDay = $phpDay === 0 ? 1 : ($phpDay === 6 ? 0 : $phpDay + 1);

        $freeTurn = null;
        for ($i = 0; $i < 7; $i++) {
            $idx = ($iranDay + $i) % 7;
            if ($dayTimes[$idx] !== null) {
                $firstTime = explode(' و ', $dayTimes[$idx])[0];
                $freeTurn  = self::DAY_NAMES[$idx] . ' ' . $firstTime;
                break;
            }
        }

        return [
            'free_turn'     => $freeTurn ?? 'نوبت آزادی موجود نیست',
            'hours_of_work' => implode(' | ', $parts),
            'has_schedule'  => true,
        ];
    }

    public function getExperience(): int
    {
        if ($this->activityTime === null) {
            return 0;
        }
        return max(0, (int)((time() - $this->activityTime) / (365.25 * 24 * 3600)));
    }

    public function toListArray(?WeeklySchedule $schedule = null): array
    {
        $sf = $this->computeScheduleFields($schedule);
        return [
            'id'            => (string) $this->id,
            'uuid'          => $this->uuid,
            'name'          => $this->name,
            'gender'        => $this->gender,
            'degree'        => $this->degree,
            'img'           => $this->images ?? [],
            'specialties'   => array_map(fn(Specialty $s) => [
                'uuid' => $s->getUuid(),
                'id' => (string) $s->getId(),
                'name' => $s->getName(),
            ], $this->specialties->toArray()),
            'satisfaction'  => $this->hasPublicRating() ? (string) $this->doctorRatePercentage : null,
            'point'         => $this->hasPublicRating() ? (string) $this->doctorRate : null,
            'free_turn'     => $sf['free_turn'],
            'hours_of_work' => $sf['hours_of_work'],
            'active'        => $this->activeDoctorAppointment && $sf['has_schedule'],
            'owner_status'  => $this->ownerStatus,
        ];
    }

    public function toDetailArray(?WeeklySchedule $schedule = null): array
    {
        $sf = $this->computeScheduleFields($schedule);
        return [
            'id'                  => (string) $this->id,
            'uuid'                => $this->uuid,
            'name'                => $this->name,
            'gender'              => $this->gender,
            'experience'          => $this->getExperience(),
            'activity_time'       => $this->activityTime !== null ? (string) $this->activityTime : null,
            'medical_system_code' => $this->medicalSystemCode,
            'detail'              => $this->info,
            'degree'              => $this->degree,
            'specialties'         => array_map(fn(Specialty $s) => [
                'uuid' => $s->getUuid(),
                'id' => (string) $s->getId(),
                'name' => $s->getName(),
                'parent_id' => $s->getParent()?->getId() !== null ? (string) $s->getParent()->getId() : null,
            ], $this->specialties->toArray()),
            'active'              => $this->activeDoctorAppointment && $sf['has_schedule'],
            'img'                 => $this->images ?? [],
            'social_media'        => $this->socialMedia,
            'expertise'           => array_map(fn(DoctorService $ds) => [
                'uuid' => $ds->getUuid(),
                'id' => (string) $ds->getId(),
                'name' => $ds->getName(),
            ], $this->services->toArray()),
            'satisfaction'        => $this->hasPublicRating() ? (string) $this->doctorRatePercentage : null,
            'point'               => $this->hasPublicRating() ? (string) $this->doctorRate : null,
            'owner_status'        => $this->ownerStatus,
            'free_turn'           => $sf['free_turn'],
            'hours_of_work'       => $sf['hours_of_work'],
            'address'             => array_map(fn(DoctorAddress $a) => $a->toArray(), $this->addresses->toArray()),
            'average_rate'        => ['total_rates' => null],
            'state'               => array_map(fn(Province $p) => [
                'uuid' => $p->getUuid(),
                'id' => (string) $p->getId(),
                'name' => $p->getName(),
            ], $this->provinces->toArray()),
            'city'                => array_map(fn(City $c) => [
                'uuid'   => $c->getUuid(),
                'id' => (string) $c->getId(),
                'name' => $c->getName(),
                'parent' => $c->getProvince() !== null ? (string) $c->getProvince()->getId() : null,
            ], $this->cities->toArray()),
        ];
    }
}
