<?php

namespace Modules\MedDocuments\Http\Controllers;

use Illuminate\Http\Request;
use Modules\MedDocuments\Entities\MeddocDocument;
use Modules\MedDocuments\Services\Watermarker;

/**
 * The ONLY path to a document's bytes for authed users. Every response here is
 * watermarked (viewer name + datetime) and audited, EXCEPT downloadOriginal(),
 * which serves the raw file and requires the meddoc.download_original
 * permission.
 *
 *  - view()            → inline, watermarked (image = GD overlay, pdf = mPDF
 *                        stamp, else HTML viewer w/ CSS overlay). Logs `view`.
 *  - printView()       → HTML print page with a CSS watermark overlay. Logs `print`.
 *  - download()        → watermarked file as an attachment. Logs `download`.
 *  - downloadOriginal()→ raw, unwatermarked; perm-gated. Logs `download`.
 */
class FileController extends MedDocBaseController
{
    public function __construct(protected Watermarker $watermarker)
    {
    }

    public function view(Request $request, $id)
    {
        $this->guard('meddoc.view');

        $document = $this->find($id);
        $version = $this->docs()->versionToServe($document, $request->get('version'));
        if (! $version) {
            abort(404);
        }

        $bytes = $this->docs()->bytes($version);
        if ($bytes === null) {
            abort(404);
        }

        $caption = Watermarker::caption(auth()->user()->user_full_name ?? auth()->user()->username ?? null);

        $this->docs()->log($document, 'view', $version->id, $this->userId());

        // Images → watermark the pixels and serve inline.
        if ($version->isImage()) {
            $out = $this->watermarker->image($bytes, $caption);

            return response($out, 200, [
                'Content-Type'        => 'image/png',
                'Content-Disposition' => 'inline; filename="' . $this->safeName($version->original_name, 'png') . '"',
                'Cache-Control'       => 'private, no-store',
            ]);
        }

        // PDFs → stamp each page with mPDF; if that fails, fall back to the HTML viewer.
        if ($version->isPdf()) {
            $stamped = $this->watermarker->pdf($bytes, $caption);
            if ($stamped !== null) {
                return response($stamped, 200, [
                    'Content-Type'        => 'application/pdf',
                    'Content-Disposition' => 'inline; filename="' . $this->safeName($version->original_name, 'pdf') . '"',
                    'Cache-Control'       => 'private, no-store',
                ]);
            }
        }

        // Fallback: HTML viewer page with a CSS watermark overlay (embeds the
        // original via the perm-checked or inline route as appropriate).
        return view('meddocuments::public.viewer', [
            'document'   => $document,
            'version'    => $version,
            'caption'    => $caption,
            'sourceUrl'  => route('meddoc.documents.download-original', $document->id),
            'canOriginal' => auth()->user()->can('meddoc.download_original'),
            'isPublic'   => false,
        ]);
    }

    public function printView(Request $request, $id)
    {
        $this->guard('meddoc.view');

        $document = $this->find($id);
        $version = $this->docs()->versionToServe($document, $request->get('version'));
        if (! $version) {
            abort(404);
        }

        $caption = Watermarker::caption(auth()->user()->user_full_name ?? auth()->user()->username ?? null);
        $this->docs()->log($document, 'print', $version->id, $this->userId());

        return view('meddocuments::public.print', [
            'document'  => $document,
            'version'   => $version,
            'caption'   => $caption,
            'viewUrl'   => route('meddoc.documents.view', [$document->id, 'version' => $version->id]),
        ]);
    }

    public function download(Request $request, $id)
    {
        $this->guard('meddoc.view');

        $document = $this->find($id);
        $version = $this->docs()->versionToServe($document, $request->get('version'));
        if (! $version) {
            abort(404);
        }

        $bytes = $this->docs()->bytes($version);
        if ($bytes === null) {
            abort(404);
        }

        $caption = Watermarker::caption(auth()->user()->user_full_name ?? auth()->user()->username ?? null);
        $this->docs()->log($document, 'download', $version->id, $this->userId());

        // Watermark where we can; otherwise the raw bytes are returned as an
        // attachment (download of a non-image/non-pdf can't be visually marked).
        if ($version->isImage()) {
            $bytes = $this->watermarker->image($bytes, $caption);

            return $this->attachment($bytes, $this->safeName($version->original_name, 'png'), 'image/png');
        }

        if ($version->isPdf()) {
            $stamped = $this->watermarker->pdf($bytes, $caption);
            if ($stamped !== null) {
                return $this->attachment($stamped, $this->safeName($version->original_name, 'pdf'), 'application/pdf');
            }
        }

        return $this->attachment($bytes, $version->original_name, $version->mime ?: 'application/octet-stream');
    }

    /**
     * Raw, unwatermarked download — requires meddoc.download_original.
     */
    public function downloadOriginal(Request $request, $id)
    {
        $this->guard('meddoc.download_original');

        $document = $this->find($id);
        $version = $this->docs()->versionToServe($document, $request->get('version'));
        if (! $version) {
            abort(404);
        }

        $bytes = $this->docs()->bytes($version);
        if ($bytes === null) {
            abort(404);
        }

        $this->docs()->log($document, 'download', $version->id, $this->userId());

        return $this->attachment($bytes, $version->original_name, $version->mime ?: 'application/octet-stream');
    }

    /*
    |--------------------------------------------------------------------------
    | Internals
    |--------------------------------------------------------------------------
    */

    protected function find($id): MeddocDocument
    {
        return MeddocDocument::forBusiness($this->businessId())
            ->where('status', '!=', 'deleted')
            ->findOrFail($id);
    }

    protected function attachment(string $bytes, string $name, string $mime)
    {
        return response($bytes, 200, [
            'Content-Type'        => $mime,
            'Content-Length'      => strlen($bytes),
            'Content-Disposition' => 'attachment; filename="' . addslashes($name) . '"',
            'Cache-Control'       => 'private, no-store',
        ]);
    }

    protected function safeName(string $name, string $forceExt): string
    {
        $base = pathinfo($name, PATHINFO_FILENAME);

        return addslashes($base) . '.' . $forceExt;
    }
}
