<?php

namespace App\Livewire\Groups;

use App\Domain\Attendance\Enums\AttendanceStatus;
use App\Domain\Attendance\Models\AttendanceRecord;
use App\Domain\Financial\Enums\InvoiceStatus;
use App\Domain\Financial\Models\Invoice;
use App\Domain\Financial\Models\InvoiceItem;
use App\Domain\Financial\Models\PaymentPlan;
use App\Domain\Inventory\Models\Product;
use App\Domain\Financial\Services\ParticipantBillingService;
use App\Domain\Participant\Models\Participant;
use App\Domain\Scheduling\Models\Assignment;
use App\Domain\Training\Enums\SessionStatus;
use App\Domain\Training\Models\Enrollment;
use App\Domain\Training\Models\TrainingGroup;
use App\Domain\Training\Models\TrainingSession;
use App\Models\User;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('تفاصيل المجموعة')]
class GroupShow extends Component
{
    public TrainingGroup $group;
    public string $activeTab = 'overview';
    public ?int $assignTrainerId = null;
    public string $enrollSortBy = 'name_ar';
    public string $enrollSortDir = 'asc';

    public function sortEnrollments(string $column): void
    {
        if ($this->enrollSortBy === $column) {
            $this->enrollSortDir = $this->enrollSortDir === 'asc' ? 'desc' : 'asc';
        } else {
            $this->enrollSortBy = $column;
            $this->enrollSortDir = 'asc';
        }
    }

    public function mount(TrainingGroup $group): void
    {
        $this->authorize('groups.list');

        $this->group = $group->load([
            'program',
            'branch',
            'headTrainer',
            'creator',
            'schedules',
            'schedules.facility',
            'schedules.trainer',
        ]);
    }

    public function assignTrainer(): void
    {
        $this->authorize('groups.update');

        if (!$this->assignTrainerId) {
            return;
        }

        $exists = Assignment::where('assignable_type', TrainingGroup::class)
            ->where('assignable_id', $this->group->id)
            ->where('user_id', $this->assignTrainerId)
            ->where('status', 'active')
            ->exists();

        if ($exists) {
            session()->flash('error', __('هذا المدرب مُعيَّن بالفعل لهذه المجموعة'));
            return;
        }

        Assignment::create([
            'academy_id' => $this->group->academy_id,
            'user_id' => $this->assignTrainerId,
            'assignable_type' => TrainingGroup::class,
            'assignable_id' => $this->group->id,
            'role_label' => 'مدرب',
            'scope' => 'full',
            'status' => 'active',
            'is_primary' => false,
            'start_date' => now()->toDateString(),
            'created_by' => auth()->id(),
        ]);

        $this->assignTrainerId = null;
        session()->flash('success', __('تم تعيين المدرب بنجاح'));
    }

    public function removeTrainer(int $assignmentId): void
    {
        $this->authorize('groups.update');

        Assignment::where('id', $assignmentId)
            ->where('assignable_type', TrainingGroup::class)
            ->where('assignable_id', $this->group->id)
            ->update(['status' => 'cancelled']);

        session()->flash('success', __('تم إلغاء تعيين المدرب'));
    }

    public function render()
    {
        // Active enrollments with participant details
        $enrollQuery = Enrollment::where('enrollments.training_group_id', $this->group->id)
            ->where('enrollments.status', 'active')
            ->with(['participant.person']);

        if ($this->enrollSortBy === 'name_ar') {
            $enrollQuery->join('participants', 'enrollments.participant_id', '=', 'participants.id')
                ->join('people', 'participants.person_id', '=', 'people.id')
                ->orderBy('people.name_ar', $this->enrollSortDir)
                ->select('enrollments.*');
        } elseif ($this->enrollSortBy === 'enrollment_date') {
            $enrollQuery->orderBy('enrollment_date', $this->enrollSortDir);
        } elseif ($this->enrollSortBy === 'payment_status') {
            $enrollQuery->orderBy('payment_status', $this->enrollSortDir);
        } else {
            $enrollQuery->orderBy('enrollment_date', 'asc');
        }

        $activeEnrollments = $enrollQuery->get();

        // Subscription payment status — ONLY checks program subscription, not products
        $participantIds = $activeEnrollments->pluck('participant_id')->toArray();

        $notPaidParticipantIds = [];
        $programName = $this->group->program?->name_ar ?? '';

        foreach ($activeEnrollments as $enrollment) {
            if ($enrollment->participant?->is_free) {
                continue;
            }

            $paymentStatus = $enrollment->payment_status?->value ?? $enrollment->payment_status;

            // Enrollment explicitly marked as paid or waived
            if (in_array($paymentStatus, ['paid', 'waived'])) {
                continue;
            }

            // Check if the enrollment's linked invoice is paid
            if ($enrollment->invoice_id) {
                $invoiceStatus = Invoice::where('id', $enrollment->invoice_id)
                    ->whereNull('deleted_at')
                    ->value('status');

                $paidStatuses = [InvoiceStatus::Paid, InvoiceStatus::Overpaid];
                if ($invoiceStatus && in_array($invoiceStatus, $paidStatuses)) {
                    continue;
                }
            }

            // Check if there's a paid renewal/subscription invoice for current period
            $hasPaidSubscriptionInvoice = Invoice::where('billable_type', Participant::class)
                ->where('billable_id', $enrollment->participant_id)
                ->whereIn('status', [InvoiceStatus::Paid, InvoiceStatus::Overpaid])
                ->whereNull('deleted_at')
                ->when($enrollment->last_billed_at, fn ($q) => $q->where('issue_date', '>=', $enrollment->last_billed_at))
                ->whereHas('items', function ($q) use ($programName) {
                    $q->where('description', 'ilike', '%اشتراك%');
                    if ($programName) {
                        $q->where('description', 'ilike', "%{$programName}%");
                    }
                })
                ->exists();

            if ($hasPaidSubscriptionInvoice) {
                continue;
            }

            $notPaidParticipantIds[] = $enrollment->participant_id;
        }

        $notPaidParticipantIds = array_unique($notPaidParticipantIds);

        // Payment summary: paid, unpaid, free
        $freePlayerIds = $activeEnrollments
            ->filter(fn ($e) => $e->participant?->is_free)
            ->pluck('participant_id')
            ->toArray();
        $freeCount = count($freePlayerIds);
        $nonFreeParticipantIds = array_diff($participantIds, $freePlayerIds);
        $paidCount = count(array_diff($nonFreeParticipantIds, $notPaidParticipantIds));
        $unpaidCount = count(array_intersect($nonFreeParticipantIds, $notPaidParticipantIds));

        // Essential products: who bought / didn't buy each one
        $essentialProducts = Product::where('is_essential', true)
            ->where('is_active', true)
            ->orderBy('sort_order')
            ->get(['id', 'name_ar', 'name']);

        $essentialProductStats = [];
        if ($essentialProducts->isNotEmpty() && $participantIds) {
            $productClass = Product::class;
            foreach ($essentialProducts as $product) {
                $buyerIds = InvoiceItem::where('itemable_type', $productClass)
                    ->where('itemable_id', $product->id)
                    ->whereHas('invoice', function ($q) use ($participantIds) {
                        $q->where('billable_type', Participant::class)
                            ->whereIn('billable_id', $participantIds)
                            ->whereNull('deleted_at')
                            ->whereNotIn('status', [InvoiceStatus::Cancelled]);
                    })
                    ->join('invoices', 'invoice_items.invoice_id', '=', 'invoices.id')
                    ->pluck('invoices.billable_id')
                    ->unique()
                    ->toArray();

                $essentialProductStats[] = [
                    'product' => $product,
                    'bought_count' => count($buyerIds),
                    'not_bought_count' => count($participantIds) - count($buyerIds),
                ];
            }
        }

        // Per-participant installment info (active payment plans)
        $participantInstallments = [];
        if ($participantIds) {
            $plansWithItems = PaymentPlan::whereIn('status', ['active', 'partial'])
                ->whereHas('invoice', function ($q) use ($participantIds) {
                    $q->where('billable_type', Participant::class)
                        ->whereIn('billable_id', $participantIds)
                        ->whereNull('deleted_at');
                })
                ->with(['invoice.items.itemable', 'invoice'])
                ->get();

            foreach ($plansWithItems as $plan) {
                $pid = $plan->invoice->billable_id;
                $itemName = $plan->invoice->items->first()?->itemable?->name_ar
                    ?? $plan->invoice->items->first()?->description
                    ?? __('اشتراك');

                $participantInstallments[$pid][] = [
                    'name' => $itemName,
                    'paid' => $plan->paid_installments,
                    'remaining' => $plan->total_installments - $plan->paid_installments,
                    'total' => $plan->total_installments,
                ];
            }
        }

        // All enrollments count (any status)
        $totalEnrollments = Enrollment::where('training_group_id', $this->group->id)
            ->whereNotIn('status', ['cancelled'])
            ->count();

        // Sessions: recent completed + upcoming scheduled
        $recentSessions = TrainingSession::where('training_group_id', $this->group->id)
            ->where('session_date', '<', now()->toDateString())
            ->orderByDesc('session_date')
            ->limit(20)
            ->get();

        $upcomingSessions = TrainingSession::where('training_group_id', $this->group->id)
            ->where('session_date', '>=', now()->toDateString())
            ->orderBy('session_date')
            ->limit(10)
            ->get();

        // Session stats
        $totalSessions = TrainingSession::where('training_group_id', $this->group->id)->count();
        $completedSessions = TrainingSession::where('training_group_id', $this->group->id)
            ->where('status', SessionStatus::Completed)
            ->count();

        // Attendance stats for the group (across all sessions)
        $sessionIds = TrainingSession::where('training_group_id', $this->group->id)
            ->pluck('id');

        $attendanceQuery = AttendanceRecord::whereIn('training_session_id', $sessionIds);

        $totalAttendanceRecords = (clone $attendanceQuery)->count();
        $presentCount = (clone $attendanceQuery)->where('status', AttendanceStatus::Present)->count();
        $lateCount = (clone $attendanceQuery)->where('status', AttendanceStatus::Late)->count();
        $absentCount = (clone $attendanceQuery)->whereIn('status', [AttendanceStatus::Absent, AttendanceStatus::NoShow])->count();
        $partialCount = (clone $attendanceQuery)->where('status', AttendanceStatus::Partial)->count();
        $excusedCount = (clone $attendanceQuery)->where('status', AttendanceStatus::Excused)->count();
        $cancelledExempt = (clone $attendanceQuery)->whereIn('status', [AttendanceStatus::Cancelled, AttendanceStatus::Exempt])->count();

        $attendanceDenominator = $totalAttendanceRecords - $cancelledExempt;
        $attendanceRate = $attendanceDenominator > 0
            ? round(($presentCount + $lateCount + $partialCount) / $attendanceDenominator * 100, 1)
            : 0;

        // Capacity percentage
        $capacityPercent = $this->group->max_capacity > 0
            ? round(($this->group->current_count / $this->group->max_capacity) * 100, 1)
            : 0;

        // Per-participant attendance rates for enrollments tab
        $participantAttendanceRates = [];
        if ($activeEnrollments->isNotEmpty() && $sessionIds->isNotEmpty()) {
            $participantClass = \App\Domain\Participant\Models\Participant::class;
            foreach ($activeEnrollments as $enrollment) {
                $pQuery = AttendanceRecord::whereIn('training_session_id', $sessionIds)
                    ->where('subject_type', $participantClass)
                    ->where('subject_id', $enrollment->participant_id);

                $pTotal = (clone $pQuery)->count();
                $pExcluded = (clone $pQuery)->whereIn('status', [AttendanceStatus::Cancelled, AttendanceStatus::Exempt])->count();
                $pPositive = (clone $pQuery)->whereIn('status', [AttendanceStatus::Present, AttendanceStatus::Late, AttendanceStatus::Partial])->count();

                $pDenom = $pTotal - $pExcluded;
                $participantAttendanceRates[$enrollment->participant_id] = $pDenom > 0
                    ? round(($pPositive / $pDenom) * 100, 1)
                    : null;
            }
        }

        // Waitlist count
        $waitlistCount = $this->group->waitlist_count ?? 0;

        // Trainer assignments for this group
        $trainerAssignments = Assignment::where('assignable_type', TrainingGroup::class)
            ->where('assignable_id', $this->group->id)
            ->where('status', 'active')
            ->with('user')
            ->get();

        $availableTrainers = User::whereHas('roles', fn ($q) => $q->whereIn('slug', ['trainer', 'head_trainer']))
            ->where('status', 'active')
            ->whereNotIn('id', $trainerAssignments->pluck('user_id')->toArray())
            ->orderBy('name_ar')
            ->get(['id', 'name_ar', 'name']);

        // ---- Money actually collected, per participant -------------------
        //
        // Invoices are routinely bundled (subscription + registration card on
        // one invoice), so a payment cannot simply be read off a line. The
        // billing service allocates each payment across the lines it covers.
        $billing = app(ParticipantBillingService::class);

        $subscriptionPaid = $billing->subscriptionPaid($participantIds);

        // Products this programme requires. Falls back to nothing when the
        // programme has no bundle configured, so groups are unaffected until
        // someone sets one up.
        $bundledProducts = $this->group->program
            ? $this->group->program->bundledProducts()->get()
            : collect();

        $bundleColumns = [];
        foreach ($bundledProducts as $product) {
            $paid = $billing->productPaid($participantIds, $product->id);
            $billed = $billing->productBilled($participantIds, $product->id);

            $rows = [];
            foreach ($participantIds as $pid) {
                $billedAmount = $billed[$pid]['billed'] ?? 0;
                $paidAmount = $paid[$pid] ?? 0;
                $owned = $billedAmount > 0;

                $rows[$pid] = [
                    'owned' => $owned,
                    'billed' => $billedAmount,
                    'paid' => $paidAmount,
                    // Percentage of the product's price actually settled.
                    'percent' => $billedAmount > 0
                        ? min(100, (int) round($paidAmount * 100 / $billedAmount))
                        : 0,
                    'fully_paid' => $owned && $paidAmount >= $billedAmount,
                ];
            }

            $bundleColumns[] = [
                'product' => $product,
                'required' => (bool) ($product->pivot->is_required ?? true),
                'rows' => $rows,
                'missing_count' => count(array_filter($rows, fn ($r) => ! $r['owned'])),
            ];
        }

        // Collected for this group, for the header summary. Only shown to users
        // allowed to see money.
        $groupCollected = array_sum($subscriptionPaid);
        foreach ($bundleColumns as $col) {
            $groupCollected += array_sum(array_column($col['rows'], 'paid'));
        }

        $canSeeFinancials = auth()->user()?->can('invoices.list') ?? false;

        return view('livewire.groups.group-show', [
            'activeEnrollments' => $activeEnrollments,
            'notPaidParticipantIds' => $notPaidParticipantIds,
            'paidCount' => $paidCount,
            'unpaidCount' => $unpaidCount,
            'freeCount' => $freeCount,
            'essentialProductStats' => $essentialProductStats,
            'participantInstallments' => $participantInstallments,
            'subscriptionPaid' => $subscriptionPaid,
            'bundleColumns' => $bundleColumns,
            'groupCollected' => $groupCollected,
            'canSeeFinancials' => $canSeeFinancials,
            'totalEnrollments' => $totalEnrollments,
            'recentSessions' => $recentSessions,
            'upcomingSessions' => $upcomingSessions,
            'totalSessions' => $totalSessions,
            'completedSessions' => $completedSessions,
            'totalAttendanceRecords' => $totalAttendanceRecords,
            'presentCount' => $presentCount,
            'lateCount' => $lateCount,
            'absentCount' => $absentCount,
            'partialCount' => $partialCount,
            'excusedCount' => $excusedCount,
            'attendanceRate' => $attendanceRate,
            'capacityPercent' => $capacityPercent,
            'participantAttendanceRates' => $participantAttendanceRates,
            'waitlistCount' => $waitlistCount,
            'trainerAssignments' => $trainerAssignments,
            'availableTrainers' => $availableTrainers,
        ]);
    }
}
