<?php

namespace Modules\MedBilling\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Validation\ValidationException;
use Modules\MedBilling\Entities\MedbillClaim;
use Modules\MedBilling\Services\ClaimService;
use Yajra\DataTables\Facades\DataTables;

/**
 * Claims screen (SIFA P4-T1) — DataTable by status, a detail modal with the
 * adjudication form + lifecycle transitions, and a printable claim form.
 * Transitions run through ClaimService. Guarded by medbill.claims.manage; the
 * submit transition also requires medbill.claims.submit.
 */
class ClaimController extends MedBillingBaseController
{
    public function index(Request $request)
    {
        $this->guard('medbill.claims.manage');

        if ($request->ajax()) {
            $query = MedbillClaim::forBusiness($this->businessId())
                ->leftJoin('medbill_payers', 'medbill_claims.payer_id', '=', 'medbill_payers.id')
                ->select([
                    'medbill_claims.id',
                    'medbill_claims.claim_no',
                    'medbill_claims.patient_id',
                    'medbill_claims.total_claimed',
                    'medbill_claims.total_approved',
                    'medbill_claims.payer_share',
                    'medbill_claims.patient_share',
                    'medbill_claims.status',
                    'medbill_claims.transaction_id',
                    'medbill_claims.created_at',
                    'medbill_payers.name as payer_name',
                ]);

            if ($request->filled('status')) {
                $query->where('medbill_claims.status', $request->get('status'));
            }

            return DataTables::of($query)
                ->addColumn('patient_name', fn ($r) => $this->patientName((int) $r->patient_id))
                ->editColumn('status', fn ($r) => '<span class="label ' . $this->statusClass($r->status) . '">'
                    . __('medbilling::lang.claim_status_' . $r->status) . '</span>')
                ->editColumn('created_at', fn ($r) => optional($r->created_at)->format('Y-m-d'))
                ->addColumn('action', function ($r) {
                    return '<button class="btn btn-xs btn-primary medbill-modal-btn" data-href="'
                        . route('medbill.claims.show', $r->id) . '"><i class="fa fa-eye"></i> ' . __('medbilling::lang.view') . '</button> '
                        . '<a class="btn btn-xs btn-default" target="_blank" href="'
                        . route('medbill.claims.print', $r->id) . '"><i class="fa fa-print"></i></a>';
                })
                ->rawColumns(['status', 'action'])
                ->make(true);
        }

        return view('medbilling::claims.index', [
            'statuses' => MedbillClaim::STATUSES,
        ]);
    }

    /** Detail modal: items + adjudication form + available transitions. */
    public function show($id)
    {
        $this->guard('medbill.claims.manage');

        $claim = MedbillClaim::forBusiness($this->businessId())
            ->with(['items', 'payer', 'policy'])
            ->findOrFail($id);

        return view('medbilling::claims.detail', [
            'claim'       => $claim,
            'patientName' => $this->patientName((int) $claim->patient_id),
            // The submit transition also requires medbill.claims.submit; surface
            // it so the view hides the button for users who'd only get a 403.
            'canSubmitPerm' => auth()->user()->can('medbill.claims.submit'),
        ]);
    }

    /** Printable claim form. */
    public function print($id)
    {
        $this->guard('medbill.claims.manage');

        $claim = MedbillClaim::forBusiness($this->businessId())
            ->with(['items', 'payer', 'policy'])
            ->findOrFail($id);

        return view('medbilling::claims.print', [
            'claim'       => $claim,
            'patientName' => $this->patientName((int) $claim->patient_id),
            'business'    => DB::table('business')->where('id', $this->businessId())->first(),
        ]);
    }

    /** Perform a lifecycle transition. */
    public function transition(Request $request, $id, ClaimService $service)
    {
        $this->guard('medbill.claims.manage');

        $claim = MedbillClaim::forBusiness($this->businessId())->findOrFail($id);
        $action = (string) $request->get('action');

        try {
            switch ($action) {
                case 'submit':
                    $this->guard('medbill.claims.submit');
                    $service->submit($claim);
                    break;

                case 'acknowledge':
                    $service->acknowledge($claim, $request->get('status', 'acknowledged'));
                    break;

                case 'adjudicate':
                    $approved = collect($request->get('approved', []))
                        ->mapWithKeys(fn ($v, $k) => [(int) $k => (float) $v])->all();
                    $reasons = collect($request->get('denied_reason', []))
                        ->mapWithKeys(fn ($v, $k) => [(int) $k => (string) $v])->all();
                    $service->adjudicate($claim, $approved, $reasons);
                    break;

                case 'deny':
                    $reason = (string) $request->get('reason', '');
                    if ($reason === '') {
                        throw ValidationException::withMessages(['reason' => __('medbilling::lang.reason_required')]);
                    }
                    $service->deny($claim, $reason);
                    break;

                case 'appeal':
                    $reason = (string) $request->get('reason', '');
                    if ($reason === '') {
                        throw ValidationException::withMessages(['reason' => __('medbilling::lang.reason_required')]);
                    }
                    $service->appeal($claim, $reason);
                    break;

                case 'mark_paid':
                    $out = $service->markPaid($claim, $request->boolean('record_erp_payment', true));

                    return $this->ok(
                        __('medbilling::lang.claim_updated'),
                        ['erp_payment_recorded' => $out['erp_payment_recorded'], 'note' => $out['note']]
                    );

                default:
                    return $this->fail(__('medbilling::lang.invalid_action'));
            }

            return $this->ok(__('medbilling::lang.claim_updated'));
        } catch (ValidationException $e) {
            return response()->json(['success' => false, 'msg' => collect($e->errors())->flatten()->first()], 422);
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());

            return $this->fail();
        }
    }

    private function statusClass(string $status): string
    {
        return [
            'draft'              => 'label-default',
            'submitted'          => 'label-info',
            'acknowledged'       => 'label-info',
            'under_review'       => 'label-warning',
            'approved'           => 'label-success',
            'partially_approved' => 'label-warning',
            'denied'             => 'label-danger',
            'paid'               => 'label-success',
            'appealed'           => 'label-primary',
        ][$status] ?? 'label-default';
    }

    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);
    }
}
