<?php

namespace Modules\MedBilling\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Modules\MedBilling\Entities\MedbillPatientPolicy;
use Modules\MedBilling\Entities\MedbillPayer;
use Yajra\DataTables\Facades\DataTables;

/**
 * Patient policy CRUD (SIFA P4-T1). One row = a patient's coverage under a payer.
 * Linkable to a Wave-A clc_patient_insurances card row. Guarded by
 * medbill.payers.manage (payer/policy administration).
 */
class PolicyController extends MedBillingBaseController
{
    public function index(Request $request)
    {
        $this->guard('medbill.payers.manage');

        if ($request->ajax()) {
            $query = MedbillPatientPolicy::forBusiness($this->businessId())
                ->leftJoin('medbill_payers', 'medbill_patient_policies.payer_id', '=', 'medbill_payers.id')
                ->select([
                    'medbill_patient_policies.id',
                    'medbill_patient_policies.patient_id',
                    'medbill_patient_policies.policy_number',
                    'medbill_patient_policies.plan_name',
                    'medbill_patient_policies.coverage_pct',
                    'medbill_patient_policies.copay_fixed',
                    'medbill_patient_policies.status',
                    'medbill_payers.name as payer_name',
                ]);

            if ($request->filled('patient_id')) {
                $query->where('medbill_patient_policies.patient_id', (int) $request->get('patient_id'));
            }

            return DataTables::of($query)
                ->addColumn('patient_name', fn ($r) => $this->patientName((int) $r->patient_id))
                ->editColumn('status', fn ($r) => '<span class="label label-' . ($r->status === 'active' ? 'success' : 'default') . '">'
                    . __('medbilling::lang.status_' . $r->status) . '</span>')
                ->addColumn('action', function ($r) {
                    return '<button class="btn btn-xs btn-default medbill-modal-btn" data-href="'
                        . route('medbill.policies.edit', $r->id) . '"><i class="fa fa-edit"></i> ' . __('messages.edit') . '</button> '
                        . '<button class="btn btn-xs btn-danger medbill-delete-btn" data-href="'
                        . route('medbill.policies.destroy', $r->id) . '"><i class="fa fa-trash"></i></button>';
                })
                ->rawColumns(['status', 'action'])
                ->make(true);
        }

        return view('medbilling::policies.index');
    }

    public function create(Request $request)
    {
        $this->guard('medbill.payers.manage');

        return view('medbilling::policies.form', [
            'policy'   => new MedbillPatientPolicy(['patient_id' => $request->get('patient_id')]),
            'payers'   => $this->payers(),
            'patients' => $this->patients(),
            'cards'    => $this->insuranceCards(),
        ]);
    }

    public function edit($id)
    {
        $this->guard('medbill.payers.manage');

        $policy = MedbillPatientPolicy::forBusiness($this->businessId())->findOrFail($id);

        return view('medbilling::policies.form', [
            'policy'   => $policy,
            'payers'   => $this->payers(),
            'patients' => $this->patients(),
            'cards'    => $this->insuranceCards(),
        ]);
    }

    public function store(Request $request)
    {
        $this->guard('medbill.payers.manage');
        $data = $this->validated($request);

        try {
            $data['business_id'] = $this->businessId();
            $data['contact_id'] = $this->resolveContactId((int) $data['patient_id']);
            MedbillPatientPolicy::create($data);

            return $this->ok(__('medbilling::lang.policy_saved'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());

            return $this->fail();
        }
    }

    public function update(Request $request, $id)
    {
        $this->guard('medbill.payers.manage');
        $policy = MedbillPatientPolicy::forBusiness($this->businessId())->findOrFail($id);
        $data = $this->validated($request);

        try {
            $data['contact_id'] = $this->resolveContactId((int) $data['patient_id']);
            $policy->update($data);

            return $this->ok(__('medbilling::lang.policy_saved'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());

            return $this->fail();
        }
    }

    public function destroy($id)
    {
        $this->guard('medbill.payers.manage');

        try {
            MedbillPatientPolicy::forBusiness($this->businessId())->findOrFail($id)->delete();

            return $this->ok(__('medbilling::lang.policy_deleted'));
        } catch (\Exception $e) {
            return $this->fail();
        }
    }

    private function validated(Request $request): array
    {
        $data = $this->validate($request, [
            'payer_id'                  => 'required|integer',
            'clc_patient_insurance_id'  => 'nullable|integer',
            'patient_id'                => 'required|integer',
            'policy_number'             => 'required|string|max:100',
            'plan_name'                 => 'nullable|string|max:150',
            'coverage_pct'              => 'required|numeric|min:0|max:100',
            'copay_fixed'               => 'nullable|numeric|min:0',
            'annual_limit'              => 'nullable|numeric|min:0',
            'effective_from'            => 'nullable|date',
            'expiry_date'               => 'nullable|date',
            'status'                    => 'required|in:active,expired,cancelled',
        ]);

        return [
            'payer_id'                 => (int) $data['payer_id'],
            'clc_patient_insurance_id' => ! empty($data['clc_patient_insurance_id']) ? (int) $data['clc_patient_insurance_id'] : null,
            'patient_id'               => (int) $data['patient_id'],
            'policy_number'            => $data['policy_number'],
            'plan_name'                => $data['plan_name'] ?? null,
            'coverage_pct'             => (float) $data['coverage_pct'],
            'copay_fixed'              => (float) ($data['copay_fixed'] ?? 0),
            'annual_limit'             => ! empty($data['annual_limit']) ? (float) $data['annual_limit'] : null,
            'effective_from'           => $data['effective_from'] ?? null,
            'expiry_date'              => $data['expiry_date'] ?? null,
            'status'                   => $data['status'],
        ];
    }

    private function payers(): array
    {
        return MedbillPayer::forBusiness($this->businessId())->active()->pluck('name', 'id')->toArray();
    }

    /** clc_patients dropdown (id => display name). Fail-soft when ClinicCore absent. */
    private function patients(): array
    {
        if (! Schema::hasTable('clc_patients')) {
            return [];
        }

        return DB::table('clc_patients')
            ->leftJoin('contacts', 'clc_patients.contact_id', '=', 'contacts.id')
            ->where('clc_patients.business_id', $this->businessId())
            ->orderBy('contacts.name')
            ->limit(500)
            ->get(['clc_patients.id', 'contacts.name as contact_name', 'clc_patients.patient_name'])
            ->mapWithKeys(fn ($p) => [$p->id => ($p->contact_name ?: $p->patient_name ?: ('Patient #' . $p->id))])
            ->toArray();
    }

    /** Wave-A insurance card rows for linking (id => label). */
    private function insuranceCards(): array
    {
        if (! Schema::hasTable('clc_patient_insurances')) {
            return [];
        }

        return DB::table('clc_patient_insurances')
            ->where('business_id', $this->businessId())
            ->orderByDesc('id')
            ->limit(500)
            ->get(['id', 'provider_name', 'policy_number', 'patient_id'])
            ->mapWithKeys(fn ($c) => [$c->id => trim(($c->provider_name ?: '') . ' — ' . ($c->policy_number ?: '') . ' (patient #' . $c->patient_id . ')')])
            ->toArray();
    }

    private function patientName(int $patientId): string
    {
        if (! Schema::hasTable('clc_patients')) {
            return 'Patient #' . $patientId;
        }
        $name = DB::table('clc_patients')
            ->leftJoin('contacts', 'clc_patients.contact_id', '=', 'contacts.id')
            ->where('clc_patients.id', $patientId)
            ->value('contacts.name');

        return $name ?: ('Patient #' . $patientId);
    }

    private function resolveContactId(int $patientId): int
    {
        $contactId = Schema::hasTable('clc_patients')
            ? DB::table('clc_patients')->where('id', $patientId)->value('contact_id')
            : null;

        return (int) ($contactId ?: 0);
    }
}
