<?php

namespace Mtc\Plugins\WeightVerification\Http\Controllers;

use Carbon\Carbon;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;
use Mtc\Modules\Members\Classes\Auth;
use Mtc\Modules\Members\Models\Member;
use Mtc\Plugins\Clinic\Src\Clinic;
use Mtc\Plugins\Clinic\Src\Logger;
use Mtc\Plugins\Clinic\Src\Models\PatientFile;
use Mtc\Plugins\Clinic\Src\Uploader;
use Mtc\Plugins\WeightVerification\Src\Models\WeightVerification;
use Mtc\Plugins\WeightVerification\Src\WeightVerificationService;

class WeightVerificationController extends BaseController
{
    private Member $member;

    private WeightVerificationService $service;

    public function __construct(WeightVerificationService $service)
    {
        $this->service = $service;

        $this->middleware(function ($request, $next) {
            $this->member = Auth::getLoggedInMember();

            if (!$this->member->id) {
                return redirect(route('members-login'));
            }

            return $next($request);
        });
    }

    /**
     * Weight verification dashboard tab.
     */
    public function index()
    {
        $page_meta['title'] = config('app.name') . ' | Weight Verification';

        // All flagged orders awaiting the patient's photos, each rendered with
        // its own submit form. `latest` backs the confirmation/empty states.
        $verifications = $this->service->actionRequiredForMember($this->member->id);
        $verifications->load(['order.items.item', 'order.items.item_size']);

        return template('members/weight_verification.twig', [
            'page_meta' => $page_meta,
            'member' => $this->member,
            'verifications' => $verifications,
            'latest' => $this->service->latestForMember($this->member->id),
            'success' => session('success'),
            'error' => session('error'),
        ]);
    }

    /**
     * Receives the two captured photos and submits them for review.
     */
    public function store(Request $request): RedirectResponse
    {
        $verificationId = (int) $request->input('verification_id');
        $verification = $verificationId
            ? $this->service->forMemberAndId($this->member->id, $verificationId)
            : null;

        if (empty($verification) || !$verification->action_required) {
            return redirect(route('members-weight-verification', [], false));
        }

        if (!$request->hasFile('front_photo') || !$request->hasFile('side_photo')) {
            return redirect(route('members-weight-verification', [], false))
                ->with('error', 'Both photos are required before you can submit');
        }

        $uploads = [
            'front_file_id' => [$request->file('front_photo'), PatientFile::FILE_TYPE_WEIGHT_FRONT],
            'side_file_id' => [$request->file('side_photo'), PatientFile::FILE_TYPE_WEIGHT_SIDE],
        ];

        $stored = [];
        foreach ($uploads as $field => [$uploadedFile, $type]) {
            $file = $this->storePhoto($verification, $uploadedFile, $type, PatientFile::$types[$type]);
            if (empty($file)) {
                $this->discard($stored);
                return redirect(route('members-weight-verification', [], false))
                    ->with('error', 'We could not process your photos. Please try again.');
            }
            $stored[$field] = $file;
        }

        // Conditional update guards against a concurrent double-submit overwriting
        // a record that has already moved past the action-required state.
        $updated = WeightVerification::query()
            ->where('id', $verification->id)
            ->whereIn('status', WeightVerification::ACTION_REQUIRED_STATUSES)
            ->update([
                'status' => WeightVerification::STATUS_SUBMITTED,
                'front_file_id' => $stored['front_file_id']->id,
                'side_file_id' => $stored['side_file_id']->id,
                'submitted_at' => Carbon::now(),
                'decline_reason' => null,
            ]);

        if (!$updated) {
            $this->discard($stored);
            return redirect(route('members-weight-verification', [], false));
        }

        // Remove any superseded photos from a previous (resubmitted) attempt.
        $this->discardSuperseded($verification, $stored);

        return redirect(route('members-weight-verification', [], false))
            ->with('success', 'Your photos have been submitted for review.');
    }

    /**
     * Delete patient files stored during a request that did not complete.
     *
     * @param PatientFile[] $files
     */
    private function discard(array $files): void
    {
        foreach ($files as $file) {
            $this->deletePhoto($file);
        }
    }

    /**
     * Delete the previously submitted photos that have just been replaced.
     *
     * @param PatientFile[] $stored
     */
    private function discardSuperseded(WeightVerification $verification, array $stored): void
    {
        $newIds = [$stored['front_file_id']->id, $stored['side_file_id']->id];

        foreach (array_filter([$verification->front_file_id, $verification->side_file_id]) as $oldId) {
            if (!in_array($oldId, $newIds, true) && $old = PatientFile::find($oldId)) {
                $this->deletePhoto($old);
            }
        }
    }

    /**
     * Stores a single uploaded photo as a private patient file.
     */
    private function storePhoto(WeightVerification $verification, $uploadedFile, string $type, string $title): ?PatientFile
    {
        $uploader = new Uploader([
            'public' => false,
            'type' => $type,
            'title' => $title,
            // Patient-identifiable clinical photos: restrict to images explicitly
            // rather than inheriting the Uploader's broader default formats.
            'allowed_formats' => ['jpg', 'jpeg', 'png'],
            'max_file_size' => 16 * 1024 * 1024,
            // Tag the file to this plugin so the Clinic retention/cleanup
            // (PatientFile::deleteFiles) can find it by module + module_id.
            'module' => WeightVerification::AUDIT_MODULE,
            'id' => $verification->id,
        ]);

        $file = $uploader->uploadFile($uploadedFile);

        if (empty($file)) {
            return null;
        }

        $file->update([
            'member_id' => $this->member->id,
            'date_provided' => Carbon::now()->format('Y-m-d'),
        ]);

        // Special Category clinical photo: record its creation in the Clinic
        // audit trail, matching every other PatientFile write path.
        (new Logger($file, Clinic::getActor(), $this->member->id))
            ->log(Logger::FILE, Logger::ACTION_CREATED, $file->toArray());

        return $file;
    }

    /**
     * Delete a stored photo and record the deletion in the Clinic audit trail.
     *
     * deleteFile() itself does not log, so the audit entry is written here to
     * keep the file's create/delete history complete for DPA / ISO 27001.
     */
    private function deletePhoto(PatientFile $file): void
    {
        (new Logger($file, Clinic::getActor(), $file->member_id))
            ->log(Logger::FILE, Logger::ACTION_DELETED, $file->toArray());

        $file->deleteFile();
    }
}
