<?php

namespace App\Http\Controllers\Frontdesk;

use App\Http\Controllers\Controller;
use App\Models\Lab;
use App\Models\Otp;
use App\Models\Patient;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\StreamedResponse;

class PatientController extends Controller
{
    public function index(Request $request): View
    {
        $patients = $this->queryPatients($request)
            ->with('lab:id,name,branch_name')
            ->latest('id')
            ->paginate(15)
            ->withQueryString();

        return view('frontdesk.patients.index', compact('patients'));
    }

    public function create(): View
    {
        $user = request()->user();
        $canSelectLab = $user->role === 'super_admin';
        $labs = $canSelectLab
            ? Lab::query()->select(['id', 'name', 'branch_name'])->orderBy('name')->get()
            : collect();

        return view('frontdesk.patients.create', compact('canSelectLab', 'labs'));
    }

    public function store(Request $request): RedirectResponse
    {
        $user = $request->user();
        if (in_array($user->role, ['lab_manager', 'lab_staff'], true) && ! $user->lab_id) {
            return back()->withErrors(['lab_id' => 'Your account is not assigned to a lab.'])->withInput();
        }

        $data = $request->validate([
            'full_name' => ['required', 'string', 'max:255'],
            'email' => ['nullable', 'email', 'max:255', 'unique:patients,email'],
            'phone' => ['nullable', 'string', 'max:30', 'unique:patients,phone'],
            'dob' => ['nullable', 'date'],
            'gender' => ['nullable', 'string', 'max:20'],
            'lab_id' => [$user->role === 'super_admin' ? 'required' : 'nullable', 'exists:labs,id'],
        ]);

        $data['lab_id'] = $user->role === 'super_admin'
            ? (int) $data['lab_id']
            : (int) $user->lab_id;

        $patient = Patient::query()->create($data);

        return redirect()->route('frontdesk.patients.show', $patient)->with('success', 'Patient created.');
    }

    public function show(Request $request, Patient $patient): View
    {
        $this->ensurePatientAccessible($request, $patient);

        $orders = $patient->orders()
            ->with('lab:id,name,branch_name')
            ->latest('id')
            ->paginate(10)
            ->withQueryString();

        return view('frontdesk.patients.show', compact('patient', 'orders'));
    }

    public function history(Request $request, Patient $patient): View
    {
        $this->ensurePatientAccessible($request, $patient);

        $orderQuery = $patient->orders()->with([
            'lab:id,name,branch_name',
            'payments:id,order_id,amount,method,reference,paid_at,created_at',
            'results:id,order_id,version,file_path,ready_at,created_at',
            'items:id,order_id,test_id,qty,price,line_total',
            'items.test:id,name,code',
        ])->latest('id');

        $orders = (clone $orderQuery)->paginate(10, ['*'], 'orders_page')->withQueryString();

        $payments = $patient->orders()
            ->join('payments', 'payments.order_id', '=', 'orders.id')
            ->select([
                'payments.id',
                'payments.order_id',
                'payments.amount',
                'payments.method',
                'payments.reference',
                'payments.paid_at',
                'payments.created_at',
                'orders.order_code',
                'orders.lab_id',
            ])
            ->when(
                in_array($request->user()->role, ['lab_manager', 'lab_staff'], true),
                fn ($q) => $q->where('orders.lab_id', $request->user()->lab_id)
            )
            ->latest('payments.id')
            ->paginate(12, ['*'], 'payments_page')
            ->withQueryString();

        $results = $patient->orders()
            ->join('results', 'results.order_id', '=', 'orders.id')
            ->select([
                'results.id',
                'results.order_id',
                'results.version',
                'results.file_path',
                'results.ready_at',
                'results.created_at',
                'orders.order_code',
                'orders.lab_id',
            ])
            ->when(
                in_array($request->user()->role, ['lab_manager', 'lab_staff'], true),
                fn ($q) => $q->where('orders.lab_id', $request->user()->lab_id)
            )
            ->latest('results.id')
            ->paginate(12, ['*'], 'results_page')
            ->withQueryString();

        $otpHistory = Otp::query()
            ->where('patient_id', $patient->id)
            ->latest('id')
            ->paginate(15, ['*'], 'otp_page')
            ->withQueryString();

        return view('frontdesk.patients.history', compact('patient', 'orders', 'payments', 'results', 'otpHistory'));
    }

    public function edit(Request $request, Patient $patient): View
    {
        $this->ensurePatientAccessible($request, $patient);

        $canSelectLab = $request->user()->role === 'super_admin';
        $labs = $canSelectLab
            ? Lab::query()->select(['id', 'name', 'branch_name'])->orderBy('name')->get()
            : collect();

        return view('frontdesk.patients.edit', compact('patient', 'canSelectLab', 'labs'));
    }

    public function update(Request $request, Patient $patient): RedirectResponse
    {
        $this->ensurePatientAccessible($request, $patient);

        $data = $request->validate([
            'full_name' => ['required', 'string', 'max:255'],
            'email' => ['nullable', 'email', 'max:255', 'unique:patients,email,'.$patient->id],
            'phone' => ['nullable', 'string', 'max:30', 'unique:patients,phone,'.$patient->id],
            'dob' => ['nullable', 'date'],
            'gender' => ['nullable', 'string', 'max:20'],
            'lab_id' => [$request->user()->role === 'super_admin' ? 'required' : 'nullable', 'exists:labs,id'],
        ]);

        if ($request->user()->role !== 'super_admin') {
            $data['lab_id'] = $patient->lab_id;
        }

        $patient->update($data);

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

    public function destroy(Request $request, Patient $patient): RedirectResponse
    {
        $this->ensurePatientAccessible($request, $patient);
        $patient->delete();

        return redirect()->route('frontdesk.patients.index')->with('success', 'Patient deleted.');
    }

    public function exportCsv(Request $request): StreamedResponse
    {
        $rows = $this->queryPatients($request)->latest('id')->get();
        $filename = 'patients_'.now()->format('Ymd_His').'.csv';

        return response()->streamDownload(static function () use ($rows): void {
            $handle = fopen('php://output', 'wb');
            fputcsv($handle, ['ID', 'Lab', 'Name', 'Email', 'Phone', 'DOB', 'Gender']);

            foreach ($rows as $patient) {
                fputcsv($handle, [
                    $patient->id,
                    trim((string) optional($patient->lab)->name.' '.(string) optional($patient->lab)->branch_name),
                    $patient->full_name,
                    $patient->email,
                    $patient->phone,
                    optional($patient->dob)->format('Y-m-d'),
                    $patient->gender,
                ]);
            }

            fclose($handle);
        }, $filename, ['Content-Type' => 'text/csv']);
    }

    public function exportPdf(Request $request)
    {
        $patients = $this->queryPatients($request)->latest('id')->get();
        $pdf = Pdf::loadView('frontdesk.patients.export-pdf', compact('patients'));

        return $pdf->download('patients_'.now()->format('Ymd_His').'.pdf');
    }

    public function print(Request $request): View
    {
        $patients = $this->queryPatients($request)->latest('id')->get();
        return view('frontdesk.patients.print', compact('patients'));
    }

    private function queryPatients(Request $request)
    {
        $query = Patient::query()->with('lab:id,name,branch_name');
        $user = $request->user();

        if (in_array($user->role, ['lab_manager', 'lab_staff'], true)) {
            $query->where('lab_id', $user->lab_id);
        } elseif ($labId = (int) $request->query('lab_id')) {
            $query->where('lab_id', $labId);
        }

        if ($term = trim((string) $request->query('q'))) {
            $query->where(function ($q) use ($term): void {
                $q->where('full_name', 'like', "%{$term}%")
                    ->orWhere('email', 'like', "%{$term}%")
                    ->orWhere('phone', 'like', "%{$term}%");
            });
        }

        return $query;
    }

    private function ensurePatientAccessible(Request $request, Patient $patient): void
    {
        $user = $request->user();

        if (in_array($user->role, ['lab_manager', 'lab_staff'], true)) {
            if ((int) $patient->lab_id !== (int) $user->lab_id) {
                abort(403, 'Unauthorized patient access.');
            }
        }
    }
}
