<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\Document;
use Carbon\Carbon;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

class DocumentController extends Controller
{
    public function dashboard()
    {
        $query = $this->baseVisibleQuery();

        $kpis = [
            'total' => (clone $query)->count(),
            'active' => (clone $query)->where('status', 'active')->count(),
            'expiring_soon' => (clone $query)
                ->whereDate('expires_on', '>=', now()->toDateString())
                ->whereDate('expires_on', '<=', now()->addDays(30)->toDateString())
                ->count(),
            'expired' => (clone $query)->whereDate('expires_on', '<', now()->toDateString())->count(),
            'mandatory_missing' => $this->mandatoryMissingCount(),
        ];

        $expiringSoon = (clone $query)
            ->with('uploader')
            ->whereDate('expires_on', '>=', now()->toDateString())
            ->whereDate('expires_on', '<=', now()->addDays(30)->toDateString())
            ->orderBy('expires_on')
            ->limit(8)
            ->get();

        $recentDocuments = (clone $query)
            ->with('uploader')
            ->orderByDesc('created_at')
            ->limit(8)
            ->get();

        return view('documents.dashboard', compact('kpis', 'expiringSoon', 'recentDocuments'));
    }

    public function index()
    {
        $filters = [
            'search' => (string) request('search', ''),
            'category' => (string) request('category', ''),
            'status' => (string) request('status', ''),
            'visibility' => (string) request('visibility', ''),
            'from' => (string) request('from', ''),
            'to' => (string) request('to', ''),
        ];

        $documents = $this->baseVisibleQuery()
            ->with('uploader')
            ->search($filters['search'])
            ->filterCategory($filters['category'])
            ->filterStatus($filters['status'])
            ->filterVisibility($filters['visibility'])
            ->when($filters['from'] !== '', fn ($q) => $q->whereDate('created_at', '>=', $filters['from']))
            ->when($filters['to'] !== '', fn ($q) => $q->whereDate('created_at', '<=', $filters['to']))
            ->orderByDesc('created_at')
            ->paginate(15)
            ->withQueryString();

        $categories = config('employee_documents.upload_categories', []);
        $statusOptions = ['active', 'expiring_soon', 'expired', 'archived'];
        $visibilityOptions = ['company' => 'Company', 'private' => 'Private', 'team' => 'Team'];

        return view('documents.index', compact('documents', 'filters', 'categories', 'statusOptions', 'visibilityOptions'));
    }

    public function create()
    {
        $categories = config('employee_documents.upload_categories', []);
        $visibilityOptions = ['company' => 'Company', 'private' => 'Private', 'team' => 'Team'];

        return view('documents.create', compact('categories', 'visibilityOptions'));
    }

    public function store(Request $request)
    {
        $this->ensureCanCreate();

        $categoryKeys = implode(',', array_keys(config('employee_documents.upload_categories', [])));
        $validated = $request->validate([
            'name' => 'required|string|max:191',
            'category' => "required|string|in:{$categoryKeys}",
            'type' => 'nullable|string|max:50',
            'document_number' => 'nullable|string|max:100',
            'issued_on' => 'nullable|date',
            'expires_on' => 'nullable|date|after_or_equal:issued_on',
            'visibility' => 'nullable|in:company,private,team',
            'notes' => 'nullable|string|max:2000',
            'is_mandatory' => 'nullable|boolean',
            'document' => 'required|file|max:10240|mimes:pdf,doc,docx,xls,xlsx,txt,png,jpg,jpeg',
        ]);
        $file = $request->file('document');
        /** @var User $user */
        $user = $request->user();
        $path = $file->store('documents/' . ($user->company_id ?? 'shared'), 'public');
        Document::create([
            'company_id' => $user->company_id,
            'name' => $validated['name'],
            'type' => $validated['type'] ?? $validated['category'] ?? $file->getClientOriginalExtension(),
            'category' => $validated['category'],
            'document_number' => $validated['document_number'] ?? null,
            'issued_on' => $validated['issued_on'] ?? null,
            'expires_on' => $validated['expires_on'] ?? null,
            'status' => $this->resolveStatusFromDates($validated['expires_on'] ?? null),
            'visibility' => $validated['visibility'] ?? 'company',
            'is_mandatory' => (bool) ($validated['is_mandatory'] ?? false),
            'notes' => $validated['notes'] ?? null,
            'path' => $path,
            'uploaded_by' => $user->id,
        ]);
        return redirect()->route('documents.index')->with('success', 'Document uploaded successfully.');
    }

    public function download(Document $document): BinaryFileResponse
    {
        $this->authorizeDocumentView($document);

        abort_if(! $document->path || ! Storage::disk('public')->exists($document->path), 404);

        return response()->download(Storage::disk('public')->path($document->path), basename($document->path));
    }

    public function destroy(Document $document)
    {
        $this->ensureCanManageDocument($document);
        if ($document->path && Storage::disk('public')->exists($document->path)) {
            Storage::disk('public')->delete($document->path);
        }
        $document->delete();
        $redirect = request()->query('redirect', route('documents.index'));
        return redirect($redirect)->with('success', 'Document deleted.');
    }

    private function baseVisibleQuery(): \Illuminate\Database\Eloquent\Builder
    {
        /** @var User|null $user */
        $user = request()->user();
        $query = Document::query();
        if (! $user) {
            return $query->whereRaw('1=0');
        }

        if ($user->company_id) {
            $query->where('company_id', $user->company_id);
        }

        if ($user->isTenantManager() || $user->hasPermission('documents_view')) {
            return $query;
        }

        return $query->where(function ($q) use ($user): void {
            $q->where('uploaded_by', $user->id)
                ->orWhere(function ($privateQ): void {
                    $privateQ->whereNull('visibility')->orWhere('visibility', '!=', 'private');
                });
        });
    }

    private function ensureCanCreate(): void
    {
        /** @var User|null $user */
        $user = request()->user();
        abort_unless($user && ($user->isTenantManager() || $user->hasPermission('documents_create')), 403);
    }

    private function ensureCanManageDocument(Document $document): void
    {
        /** @var User|null $user */
        $user = request()->user();
        abort_unless($user, 403);
        if ($user->company_id && $document->company_id !== $user->company_id) {
            abort(403);
        }
        abort_unless($user->isTenantManager() || $user->hasPermission('documents_edit'), 403);
    }

    private function authorizeDocumentView(Document $document): void
    {
        /** @var User|null $user */
        $user = request()->user();
        abort_unless($user, 403);
        if ($user->company_id && $document->company_id !== $user->company_id) {
            abort(403);
        }
        if ($user->isTenantManager() || $user->hasPermission('documents_view')) {
            return;
        }
        abort_unless($document->uploaded_by === $user->id || $document->visibility !== 'private', 403);
    }

    private function resolveStatusFromDates(?string $expiresOn): string
    {
        if (! $expiresOn) {
            return 'active';
        }

        $expiry = Carbon::parse($expiresOn);
        if ($expiry->isPast()) {
            return 'expired';
        }
        if ($expiry->lte(now()->addDays(30))) {
            return 'expiring_soon';
        }
        return 'active';
    }

    private function mandatoryMissingCount(): int
    {
        /** @var User|null $user */
        $user = request()->user();
        if (! $user?->company_id) {
            return 0;
        }

        $mandatory = collect(config('employee_documents.mandatory', []));
        if ($mandatory->isEmpty()) {
            return 0;
        }

        $uploaded = Document::query()
            ->where('company_id', $user->company_id)
            ->select(['category', 'type', 'name'])
            ->get();

        $uploadedTokens = $uploaded
            ->flatMap(function (Document $doc) {
                return [
                    strtolower((string) $doc->category),
                    strtolower((string) $doc->type),
                    strtolower((string) $doc->name),
                ];
            })
            ->filter()
            ->values()
            ->all();

        $missing = 0;
        foreach ($mandatory as $key => $meta) {
            $needles = array_merge([$key], (array) ($meta['aliases'] ?? []));
            $present = false;
            foreach ($needles as $needle) {
                $needle = strtolower((string) $needle);
                if (collect($uploadedTokens)->contains(fn (string $token) => str_contains($token, $needle))) {
                    $present = true;
                    break;
                }
            }
            if (! $present) {
                $missing++;
            }
        }
        return $missing;
    }
}
