<?php

namespace App\Services;

use App\Models\Billing;
use App\Models\Customer;
use App\Repositories\BillingRepository;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;

class BillingService
{
    protected BillingRepository $repo;

    protected ActivityLogService $activityLog;

    public function __construct(BillingRepository $repo, ActivityLogService $activityLog)
    {
        $this->repo = $repo;
        $this->activityLog = $activityLog;
    }

    public function create(array $data): Billing
    {
        $data['created_by'] = $data['created_by'] ?? Auth::id();
        $billing = $this->repo->create($data);

        $this->activityLog->log([
            'action' => 'billing.created',
            'description' => 'Billing created for customer '.$billing->customer_id.' amount '.$billing->amount,
            'metadata' => ['billing_id' => $billing->id],
        ]);

        return $billing;
    }

    public function update(Billing $billing, array $data): Billing
    {
        $billing = $this->repo->update($billing, $data);

        $this->activityLog->log([
            'action' => 'billing.updated',
            'description' => 'Billing updated '.$billing->id,
            'metadata' => ['billing_id' => $billing->id],
        ]);

        return $billing;
    }

    public function delete(Billing $billing): bool
    {
        $result = $this->repo->delete($billing);

        $this->activityLog->log([
            'action' => 'billing.deleted',
            'description' => 'Billing deleted '.$billing->id,
            'metadata' => ['billing_id' => $billing->id],
        ]);

        return $result;
    }

    public function generateMonthlyBillingsForAll(?Carbon $billingDate = null): void
    {
        $billingDate = $billingDate ?? Carbon::now();

        Customer::where('status', 1)
            ->with('package')
            ->chunkById(100, function ($customers) use ($billingDate) {
                foreach ($customers as $customer) {
                    $this->generateMonthlyBillingForCustomer($customer, $billingDate);
                }
            });
    }

    public function generateMonthlyBillingForCustomer(Customer $customer, ?Carbon $billingDate = null): ?Billing
    {
        $billingDate = $billingDate ?? Carbon::now();
        $billingMonth = $billingDate->month;
        $billingYear = $billingDate->year;

        $existing = Billing::withTrashed()->where('customer_id', $customer->id)
            ->where('billing_month', $billingMonth)
            ->where('billing_year', $billingYear)
            ->first();

        if ($existing) {
            return $existing;
        }

        $amount = (float) $customer->monthly_bill > 0
            ? (float) $customer->monthly_bill
            : (float) ($customer->package?->price ?? 0);
        $description = 'Monthly billing generated for '.$billingDate->format('F Y');

        $previousDue = Billing::where('customer_id', $customer->id)
            ->where(function ($q) use ($billingYear, $billingMonth) {
                $q->where('billing_year', '<', $billingYear)
                    ->orWhere(function ($q) use ($billingYear, $billingMonth) {
                        $q->where('billing_year', $billingYear)
                            ->where('billing_month', '<', $billingMonth);
                    });
            })
            ->whereIn('status', ['pending', 'partial'])
            ->sum(DB::raw('COALESCE(due_amount, amount)'));

        $totalCredits = $customer->accounts()->whereIn('type', [1, 99])->sum('amount');

        $totalBilling = $customer->billings()->sum('amount');
        $advanceBalance = max(0, $totalCredits - $totalBilling);

        // Previous bills keep their own outstanding balance; do not charge it twice.
        $paidAmount = min($advanceBalance, $amount);
        $dueAmount = max(0, $amount - $paidAmount);
        $status = $dueAmount <= 0 ? 'paid' : ($paidAmount > 0 ? 'partial' : 'pending');

        $billing = Billing::firstOrCreate([
            'customer_id' => $customer->id,
            'billing_month' => $billingMonth,
            'billing_year' => $billingYear,
        ], [
            'invoice_id' => null,
            'amount' => $amount,
            'previous_due' => $previousDue,
            'paid_amount' => $paidAmount,
            'due_amount' => $dueAmount,
            'due_date' => $billingDate->copy()->endOfMonth()->toDateString(),
            'status' => $status,
            'description' => $description,
            'created_by' => Auth::id(),
        ]);

        if ($billing->wasRecentlyCreated) {
            $this->activityLog->log([
                'action' => 'billing.monthly_generated',
                'description' => 'Monthly billing generated for customer '.$customer->id.' for '.$billingDate->format('F Y'),
                'metadata' => ['billing_id' => $billing->id, 'customer_id' => $customer->id],
            ]);
        }

        return $billing;
    }

    public function generateInvoice(Billing $billing): void
    {
        if (! $billing->invoice_id) {
            $billing->forceFill(['invoice_id' => $billing->id])->save();
        }

        $this->activityLog->log([
            'action' => 'billing.invoice_generated',
            'description' => 'Printable invoice generated for billing '.$billing->id,
            'metadata' => ['billing_id' => $billing->id],
        ]);
    }
}
