<?php

namespace Modules\MedPortal\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Modules\MedPortal\Entities\MportalHealthLog;
use Modules\MedPortal\Services\MessagingService;
use Modules\MedPortal\Services\RecordAccessService;
use Modules\MedPortal\Support\ClinicBridge;

/**
 * PATIENT-FACING thin web view (SIFA Phase 8).
 *
 * Full portal authentication is PortalApi's job (portal_owners + OTP + tokens),
 * consumed by the Vue PWAs over the REST API. On the ERP *web* stack that token
 * auth may not be wired, so this controller is deliberately thin and defensive:
 *
 *   - It authenticates via the `customer` session guard (config/auth.php →
 *     provider `contacts`) WHEN AVAILABLE.
 *   - If the guard is absent / no authenticated contact, it renders a
 *     "portal login required" page instead of erroring (spec §D).
 *
 * Everything it shows is RELEASE-GATED by RecordAccessService — the patient only
 * sees record types released to them. It is otherwise API-ready: the same
 * release gate + repositories back a future PortalApi endpoint.
 *
 * Read-only except: log a self-tracked health reading, and reply in a thread.
 */
class MeController extends Controller
{
    /** Resolve the authenticated portal contact, or null (guard absent/none). */
    private function contact()
    {
        try {
            if (! auth()->guard('customer')->check()) {
                return null;
            }

            return auth()->guard('customer')->user();
        } catch (\Throwable $e) {
            // Guard not registered on this host — patient side not wired.
            return null;
        }
    }

    /** Resolve the clc_patients row(s) for the authenticated contact. */
    private function patientFor($contact): ?object
    {
        if (! $contact || ! ClinicBridge::hasClinic()) {
            return null;
        }

        return DB::table('clc_patients')
            ->where('business_id', $contact->business_id)
            ->where('contact_id', $contact->id)
            ->first(['id', 'business_id', 'contact_id', 'patient_name']);
    }

    public function dashboard(RecordAccessService $access, MessagingService $messaging)
    {
        $contact = $this->contact();
        if (! $contact) {
            return $this->loginRequired();
        }

        $patient = $this->patientFor($contact);
        if (! $patient) {
            return view('medportal::me.no_patient');
        }

        $businessId = (int) $patient->business_id;
        $patientId  = (int) $patient->id;

        $threads = $messaging->threadsForBusiness($businessId)
            ->where('patient_id', $patientId)->values();

        // Unread FROM staff (what the patient hasn't read yet) across their threads.
        $unread = \Modules\MedPortal\Entities\MportalMessage::forBusiness($businessId)
            ->where('patient_id', $patientId)
            ->where('sender_type', 'staff')
            ->whereNull('read_at')
            ->count();

        return view('medportal::me.dashboard', [
            'patient'      => $patient,
            'visibility'   => $access->visibleRecords($businessId, $patientId),
            'appointments' => $this->appointments($businessId, $patientId),
            'unread'       => $unread,
            'threads'      => $threads,
            'healthLogs'   => MportalHealthLog::forBusiness($businessId)->forPatient($patientId)
                ->orderByDesc('logged_at')->limit(20)->get(),
        ]);
    }

    /** Released clinical records for the authenticated patient (release-gated). */
    public function records(RecordAccessService $access)
    {
        $contact = $this->contact();
        if (! $contact) {
            return $this->loginRequired();
        }

        $patient = $this->patientFor($contact);
        if (! $patient) {
            return view('medportal::me.no_patient');
        }

        $businessId = (int) $patient->business_id;
        $patientId  = (int) $patient->id;
        $visibility = $access->visibleRecords($businessId, $patientId);

        $data = [
            'patient'    => $patient,
            'visibility' => $visibility,
            'prescriptions' => collect(),
            'reports'       => collect(),
            'imaging'       => collect(),
            'vitals'        => collect(),
        ];

        // Each block is only queried when RELEASED (RecordAccessService gate).
        if ($visibility['prescription'] && Schema::hasTable('clc_prescriptions')) {
            $data['prescriptions'] = DB::table('clc_prescriptions')
                ->where('business_id', $businessId)->where('patient_id', $patientId)
                ->orderByDesc('prescribed_date')->limit(50)->get();
        }
        if (($visibility['lab_result'] || $visibility['clinical_note']) && Schema::hasTable('clc_medical_reports')) {
            // Lab results live in clc_medical_reports (report_type='lab'); other
            // report types surface only when clinical notes are released.
            $q = DB::table('clc_medical_reports')
                ->where('business_id', $businessId)->where('patient_id', $patientId);
            if (! $visibility['clinical_note']) {
                $q->where('report_type', 'lab');
            }
            if (! $visibility['lab_result']) {
                $q->where('report_type', '!=', 'lab');
            }
            $data['reports'] = $q->orderByDesc('id')->limit(50)->get();
        }
        if ($visibility['imaging_report'] && Schema::hasTable('clc_imaging_reports')) {
            $data['imaging'] = DB::table('clc_imaging_reports')
                ->where('business_id', $businessId)->where('patient_id', $patientId)
                ->whereIn('report_status', ['final', 'amended']) // only finalised reports
                ->orderByDesc('id')->limit(50)->get();
        }
        if ($visibility['vitals'] && Schema::hasTable('clc_vitals')) {
            $data['vitals'] = DB::table('clc_vitals')
                ->where('business_id', $businessId)->where('patient_id', $patientId)
                ->orderByDesc('recorded_at')->limit(50)->get();
        }

        return view('medportal::me.records', $data);
    }

    /** Patient logs a self-tracked reading. */
    public function logHealth(Request $request)
    {
        $contact = $this->contact();
        if (! $contact) {
            return $this->loginRequired();
        }

        $patient = $this->patientFor($contact);
        if (! $patient) {
            return redirect()->back();
        }

        $request->validate([
            'metric'     => 'required|in:' . implode(',', MportalHealthLog::METRICS),
            'value_num'  => 'nullable|numeric',
            'value_text' => 'nullable|string|max:60',
        ]);

        MportalHealthLog::create([
            'business_id' => (int) $patient->business_id,
            'patient_id'  => (int) $patient->id,
            'metric'      => $request->input('metric'),
            'value_num'   => $request->input('value_num'),
            'value_text'  => $request->input('value_text'),
            'logged_at'   => now(),
            'source'      => 'patient',
            'notes'       => $request->input('notes'),
        ]);

        return redirect()->route('mportal.me.dashboard')->with('status', __('medportal::lang.reading_saved'));
    }

    private function appointments(int $businessId, int $patientId)
    {
        if (! Schema::hasTable('clc_appointments')) {
            return collect();
        }

        return DB::table('clc_appointments')
            ->where('business_id', $businessId)->where('patient_id', $patientId)
            ->orderByDesc('appointment_date')->limit(20)->get();
    }

    /** "Portal login required" page — the graceful fallback when unauthenticated. */
    private function loginRequired()
    {
        return response()->view('medportal::me.login_required', [], 200);
    }
}
