<?php

namespace App\Http\Controllers\Care;

use App\Http\Controllers\Controller;
use App\Http\Controllers\Care\Concerns\ScopesToAccount;
use App\Models\Appointment;
use App\Models\Branch;
use App\Models\Patient;
use App\Models\Visit;
use App\Services\Care\AdminOverviewService;
use App\Services\Care\CareFeatures;
use App\Services\Care\CarePermissions;
use App\Services\Care\OrganizationResolver;
use App\Services\Care\PatientService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class PatientController extends Controller
{
    use ScopesToAccount;

    public function __construct(
        protected PatientService $patients,
        protected AdminOverviewService $adminOverview,
    ) {}

    public function index(Request $request): View
    {
        $this->authorizeAbility($request, 'patients.view');
        $organization = $this->organization($request);
        $member = $this->member($request);
        $resolver = app(OrganizationResolver::class);
        $branchScope = $resolver->branchScope($member);
        $practitionerScope = $resolver->practitionerScope($organization, $member);
        $permissions = app(CarePermissions::class);

        if ($permissions->isAdmin($member) && $request->query('view') !== 'list') {
            return view('care.admin.analytics', [
                'title' => 'Patients',
                'badge' => 'Analytics · Population',
                'description' => 'Registration and visit trends across your facility — not a bedside patient list.',
                'adminOverview' => $this->adminOverview->patients(
                    $organization,
                    $this->ownerRef($request),
                    $branchScope,
                ),
            ]);
        }

        $patients = $this->patients->search(
            $this->ownerRef($request),
            $organization->id,
            $request->only(['q', 'patient_number', 'phone', 'national_id', 'date_of_birth']),
            $practitionerScope === null ? $branchScope : null,
            $practitionerScope,
        );

        $owner = $this->ownerRef($request);
        $patientQuery = Patient::owned($owner)
            ->where('organization_id', $organization->id);
        if ($practitionerScope !== null) {
            if ($practitionerScope === []) {
                $patientQuery->whereRaw('1 = 0');
            } else {
                $patientQuery->whereHas(
                    'appointments',
                    fn ($q) => $q->whereIn('practitioner_id', $practitionerScope),
                );
            }
        } elseif ($branchScope) {
            $patientQuery->where('branch_id', $branchScope);
        }

        $heroStats = [
            'total' => (clone $patientQuery)->count(),
            'new_this_month' => (clone $patientQuery)->where('created_at', '>=', now()->startOfMonth())->count(),
            'with_visits' => (clone $patientQuery)->whereHas('appointments')->count(),
        ];

        return view('care.patients.index', compact('patients', 'organization', 'heroStats'));
    }

    public function scanLookup(Request $request): JsonResponse|RedirectResponse
    {
        $this->authorizeAbility($request, 'patients.view');
        $organization = $this->organization($request);
        $branchScope = app(OrganizationResolver::class)->branchScope($this->member($request));

        $code = trim((string) $request->query('code', ''));
        abort_unless($code !== '', 422, 'Scan code required.');

        $patient = $this->patients->findByScanCode(
            $this->ownerRef($request),
            $organization->id,
            $code,
            $branchScope,
        );

        if ($request->expectsJson() || $request->wantsJson() || $request->ajax()) {
            if (! $patient) {
                return response()->json(['message' => 'No patient found for that barcode.'], 404);
            }

            return response()->json([
                'patient' => [
                    'uuid' => $patient->uuid,
                    'patient_number' => $patient->patient_number,
                    'name' => $patient->fullName(),
                    'url' => route('care.patients.show', $patient),
                ],
            ]);
        }

        if (! $patient) {
            return redirect()
                ->route('care.patients.index', ['q' => $code])
                ->with('error', 'No patient found for that barcode.');
        }

        return redirect()->route('care.patients.show', $patient);
    }

    public function label(Request $request, Patient $patient): View
    {
        $this->authorizeAbility($request, 'patients.view');
        $this->authorizePatient($request, $patient);

        return view('care.patients.label', [
            'patient' => $patient,
            'organization' => $this->organization($request),
            'genders' => config('care.genders'),
        ]);
    }

    public function create(Request $request): View
    {
        $this->authorizeAbility($request, 'patients.manage');
        $organization = $this->organization($request);

        $branches = Branch::owned($this->ownerRef($request))
            ->where('organization_id', $organization->id)
            ->where('is_active', true)
            ->orderBy('name')
            ->get();

        return view('care.patients.create', [
            'organization' => $organization,
            'branches' => $branches,
            'genders' => config('care.genders'),
            'allergySeverities' => config('care.allergy_severities'),
            'documentTypes' => config('care.document_types'),
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $this->authorizeAbility($request, 'patients.manage');
        $organization = $this->organization($request);

        $validated = $this->validatedPatientData($request);

        $patient = $this->patients->create(
            $organization,
            $this->ownerRef($request),
            $validated,
            $this->ownerRef($request),
        );

        return redirect()->route('care.patients.show', $patient)
            ->with('success', 'Patient registered successfully.');
    }

    public function show(Request $request, Patient $patient): View
    {
        $this->authorizeAbility($request, 'patients.view');
        $this->authorizePatient($request, $patient);

        $dashboard = $this->patients->dashboard($patient);
        $permissions = app(CarePermissions::class);
        $member = $this->member($request);
        $organization = $this->organization($request);
        $canManage = $permissions->can($member, 'patients.manage');
        $credential = app(\App\Services\Messaging\MessagingCredentialsService::class)
            ->forOrganization($organization);
        $suiteEmailReady = app(\App\Services\Billing\PlatformEmailClient::class)->isConfigured();
        $suiteSmsReady = app(\App\Services\Billing\PlatformSmsClient::class)->isConfigured();

        $careFeatures = app(CareFeatures::class);
        $assessmentsEnabled = $careFeatures->enabled($organization, CareFeatures::ASSESSMENTS_ENGINE);
        if ($assessmentsEnabled) {
            $careFeatures->ensureAssessmentCatalog();
        }
        $canViewAssessments = $assessmentsEnabled && $permissions->can($member, 'assessments.view');
        $canCaptureAssessment = $assessmentsEnabled && (
            $permissions->can($member, 'assessments.capture')
            || $permissions->can($member, 'assessments.manage')
        );
        $recentAssessments = collect();
        $latestUniversalIntake = null;
        if ($canViewAssessments) {
            $branchScope = app(OrganizationResolver::class)->branchScope($member);
            $recentAssessments = $patient->assessments()
                ->with('template')
                ->when($branchScope, fn ($q) => $q->where('branch_id', $branchScope))
                ->orderByDesc('created_at')
                ->limit(10)
                ->get();

            $latestUniversalIntake = $patient->assessments()
                ->with(['template', 'answers.question'])
                ->when($branchScope, fn ($q) => $q->where('branch_id', $branchScope))
                ->whereHas('template', fn ($q) => $q->where('code', 'universal_intake'))
                ->whereIn('status', [\App\Models\Assessment::STATUS_DRAFT, \App\Models\Assessment::STATUS_COMPLETED])
                ->orderByDesc('created_at')
                ->first();
        }

        return view('care.patients.show', array_merge($dashboard, [
            'canManage' => $canManage,
            'messagingSmsReady' => $suiteSmsReady || $credential->hasValidSms(),
            'messagingEmailReady' => $suiteEmailReady || $credential->hasValidBird(),
            'suiteMessagingReady' => $suiteEmailReady || $suiteSmsReady,
            'messagingSettingsUrl' => function_exists('ladill_account_url')
                ? ladill_account_url('/account/settings/messaging')
                : (string) config('smtp.messaging_settings_url', 'https://account.ladill.com/account/settings/messaging'),
            'genders' => config('care.genders'),
            'allergySeverities' => config('care.allergy_severities'),
            'documentTypes' => config('care.document_types'),
            'assessmentsEnabled' => $assessmentsEnabled,
            'canViewAssessments' => $canViewAssessments,
            'canCaptureAssessment' => $canCaptureAssessment,
            'recentAssessments' => $recentAssessments,
            'latestUniversalIntake' => $latestUniversalIntake,
            'assessmentStatuses' => config('care.assessment_statuses'),
            'patientHeaderMeta' => app(\App\Services\Care\SpecialtyShellService::class)->patientHeaderMeta($patient),
            'patientTimeline' => app(\App\Services\Care\SpecialtyShellService::class)->patientTimeline($patient),
            'currency' => strtoupper((string) config('care.billing.currency', 'GHS')),
        ]));
    }

    public function edit(Request $request, Patient $patient): View
    {
        $this->authorizeAbility($request, 'patients.manage');
        $this->authorizePatient($request, $patient);
        $organization = $this->organization($request);

        $patient->load(['allergies', 'conditions', 'familyHistory', 'emergencyContacts', 'insurancePolicies', 'attachments']);

        $branches = Branch::owned($this->ownerRef($request))
            ->where('organization_id', $organization->id)
            ->where('is_active', true)
            ->orderBy('name')
            ->get();

        return view('care.patients.edit', [
            'patient' => $patient,
            'organization' => $organization,
            'branches' => $branches,
            'genders' => config('care.genders'),
            'allergySeverities' => config('care.allergy_severities'),
            'documentTypes' => config('care.document_types'),
        ]);
    }

    public function update(Request $request, Patient $patient): RedirectResponse
    {
        $this->authorizeAbility($request, 'patients.manage');
        $this->authorizePatient($request, $patient);

        $validated = $this->validatedPatientData($request);

        $this->patients->update($patient, $this->ownerRef($request), $validated, $this->ownerRef($request));

        return redirect()->route('care.patients.show', $patient)
            ->with('success', 'Patient record updated.');
    }

    public function destroy(Request $request, Patient $patient): RedirectResponse
    {
        $this->authorizeAbility($request, 'patients.manage');
        $this->authorizePatient($request, $patient);

        $this->patients->delete($patient, $this->ownerRef($request), $this->ownerRef($request));

        return redirect()->route('care.patients.index')
            ->with('success', 'Patient record archived.');
    }

    protected function authorizePatient(Request $request, Patient $patient): void
    {
        $this->authorizeOwner($request, $patient);
        abort_unless($patient->organization_id === $this->organization($request)->id, 404);

        $branchId = app(OrganizationResolver::class)->branchScope($this->member($request));
        if ($branchId === null || $patient->branch_id === $branchId) {
            return;
        }

        // Home branch may differ from the site where care is delivered — allow chart
        // access when the patient has a visit or appointment at the member's branch.
        $hasLocalActivity = Visit::query()
            ->where('patient_id', $patient->id)
            ->where('organization_id', $patient->organization_id)
            ->where('branch_id', $branchId)
            ->exists()
            || Appointment::query()
                ->where('patient_id', $patient->id)
                ->where('organization_id', $patient->organization_id)
                ->where('branch_id', $branchId)
                ->exists();

        abort_unless($hasLocalActivity, 404);
    }

    /**
     * @return array<string, mixed>
     */
    protected function validatedPatientData(Request $request): array
    {
        return $request->validate([
            'branch_id' => ['nullable', 'integer', 'exists:care_branches,id'],
            'first_name' => ['required', 'string', 'max:100'],
            'last_name' => ['required', 'string', 'max:100'],
            'other_names' => ['nullable', 'string', 'max:100'],
            'gender' => ['nullable', 'string', 'in:'.implode(',', array_keys(config('care.genders')))],
            'date_of_birth' => ['nullable', 'date', 'before:today'],
            'phone' => ['nullable', 'string', 'max:30'],
            'email' => ['nullable', 'email', 'max:255'],
            'national_id' => ['nullable', 'string', 'max:50'],
            'address' => ['nullable', 'string', 'max:500'],
            'city' => ['nullable', 'string', 'max:100'],
            'region' => ['nullable', 'string', 'max:100'],
            'notes' => ['nullable', 'string', 'max:5000'],
            'allergies' => ['nullable', 'array'],
            'allergies.*.allergen' => ['nullable', 'string', 'max:255'],
            'allergies.*.severity' => ['nullable', 'string', 'in:'.implode(',', array_keys(config('care.allergy_severities')))],
            'allergies.*.notes' => ['nullable', 'string', 'max:1000'],
            'conditions' => ['nullable', 'array'],
            'conditions.*.condition' => ['nullable', 'string', 'max:255'],
            'conditions.*.onset_date' => ['nullable', 'date'],
            'conditions.*.is_chronic' => ['nullable', 'boolean'],
            'conditions.*.notes' => ['nullable', 'string', 'max:1000'],
            'family_history' => ['nullable', 'array'],
            'family_history.*.relation' => ['nullable', 'string', 'max:100'],
            'family_history.*.condition' => ['nullable', 'string', 'max:255'],
            'family_history.*.notes' => ['nullable', 'string', 'max:1000'],
            'emergency_contacts' => ['nullable', 'array'],
            'emergency_contacts.*.name' => ['nullable', 'string', 'max:255'],
            'emergency_contacts.*.phone' => ['nullable', 'string', 'max:30'],
            'emergency_contacts.*.relationship' => ['nullable', 'string', 'max:100'],
            'emergency_contacts.*.is_primary' => ['nullable', 'boolean'],
            'insurance' => ['nullable', 'array'],
            'insurance.*.provider_name' => ['nullable', 'string', 'max:255'],
            'insurance.*.policy_number' => ['nullable', 'string', 'max:100'],
            'insurance.*.coverage_type' => ['nullable', 'string', 'max:100'],
            'insurance.*.expiry_date' => ['nullable', 'date'],
            'insurance.*.notes' => ['nullable', 'string', 'max:1000'],
            'attachments' => ['nullable', 'array'],
            'attachments.*' => ['file', 'max:10240', 'mimes:pdf,jpeg,png,jpg,webp'],
        ]);
    }
}
