<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\PrePayment;
use App\Models\User;
use App\Services\AuditService;
use App\Services\Finance\PrePaymentInstallmentService;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class PrePaymentController extends Controller
{
    public function __construct(
        protected AuditService $audit,
        protected PrePaymentInstallmentService $installments
    ) {
        $this->middleware('finance.operational');
    }

    public function index(Request $request)
    {
        $this->authorize('viewAny', PrePayment::class);

        $query = PrePayment::with('user')
            ->orderByDesc('advance_date')
            ->orderByDesc('year')
            ->orderByDesc('month')
            ->orderBy('installment_number');

        if ($request->filled('status')) {
            $query->where('status', $request->status);
        }
        if ($request->filled('user_id')) {
            $query->where('user_id', $request->integer('user_id'));
        }

        $prePayments = $query->paginate(20)->withQueryString();
        $employees = User::forActiveDirectory()
            ->when(auth()->user()?->company_id, fn ($q) => $q->where('company_id', auth()->user()->company_id))
            ->orderBy('name')
            ->get(['id', 'name']);

        return view('pre-payments.index', compact('prePayments', 'employees'));
    }

    public function create()
    {
        $this->authorize('create', PrePayment::class);

        $employees = User::forActiveDirectory()
            ->when(auth()->user()?->company_id, fn ($q) => $q->where('company_id', auth()->user()->company_id))
            ->orderBy('name')
            ->get(['id', 'name']);

        $defaultStart = $this->installments->defaultDeductionStart(now()->toDateString());
        $installmentOptions = PrePaymentInstallmentService::INSTALLMENT_OPTIONS;

        return view('pre-payments.create', compact('employees', 'defaultStart', 'installmentOptions'));
    }

    public function store(Request $request)
    {
        $this->authorize('create', PrePayment::class);

        $validated = $this->validatedForCreate($request);
        $companyId = (int) auth()->user()->company_id;

        $records = $this->installments->createPlan(
            $companyId,
            (int) $validated['user_id'],
            (float) $validated['amount'],
            $validated['advance_date'],
            (int) $validated['installments'],
            (int) $validated['deduction_start_month'],
            (int) $validated['deduction_start_year'],
            $validated['notes'] ?? null
        );

        $this->audit->log('pre_payment.created', $records[0] ?? null, [
            'installment_count' => count($records),
            'total_amount' => $validated['amount'],
            'installment_group_id' => $records[0]->installment_group_id,
        ]);

        $count = count($records);
        $message = $count === 1
            ? 'Advance recorded. It will deduct in payroll for the selected month.'
            : sprintf(
                'Advance of %s split into %d monthly repayments starting %s.',
                number_format((float) $validated['amount'], 2),
                $count,
                date('M Y', mktime(0, 0, 0, (int) $validated['deduction_start_month'], 1, (int) $validated['deduction_start_year']))
            );

        return redirect()->route('pre-payments.index')->with('success', $message);
    }

    public function edit(PrePayment $prePayment)
    {
        $this->assertTenantRecord($prePayment);
        $this->authorize('update', $prePayment);

        $employees = User::forActiveDirectory()
            ->when(auth()->user()?->company_id, fn ($q) => $q->where('company_id', auth()->user()->company_id))
            ->orderBy('name')
            ->get(['id', 'name']);

        return view('pre-payments.edit', compact('prePayment', 'employees'));
    }

    public function update(Request $request, PrePayment $prePayment)
    {
        $this->assertTenantRecord($prePayment);
        $this->authorize('update', $prePayment);

        $prePayment->update($this->validatedForEdit($request));
        $this->audit->log('pre_payment.updated', $prePayment);

        return redirect()->route('pre-payments.index')->with('success', 'Repayment installment updated.');
    }

    public function destroy(PrePayment $prePayment)
    {
        $this->assertTenantRecord($prePayment);
        $this->authorize('delete', $prePayment);

        $prePayment->delete();
        $this->audit->log('pre_payment.deleted', null, ['pre_payment_id' => $prePayment->id]);

        return redirect()->route('pre-payments.index')->with('success', 'Repayment installment removed.');
    }

    public function cancel(PrePayment $prePayment)
    {
        $this->assertTenantRecord($prePayment);
        $this->authorize('cancel', $prePayment);

        $cancelled = $this->installments->cancelPendingGroup($prePayment);
        $this->audit->log('pre_payment.cancelled', $prePayment, ['cancelled_count' => $cancelled]);

        $message = $prePayment->isInstallmentPlan()
            ? sprintf('Cancelled %d pending repayment installment(s) for this advance.', $cancelled)
            : 'Advance cancelled.';

        return redirect()->route('pre-payments.index')->with('success', $message);
    }

    /**
     * @return array<string, mixed>
     */
    protected function validatedForCreate(Request $request): array
    {
        $companyId = auth()->user()?->company_id;

        return $request->validate([
            'user_id' => [
                'required',
                Rule::exists('users', 'id')->where(fn ($q) => $q->where('company_id', $companyId)),
            ],
            'advance_date' => 'required|date',
            'amount' => 'required|numeric|min:0.01',
            'installments' => ['required', 'integer', Rule::in(PrePaymentInstallmentService::INSTALLMENT_OPTIONS)],
            'deduction_start_month' => 'required|integer|min:1|max:12',
            'deduction_start_year' => 'required|integer|min:2000|max:2100',
            'notes' => 'nullable|string|max:1000',
        ]);
    }

    /**
     * @return array<string, mixed>
     */
    protected function validatedForEdit(Request $request): array
    {
        $companyId = auth()->user()?->company_id;

        return $request->validate([
            'user_id' => [
                'required',
                Rule::exists('users', 'id')->where(fn ($q) => $q->where('company_id', $companyId)),
            ],
            'amount' => 'required|numeric|min:0.01',
            'month' => 'required|integer|min:1|max:12',
            'year' => 'required|integer|min:2000|max:2100',
            'notes' => 'nullable|string|max:1000',
        ]);
    }

    protected function assertTenantRecord(PrePayment $prePayment): void
    {
        $companyId = auth()->user()?->company_id;
        if ($companyId && (int) $prePayment->company_id !== (int) $companyId) {
            abort(403);
        }
    }
}
