<?php

namespace App\Services;

use App\Models\EmployeeLedger;
use Illuminate\Support\Carbon;

class EmployeeLedgerService
{
    public static function post(
        int $userId,
        int $employeeId,
        string $type,
        string $referenceType,
        int $referenceId,
        float $debit = 0,
        float $credit = 0,
        ?string $description = null,
        ?string $referenceNo = null,
        $entryDate = null
    ): EmployeeLedger {
        $entry = EmployeeLedger::create([
            'user_id'        => $userId,
            'employee_id'    => $employeeId,
            'entry_date'     => self::normalizeEntryDate($entryDate),
            'type'           => $type,
            'reference_type' => $referenceType,
            'reference_id'   => $referenceId,
            'reference_no'   => self::normalizeReferenceNo($referenceNo),
            'description'    => $description,
            'debit'          => round((float) $debit, 2),
            'credit'         => round((float) $credit, 2),
            'balance'        => 0,
        ]);

        self::recalculateBalances($userId, $employeeId);

        return $entry->fresh();
    }

    public static function deleteByReference(
        int $userId,
        string $referenceType,
        int $referenceId
    ): void {
        $entries = EmployeeLedger::query()
            ->where('user_id', $userId)
            ->where('reference_type', $referenceType)
            ->where('reference_id', $referenceId)
            ->orderBy('entry_date')
            ->orderBy('id')
            ->get();

        if ($entries->isEmpty()) {
            return;
        }

        $employeeIds = $entries->pluck('employee_id')->unique()->values();

        EmployeeLedger::query()
            ->where('user_id', $userId)
            ->where('reference_type', $referenceType)
            ->where('reference_id', $referenceId)
            ->delete();

        foreach ($employeeIds as $employeeId) {
            self::recalculateBalances($userId, (int) $employeeId);
        }
    }

    public static function recalculateBalances(int $userId, int $employeeId): void
    {
        $rows = EmployeeLedger::query()
            ->where('user_id', $userId)
            ->where('employee_id', $employeeId)
            ->orderBy('entry_date')
            ->orderBy('id')
            ->get();

        $running = 0;

        foreach ($rows as $row) {
            // credit = company owes employee (salary generated)
            // debit  = company paid employee (salary paid / deduction)
            $running += (float) $row->credit;
            $running -= (float) $row->debit;

            $row->balance = round($running, 2);
            $row->saveQuietly();
        }
    }

    /*
    |--------------------------------------------------------------------------
    | SALARY FLOW (KEEP THIS ✅)
    |--------------------------------------------------------------------------
    */

    public static function postSalaryGenerated($payrollItem): EmployeeLedger
    {
        return self::post(
            userId: (int) $payrollItem->user_id,
            employeeId: (int) $payrollItem->employee_id,
            type: 'salary_generated',
            referenceType: 'payroll_item',
            referenceId: (int) $payrollItem->id,
            debit: 0,
            credit: (float) $payrollItem->net_salary,
            description: 'Salary generated',
            referenceNo: 'SAL-GEN-' . $payrollItem->id,
            entryDate: $payrollItem->generated_at ?? now()
        );
    }

    public static function postSalaryPaid($payment): EmployeeLedger
    {
        return self::post(
            userId: (int) $payment->user_id,
            employeeId: (int) $payment->employee_id,
            type: 'salary_paid',
            referenceType: 'payroll_payment',
            referenceId: (int) $payment->id,
            debit: (float) $payment->amount,
            credit: 0,
            description: 'Salary paid',
            referenceNo: $payment->reference_no ?? ('SAL-PAID-' . $payment->id),
            entryDate: $payment->paid_at ?? now()
        );
    }

    protected static function normalizeReferenceNo(?string $referenceNo): ?string
    {
        $referenceNo = is_null($referenceNo) ? null : trim($referenceNo);

        return $referenceNo !== '' ? $referenceNo : null;
    }

    protected static function normalizeEntryDate($entryDate): string
    {
        if (blank($entryDate)) {
            return now()->toDateTimeString();
        }

        if ($entryDate instanceof \DateTimeInterface) {
            return Carbon::instance($entryDate)->toDateTimeString();
        }

        return Carbon::parse($entryDate)->toDateTimeString();
    }
}