<?php

namespace Modules\ClinicCore\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Storage;
use Modules\ClinicCore\Entities\ClcAppointment;
use Modules\ClinicCore\Entities\ClcConsent;
use Modules\ClinicCore\Entities\ClcConsentSignature;
use Modules\ClinicCore\Entities\ClcConsentTemplate;

/**
 * Issues and prints consent forms for an appointment. Each issued consent
 * stores a rendered snapshot of the template, so it stays legally fixed even
 * if the template is later edited or removed.
 */
class ClcConsentController extends Controller
{
    use \Modules\ClinicCore\Http\Controllers\Traits\LogsClcActivity;

    /** AJAX: the consents already issued for an appointment (tab partial). */
    public function appointmentList(Request $request, $appointmentId)
    {
        if (!auth()->user()->can('clinic.view_appointments')) {
            abort(403, 'Unauthorized action.');
        }

        $appointment = $this->findAppointment($request, $appointmentId);
        $consents = ClcConsent::with('issuedBy')
            ->where('appointment_id', $appointment->id)
            ->orderByDesc('issued_at')->get();

        return view('clinicCore::consents._appointment_list', compact('appointment', 'consents'));
    }

    /** Issue (and snapshot) a consent for an appointment. */
    public function issue(Request $request, $appointmentId)
    {
        if (!auth()->user()->can('clinic.manage_appointments')) {
            abort(403, 'Unauthorized action.');
        }

        $request->validate([
            'consent_template_id'    => 'required|integer',
            'language'               => 'nullable|in:en,ar,both',
            'signatory_name'         => 'nullable|string|max:191',
            'signatory_relationship' => 'nullable|string|max:60',
            'witness_name'           => 'nullable|string|max:191',
            'notes'                  => 'nullable|string|max:1000',
        ]);

        $businessId  = $request->session()->get('user.business_id');
        $appointment = $this->findAppointment($request, $appointmentId);

        $template = ClcConsentTemplate::forBusiness($businessId)
            ->where('status', 'active')
            ->findOrFail($request->consent_template_id);

        $vars = $this->buildVars($appointment, $businessId);

        $consent = ClcConsent::create([
            'business_id'            => $businessId,
            'consent_template_id'    => $template->id,
            'appointment_id'         => $appointment->id,
            'patient_id'                 => $appointment->patient_id,
            'contact_id'             => $appointment->contact_id,
            'code'                   => $template->code,
            'category'               => $template->category,
            'title'                  => $template->name_en,
            'language'               => $request->language ?: 'both',
            'body_en'                => ClcConsentTemplate::render($template->body_en, $vars),
            'body_ar'                => ClcConsentTemplate::render($template->body_ar, $vars),
            'signatory_name'         => $request->signatory_name ?: ($vars['{{owner_name}}'] ?? null),
            'signatory_relationship' => $request->signatory_relationship ?: 'Owner',
            'witness_name'           => $request->witness_name,
            'issued_by'              => auth()->id(),
            'issued_at'              => now(),
            'notes'                  => $request->notes,
        ]);
        $this->drmLog('create', $consent, ['appointment_id' => $appointment->id, 'template' => $template->name_en]);

        return response()->json([
            'success'   => true,
            'msg'       => 'Consent form issued.',
            'consent_id'=> $consent->id,
            'print_url' => route('clinic.consents.print', $consent->id),
        ]);
    }

    /** Printable consent form. */
    public function print(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.view_appointments')) {
            abort(403, 'Unauthorized action.');
        }

        $businessId = $request->session()->get('user.business_id');
        $consent = ClcConsent::with(['pet', 'owner', 'appointment.doctor'])
            ->where('business_id', $businessId)->findOrFail($id);

        $business = \App\Business::findOrFail($businessId);

        return view('clinicCore::consents.print', compact('consent', 'business'));
    }

    /** Delete an issued consent. Blocked once a digital signature exists — use archive() instead. */
    public function destroy(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.manage_appointments')) {
            abort(403, 'Unauthorized action.');
        }

        $businessId = $request->session()->get('user.business_id');
        $consent = ClcConsent::where('business_id', $businessId)->findOrFail($id);

        if ($this->isDigitallySigned($consent)) {
            return response()->json([
                'success' => false,
                'msg'     => 'This consent is digitally signed and cannot be deleted. Archive it instead.',
            ], 422);
        }

        $this->drmLog('delete', $consent, ['before' => $this->_drmFilterAttrs($consent->getAttributes())]);
        $consent->delete();

        return response()->json(['success' => true, 'msg' => __('clinicCore::lang.deleted_successfully')]);
    }

    /** Soft-lock a signed consent (cannot be deleted; archives stay verifiable). */
    public function archive(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.manage_appointments')) {
            abort(403, 'Unauthorized action.');
        }

        $businessId = $request->session()->get('user.business_id');
        $consent = ClcConsent::where('business_id', $businessId)->findOrFail($id);

        $consent->update([
            'archived_at' => now(),
            'archived_by' => auth()->id(),
        ]);
        $this->drmLog('update', $consent, ['action' => 'archived']);

        return response()->json(['success' => true, 'msg' => 'Consent archived.']);
    }

    /**
     * Upload the scanned, hand-signed paper copy.
     *
     * This is the no-tablet path: print the consent, owner signs on paper,
     * staff scans and uploads here. We hash the uploaded file, create a
     * clc_consent_signatures row with method='uploaded_scan', and supersede
     * any previous signature so the audit chain stays intact. The consent
     * then carries the same QR + verify URL as a canvas signature.
     */
    public function uploadSigned(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.manage_appointments')) {
            return response()->json(['success' => false, 'msg' => 'Unauthorized'], 403);
        }

        $request->validate([
            'file' => 'required|file|max:10240|mimes:pdf,jpg,jpeg,png',
        ]);

        $businessId = $request->session()->get('user.business_id');
        $consent = ClcConsent::where('business_id', $businessId)->findOrFail($id);

        if ($consent->is_archived) {
            return response()->json([
                'success' => false,
                'msg'     => 'Consent is archived and cannot accept a new signed copy.',
            ], 422);
        }

        $aptDir = $consent->appointment_id ?: 'unassigned';
        $file   = $request->file('file');

        // Hash the uploaded file BEFORE storing so the hash describes the
        // actual bytes the owner signed. Then write it to disk.
        $binary = file_get_contents($file->getRealPath());
        $hash   = hash('sha256', $binary);

        // Remove the legacy single-file copy if any (we'll re-stamp legacy
        // columns below for backward compat).
        if ($consent->signed_file_path) {
            Storage::disk('public')->delete($consent->signed_file_path);
        }

        $path = $file->store("clc_consents/{$businessId}/{$aptDir}", 'public');

        // Supersede any active signature (canvas OR previous scan) so the
        // chain is preserved and there is exactly one "binding" row.
        ClcConsentSignature::where('consent_id', $consent->id)
            ->whereNull('superseded_at')
            ->update(['superseded_at' => now(), 'updated_at' => now()]);

        ClcConsentSignature::create([
            'consent_id'         => $consent->id,
            'method'             => 'uploaded_scan',
            'signed_by_name'     => $consent->signatory_name ?: optional($consent->owner)->name ?: 'Owner',
            'signed_by_relation' => $consent->signatory_relationship ?: 'Owner',
            'png_path'           => $path,
            'png_hash_sha256'    => $hash,
            'file_mime'          => $file->getMimeType(),
            'file_size'          => $file->getSize(),
            'original_filename'  => $file->getClientOriginalName(),
            'ip'                 => $request->ip(),
            'user_agent'         => substr($request->userAgent() ?: '', 0, 255),
            'signed_at'          => now(),
            'captured_by'        => auth()->id(),
        ]);

        // Keep legacy columns populated for any blade/JS still reading
        // $consent->signed_url or $consent->has_signed_copy.
        $consent->update([
            'signed_file_path'   => $path,
            'signed_file_name'   => $file->getClientOriginalName(),
            'signed_file_size'   => $file->getSize(),
            'signed_mime_type'   => $file->getMimeType(),
            'signed_uploaded_at' => now(),
            'signed_uploaded_by' => auth()->id(),
        ]);

        $this->drmLog('update', $consent, [
            'action' => 'signed_copy_uploaded',
            'hash'   => substr($hash, 0, 12),
        ]);

        $shortHash = substr($hash, 0, 12);

        return response()->json([
            'success'    => true,
            'msg'        => 'Signed copy uploaded — consent is now locked. Re-print to include the QR code.',
            'short_hash' => $shortHash,
            'verify_url' => route('clinic.consents.verify', $shortHash),
            'print_url'  => route('clinic.consents.print', $consent->id),
        ]);
    }

    /** Remove the uploaded signed copy. */
    public function removeSigned(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.manage_appointments')) {
            return response()->json(['success' => false, 'msg' => 'Unauthorized'], 403);
        }

        $businessId = $request->session()->get('user.business_id');
        $consent = ClcConsent::where('business_id', $businessId)->findOrFail($id);

        // Canvas signatures are immutable — you can supersede them with a new
        // signature (canvas or scan), but you can't just "remove" them.
        $active = ClcConsentSignature::activeFor($consent->id);
        if ($active && $active->method === 'canvas') {
            return response()->json([
                'success' => false,
                'msg'     => 'This consent was signed on a touch device — the signature cannot be removed. Re-sign or archive instead.',
            ], 422);
        }

        // For an uploaded scan we supersede the audit row (kept for audit) and
        // delete the file from disk.
        if ($active && $active->method === 'uploaded_scan') {
            $active->update(['superseded_at' => now()]);
            if ($active->png_path) {
                Storage::disk('public')->delete($active->png_path);
            }
        }

        if ($consent->signed_file_path) {
            Storage::disk('public')->delete($consent->signed_file_path);
        }
        $consent->update([
            'signed_file_path'   => null,
            'signed_file_name'   => null,
            'signed_file_size'   => null,
            'signed_mime_type'   => null,
            'signed_uploaded_at' => null,
            'signed_uploaded_by' => null,
        ]);
        $this->drmLog('update', $consent, ['action' => 'signed_copy_removed']);

        return response()->json(['success' => true, 'msg' => 'Signed copy removed.']);
    }

    /**
     * Touch-screen signature pad — staff opens this on an in-clinic tablet and
     * hands it to the owner. Renders without admin chrome and accepts the
     * canvas PNG via storeSignature().
     */
    public function sign(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.view_appointments')) {
            abort(403, 'Unauthorized action.');
        }

        $businessId = $request->session()->get('user.business_id');
        $consent = ClcConsent::with(['pet', 'owner', 'appointment.doctor'])
            ->where('business_id', $businessId)->findOrFail($id);

        if ($consent->is_archived) {
            abort(410, 'This consent has been archived.');
        }

        $business = \App\Business::findOrFail($businessId);
        $existing = ClcConsentSignature::activeFor($consent->id);

        return view('clinicCore::consents.sign', compact('consent', 'business', 'existing'));
    }

    /**
     * Capture the base64-encoded PNG from the canvas pad, hash it (SHA-256),
     * persist to public storage + audit row, supersede any prior signature.
     */
    public function storeSignature(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.manage_appointments')) {
            return response()->json(['success' => false, 'msg' => 'Unauthorized'], 403);
        }

        $request->validate([
            'png'                => 'required|string',
            'signed_by_name'     => 'required|string|max:191',
            'signed_by_relation' => 'nullable|string|max:60',
        ]);

        $businessId = $request->session()->get('user.business_id');
        $consent = ClcConsent::where('business_id', $businessId)->findOrFail($id);

        if ($consent->is_archived) {
            return response()->json(['success' => false, 'msg' => 'Consent is archived.'], 422);
        }

        // Decode base64 PNG (strip the `data:image/png;base64,` prefix if present).
        $payload = $request->input('png');
        if (str_contains($payload, 'base64,')) {
            $payload = explode('base64,', $payload, 2)[1];
        }
        $binary = base64_decode($payload, true);
        if ($binary === false || strlen($binary) < 200) {
            return response()->json(['success' => false, 'msg' => 'Signature image is empty or invalid.'], 422);
        }

        $hash = hash('sha256', $binary);
        $aptId = $consent->appointment_id ?: 'unassigned';
        $dir   = "clc_consents/{$businessId}/{$aptId}";
        $file  = "sig-{$consent->id}-" . now()->timestamp . ".png";
        $path  = "{$dir}/{$file}";

        Storage::disk('public')->put($path, $binary);

        // Supersede any active signature so we keep a full chain.
        ClcConsentSignature::where('consent_id', $consent->id)
            ->whereNull('superseded_at')
            ->update(['superseded_at' => now(), 'updated_at' => now()]);

        ClcConsentSignature::create([
            'consent_id'         => $consent->id,
            'signed_by_name'     => $request->signed_by_name,
            'signed_by_relation' => $request->signed_by_relation,
            'png_path'           => $path,
            'png_hash_sha256'    => $hash,
            'ip'                 => $request->ip(),
            'user_agent'         => substr($request->userAgent() ?: '', 0, 255),
            'signed_at'          => now(),
            'captured_by'        => auth()->id(),
        ]);

        // Stamp the signatory name on the consent so the print view picks it up.
        $consent->update([
            'signatory_name'         => $request->signed_by_name,
            'signatory_relationship' => $request->signed_by_relation ?: ($consent->signatory_relationship ?: 'Owner'),
        ]);

        $this->drmLog('update', $consent, [
            'action' => 'digital_signature_captured',
            'hash'   => substr($hash, 0, 12),
        ]);

        return response()->json([
            'success'    => true,
            'msg'        => 'Signature captured.',
            'short_hash' => substr($hash, 0, 12),
            'print_url'  => route('clinic.consents.print', $consent->id),
            'verify_url' => route('clinic.consents.verify', substr($hash, 0, 12)),
        ]);
    }

    /** True if the consent already carries an active (non-superseded) signature. */
    private function isDigitallySigned(ClcConsent $consent): bool
    {
        return ClcConsentSignature::where('consent_id', $consent->id)
            ->whereNull('superseded_at')
            ->exists();
    }

    private function findAppointment(Request $request, $id): ClcAppointment
    {
        $businessId = $request->session()->get('user.business_id');
        return ClcAppointment::with(['pet', 'owner', 'doctor'])
            ->where('business_id', $businessId)->findOrFail($id);
    }

    /**
     * Build the {{placeholder}} => value map for an appointment.
     */
    private function buildVars(ClcAppointment $appointment, int $businessId): array
    {
        $patient   = $appointment->pet;
        $owner = $appointment->owner;
        $business = \App\Business::find($businessId);

        $patientSex = '';
        if ($patient && $patient->gender) {
            $patientSex = ucfirst($patient->gender) . ($patient->neutered ? ' · Neutered' : '');
        }
        $patientAge = ($patient && $patient->age !== null) ? $patient->age . ' yr' . ($patient->age == 1 ? '' : 's') : '';

        $address = '';
        if ($owner) {
            $address = implode(', ', array_filter([
                $owner->address_line_1 ?? null,
                $owner->address_line_2 ?? null,
                $owner->city ?? null,
                $owner->state ?? null,
                $owner->country ?? null,
            ]));
        }

        $dateFmt = session('business.date_format', 'd M Y') ?: 'd M Y';

        return [
            '{{clinic_name}}'      => $business->name ?? 'Clinic',
            '{{pet_name}}'         => $patient->patient_name ?? '—',
            '{{species}}'          => $patient->species ?? '—',
            '{{breed}}'            => $patient->breed ?? '—',
            '{{pet_sex}}'          => $patientSex ?: '—',
            '{{pet_age}}'          => $patientAge ?: '—',
            '{{owner_name}}'       => $owner->name ?? '—',
            '{{owner_phone}}'      => $owner->mobile ?? '—',
            '{{owner_email}}'      => $owner->email ?? '—',
            '{{owner_address}}'    => $address ?: '—',
            '{{doctor}}'           => optional($appointment->doctor)->name ?? '—',
            '{{appointment_date}}' => optional($appointment->appointment_date)->format($dateFmt) ?? '—',
            '{{appointment_ref}}'  => $appointment->ref_no ?? '—',
            '{{appointment_type}}' => $appointment->type ? ucfirst($appointment->type) : '—',
            '{{today}}'            => now()->format($dateFmt),
        ];
    }
}
