<?php

namespace App\Http\Controllers;

use App\Models\CheckMeasure;
use App\Models\Document;
use App\Models\Installation;
use App\Models\Job;
use App\Models\ActivityLog;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class DocumentController extends Controller
{
    // ── Authorization helpers ─────────────────────────────────────────────────

    /**
     * Supported documentable short-types → FQCN map.
     */
    private const TYPE_MAP = [
        'Contract'     => \App\Models\Contract::class,
        'CheckMeasure' => \App\Models\CheckMeasure::class,
        'Delivery'     => \App\Models\Delivery::class,
        'Installation' => \App\Models\Installation::class,
        'Completion'   => \App\Models\Completion::class,
        'Job'          => \App\Models\Job::class,
        'Lead'         => \App\Models\Lead::class,
    ];

    /**
     * Resolve the Job ID associated with a document's parent record.
     * Returns null for Job itself (job_id IS the id) or Lead (no job yet).
     */
    private function resolveJobId(Document $document): ?int
    {
        $model = $document->documentable;
        if (! $model) {
            return null;
        }
        if ($model instanceof Job) {
            return $model->id;
        }
        return $model->job_id ?? null;
    }

    /**
     * Check whether the authenticated user is permitted to access a document.
     *
     * office_admin and project_manager have unrestricted access.
     * Scoped roles may only access documents whose parent record is linked
     * to a job they are assigned on:
     *   - sales_consultant  → job.consultant_id
     *   - lead_installer    → installations.lead_installer_id on the same job
     *   - check_measurer    → check_measures.measurer_id on the same job
     */
    private function userCanAccess(Document $document, User $user): bool
    {
        $role = $user->role?->name ?? 'staff';

        if (in_array($role, ['office_admin', 'project_manager'])) {
            return true;
        }

        $jobId = $this->resolveJobId($document);

        // Orphaned / lead-only document — allow upload/view for now
        if (! $jobId) {
            return in_array($role, ['sales_consultant']);
        }

        return match ($role) {
            'sales_consultant' => Job::where('id', $jobId)
                ->where('consultant_id', $user->id)
                ->exists(),

            'lead_installer'   => Installation::where('job_id', $jobId)
                ->where('lead_installer_id', $user->id)
                ->exists(),

            'check_measurer'   => CheckMeasure::where('job_id', $jobId)
                ->where('measurer_id', $user->id)
                ->exists(),

            default            => false,
        };
    }

    /**
     * Resolve the documentable record from a short type + ID, verify it exists.
     * Returns the model instance or null.
     */
    private function resolveDocumentable(string $type, int $id): ?object
    {
        $fqcn = self::TYPE_MAP[$type] ?? null;
        if (! $fqcn) {
            return null;
        }
        return $fqcn::find($id);
    }

    // ── Controller actions ────────────────────────────────────────────────────

    /**
     * Upload a document and attach it to any documentable model.
     * POST /documents
     */
    public function store(Request $request)
    {
        $request->validate([
            'documentable_type' => 'required|string|in:Contract,CheckMeasure,Delivery,Installation,Completion,Job,Lead',
            'documentable_id'   => 'required|integer',
            'file'              => 'required|file|max:20480|mimes:pdf,doc,docx,jpg,jpeg,png,gif,xls,xlsx,csv,txt,zip',
            'name'              => 'nullable|string|max:255',
        ]);

        $type   = $request->input('documentable_type');
        $id     = (int) $request->input('documentable_id');
        $user   = auth()->user();

        // Verify the target record exists
        $parent = $this->resolveDocumentable($type, $id);
        abort_if(! $parent, 404, 'The target record does not exist.');

        // Build a temporary Document-like object to run the ownership check
        $stub                    = new Document();
        $stub->documentable_type = self::TYPE_MAP[$type];
        $stub->documentable_id   = $id;
        $stub->setRelation('documentable', $parent);

        if (! $this->userCanAccess($stub, $user)) {
            abort(403, 'You are not assigned to this record.');
        }

        $file   = $request->file('file');
        $folder = 'documents/' . strtolower($type) . '/' . $id;
        $path   = $file->store($folder, 'local');

        $document = Document::create([
            'documentable_type' => self::TYPE_MAP[$type],
            'documentable_id'   => $id,
            'name'              => $request->input('name') ?: $file->getClientOriginalName(),
            'file_path'         => $path,
            'file_type'         => $file->getClientMimeType(),
            'file_size'         => $file->getSize(),
            'uploaded_by'       => $user->id,
        ]);

        ActivityLog::create([
            'user_id'       => $user->id,
            'loggable_type' => $type,
            'loggable_id'   => $id,
            'action'        => 'Document Uploaded',
            'description'   => "File '{$document->name}' uploaded to {$type} #{$id}",
        ]);

        if ($request->expectsJson()) {
            return response()->json(['success' => true, 'document' => $document]);
        }

        return back()->with('success', "Document '{$document->name}' uploaded successfully.");
    }

    /**
     * Delete a document.
     * DELETE /documents/{document}
     * Route is gated to office_admin, project_manager, sales_consultant.
     * Additional check: must be the uploader OR an admin/PM.
     */
    public function destroy(Document $document)
    {
        $user = auth()->user();
        $role = $user->role?->name ?? 'staff';

        $isAdminOrPm  = in_array($role, ['office_admin', 'project_manager']);
        $isOwnUpload  = $document->uploaded_by === $user->id;

        // Non-admin/PM users must both be the uploader AND have assignment-scoped
        // access to the parent record (same scope enforced on download).
        if (!$isAdminOrPm) {
            abort_unless($isOwnUpload, 403, 'You can only delete documents you uploaded.');
            abort_unless($this->userCanAccess($document, $user), 403, 'You do not have permission to delete this document.');
        }

        $filePath = $document->file_path;
        $type     = class_basename($document->documentable_type);
        $id       = $document->documentable_id;
        $name     = $document->name;

        $document->delete();
        Storage::disk('local')->delete($filePath);

        ActivityLog::create([
            'user_id'       => $user->id,
            'loggable_type' => $type,
            'loggable_id'   => $id,
            'action'        => 'Document Deleted',
            'description'   => "File '{$name}' removed from {$type} #{$id}",
        ]);

        return back()->with('success', "Document '{$name}' deleted.");
    }

    /**
     * Download / serve a document.
     * GET /documents/{document}/download
     * User must have access to the parent record.
     */
    public function download(Document $document)
    {
        $user = auth()->user();

        if (! $this->userCanAccess($document, $user)) {
            abort(403, 'You do not have permission to download this document.');
        }

        abort_unless(Storage::disk('local')->exists($document->file_path), 404);

        return Storage::disk('local')->download($document->file_path, $document->name);
    }
}
