<?php

namespace Modules\MedBilling\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Modules\MedBilling\Entities\MedbillChargeItem;
use Modules\MedBilling\Entities\MedbillPatientPolicy;
use Modules\MedBilling\Services\ChargeCaptureService;

/**
 * Billing worklist (SIFA P4-T1) — completed appointments with uncaptured /
 * unbilled charges. Actions: capture charges, review the drafted charges + a
 * policy split preview, and bill (create the ERP sell). Guarded by
 * medbill.charges.manage.
 */
class WorklistController extends MedBillingBaseController
{
    public function index()
    {
        $this->guard('medbill.charges.manage');

        return view('medbilling::worklist.index', [
            'rows'        => $this->completedAppointments(),
            'locations'   => $this->locations(),
            'hasClinic'   => Schema::hasTable('clc_appointments'),
        ]);
    }

    /** Capture (draft) charges for one appointment on demand. */
    public function capture(Request $request, ChargeCaptureService $capture)
    {
        $this->guard('medbill.charges.manage');
        $request->validate(['appointment_id' => 'required|integer']);

        // Business scope: ensure the appointment belongs to this business.
        $appt = Schema::hasTable('clc_appointments')
            ? DB::table('clc_appointments')
                ->where('id', (int) $request->appointment_id)
                ->where('business_id', $this->businessId())
                ->first()
            : null;
        if (! $appt) {
            return $this->fail(__('medbilling::lang.appointment_not_found'));
        }

        try {
            $out = $capture->captureForAppointment((int) $request->appointment_id);

            return $this->ok(
                __('medbilling::lang.charges_captured', ['count' => $out['created']]),
                ['created' => $out['created'], 'skipped' => $out['skipped']]
            );
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());

            return $this->fail();
        }
    }

    /** Review drafted charges for an appointment (+ the patient's active policies). */
    public function review(Request $request)
    {
        $this->guard('medbill.charges.manage');
        $appointmentId = (int) $request->get('appointment_id');

        $charges = MedbillChargeItem::forBusiness($this->businessId())
            ->where('appointment_id', $appointmentId)
            ->where('status', 'draft')
            ->orderBy('id')
            ->get();

        $patientId = optional($charges->first())->patient_id;
        $policies = $patientId
            ? MedbillPatientPolicy::forBusiness($this->businessId())
                ->with('payer')
                ->where('patient_id', $patientId)
                ->where('status', 'active')
                ->get()
            : collect();

        return view('medbilling::worklist.review', [
            'appointmentId' => $appointmentId,
            'charges'       => $charges,
            'policies'      => $policies,
            'locations'     => $this->locations(),
        ]);
    }

    /** Bill the selected draft charges — creates the ERP sell (+ claim if a policy is chosen). */
    public function bill(Request $request, ChargeCaptureService $capture)
    {
        $this->guard('medbill.charges.manage');

        $data = $this->validate($request, [
            'charge_ids'   => 'required|array|min:1',
            'charge_ids.*' => 'integer',
            'location_id'  => 'required|integer',
            'policy_id'    => 'nullable|integer',
        ]);

        try {
            $out = $capture->billCharges(
                array_map('intval', $data['charge_ids']),
                (int) $data['location_id'],
                ! empty($data['policy_id']) ? (int) $data['policy_id'] : null
            );

            if (empty($out['sells']) && ! empty($out['errors'])) {
                return $this->fail(implode(' ', $out['errors']));
            }

            return $this->ok(
                __('medbilling::lang.charges_billed', ['count' => $out['billed'], 'sells' => count($out['sells'])]),
                [
                    'sells'      => $out['sells'],
                    'claims'     => $out['claims'],
                    'unbillable' => $out['unbillable'],
                    'errors'     => $out['errors'],
                ]
            );
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());

            return $this->fail();
        }
    }

    /**
     * Completed appointments with charge status: whether charges were captured
     * and whether any remain unbilled. Fail-soft when ClinicCore is absent.
     */
    private function completedAppointments()
    {
        if (! Schema::hasTable('clc_appointments')) {
            return collect();
        }

        $businessId = $this->businessId();

        $appts = DB::table('clc_appointments')
            ->leftJoin('clc_patients', 'clc_appointments.patient_id', '=', 'clc_patients.id')
            ->leftJoin('contacts', 'clc_patients.contact_id', '=', 'contacts.id')
            ->leftJoin('clc_doctors', 'clc_appointments.doctor_id', '=', 'clc_doctors.id')
            ->where('clc_appointments.business_id', $businessId)
            ->where('clc_appointments.status', 'completed')
            ->orderByDesc('clc_appointments.appointment_date')
            ->limit(200)
            ->get([
                'clc_appointments.id',
                'clc_appointments.appointment_date',
                'clc_appointments.patient_id',
                'clc_appointments.erp_sell_id',
                'contacts.name as patient_name',
                'clc_doctors.name as doctor_name',
            ]);

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

        // Charge counts per appointment in one query.
        $counts = MedbillChargeItem::forBusiness($businessId)
            ->whereIn('appointment_id', $appts->pluck('id'))
            ->select('appointment_id',
                DB::raw("SUM(status = 'draft') as draft_count"),
                DB::raw("SUM(status = 'billed') as billed_count"),
                DB::raw('COUNT(*) as total_count'))
            ->groupBy('appointment_id')
            ->get()
            ->keyBy('appointment_id');

        return $appts->map(function ($a) use ($counts) {
            $c = $counts->get($a->id);
            $a->draft_count = $c ? (int) $c->draft_count : 0;
            $a->billed_count = $c ? (int) $c->billed_count : 0;
            $a->captured = $c ? (int) $c->total_count > 0 : false;

            return $a;
        });
    }

    /** business_locations (id => name) for this business. */
    private function locations(): array
    {
        return DB::table('business_locations')
            ->where('business_id', $this->businessId())
            ->where('is_active', 1)
            ->pluck('name', 'id')
            ->toArray();
    }
}
