<?php

namespace App\Http\Controllers\Admin;

use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Models\Download;
use App\Models\DownloadCategory;
use App\Models\Media;
use App\Services\UploadService;

/**
 * ITSWEBER CMS – Admin Download Controller (Phase 8)
 *
 * Featured image is attached via media_id (FK → iw_media).
 * The downloadable file is uploaded directly and stored as a path in file_path.
 */
class DownloadController extends Controller
{
    /**
     * Allowed MIME types for downloadable files.
     * Images are intentionally omitted – use the Media Manager for those.
     */
    private const ALLOWED_FILE_MIMES = [
        'application/pdf'                                                              => 'pdf',
        'application/zip'                                                              => 'zip',
        'application/x-zip-compressed'                                                 => 'zip',
        'application/x-zip'                                                            => 'zip',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document'      => 'docx',
        'application/msword'                                                           => 'doc',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'            => 'xlsx',
        'application/vnd.ms-excel'                                                     => 'xls',
        'application/vnd.openxmlformats-officedocument.presentationml.presentation'    => 'pptx',
        'application/vnd.ms-powerpoint'                                                => 'ppt',
        'application/vnd.oasis.opendocument.text'                                      => 'odt',
        'application/vnd.oasis.opendocument.spreadsheet'                               => 'ods',
        'application/vnd.oasis.opendocument.presentation'                              => 'odp',
        'text/plain'                                                                   => 'txt',
        'text/csv'                                                                     => 'csv',
        'application/json'                                                             => 'json',
        'application/xml'                                                              => 'xml',
        'text/xml'                                                                     => 'xml',
        'application/x-rar-compressed'                                                 => 'rar',
        'application/vnd.rar'                                                          => 'rar',
        'application/x-7z-compressed'                                                  => '7z',
        'image/jpeg'                                                                   => 'jpg',
        'image/png'                                                                    => 'png',
        'image/gif'                                                                    => 'gif',
        'image/webp'                                                                   => 'webp',
        'image/svg+xml'                                                                => 'svg',
    ];

    /** Maximum download file size: 50 MB */
    private const MAX_FILE_SIZE = 50 * 1024 * 1024;

    // -------------------------------------------------------------------------
    // LIST
    // -------------------------------------------------------------------------

    public function index(Request $request, array $params = []): void
    {
        $search     = trim($request->input('search', ''));
        $status     = $request->input('status', '');
        $categoryId = (int) $request->input('category_id', 0);

        $downloads  = Download::adminList($search, $status, $categoryId);
        $categories = DownloadCategory::all('name');

        $this->renderAdmin('downloads/index', [
            'pageTitle'  => __('admin.downloads.title'),
            'downloads'  => $downloads,
            'categories' => $categories,
            'search'     => $search,
            'status'     => $status,
            'categoryId' => $categoryId,
            'auth'       => $this->auth(),
            'csrf'       => $this->csrf(),
        ]);
    }

    // -------------------------------------------------------------------------
    // CREATE
    // -------------------------------------------------------------------------

    public function create(Request $request, array $params = []): void
    {
        $categories = DownloadCategory::all('name');

        $this->renderAdmin('downloads/form', [
            'pageTitle'  => __('admin.downloads.create'),
            'download'   => null,
            'categories' => $categories,
            'auth'       => $this->auth(),
            'csrf'       => $this->csrf(),
        ]);
    }

    public function store(Request $request, array $params = []): void
    {
        $this->validateCsrf();

        $data = $this->extractFormData($request);

        if (empty($data['title'])) {
            set_flash('error', __('messages.error.title_required'));
            Response::redirect('/admin/downloads/create');
            return;
        }

        if (empty($data['slug'])) {
            $data['slug'] = slugify($data['title']);
        }

        $existing = Download::findBySlug($data['slug']);
        if ($existing) {
            $data['slug'] = $data['slug'] . '-' . bin2hex(random_bytes(4));
        }

        $data['created_by'] = $this->auth()->userId();
        $data['created_at'] = date('Y-m-d H:i:s');
        $data['updated_at'] = date('Y-m-d H:i:s');

        Download::create($data);

        set_flash('success', 'Download "' . $data['title'] . '" created successfully.');
        Response::redirect('/admin/downloads');
    }

    // -------------------------------------------------------------------------
    // EDIT / UPDATE
    // -------------------------------------------------------------------------

    public function edit(Request $request, array $params = []): void
    {
        $id       = (int) ($params['id'] ?? 0);
        $download = Download::findForAdmin($id);

        if (!$download) {
            set_flash('error', __('messages.not_found', ['item' => 'Download']));
            Response::redirect('/admin/downloads');
            return;
        }

        $categories = DownloadCategory::all('name');

        $this->renderAdmin('downloads/form', [
            'pageTitle'  => __('admin.downloads.edit'),
            'download'   => $download,
            'categories' => $categories,
            'auth'       => $this->auth(),
            'csrf'       => $this->csrf(),
        ]);
    }

    public function update(Request $request, array $params = []): void
    {
        $this->validateCsrf();

        $id       = (int) ($params['id'] ?? 0);
        $download = Download::findForAdmin($id);

        if (!$download) {
            set_flash('error', __('messages.not_found', ['item' => 'Download']));
            Response::redirect('/admin/downloads');
            return;
        }

        $data = $this->extractFormData($request);

        if (empty($data['title'])) {
            set_flash('error', __('messages.error.title_required'));
            Response::redirect('/admin/downloads/edit/' . $id);
            return;
        }

        if (empty($data['slug'])) {
            $data['slug'] = slugify($data['title']);
        }

        $slugConflict = Download::findBySlug($data['slug']);
        if ($slugConflict && (int) $slugConflict['id'] !== $id) {
            $data['slug'] = $data['slug'] . '-' . bin2hex(random_bytes(4));
        }

        // Handle file removal
        if ($request->input('remove_file') === '1') {
            $this->deleteDownloadFile($download['file_path'] ?? null);
            $data['file_path'] = null;
            $data['file_name'] = null;
            $data['file_size'] = null;
            $data['file_type'] = null;
        } elseif (empty($data['file_path'])) {
            // Keep existing file info if no new file provided
            $data['file_path'] = $download['file_path'];
            $data['file_name'] = $download['file_name'];
            $data['file_size'] = $download['file_size'];
            $data['file_type'] = $download['file_type'];
        }

        $data['updated_at'] = date('Y-m-d H:i:s');

        Download::updateById($id, $data);

        set_flash('success', 'Download updated successfully.');
        Response::redirect('/admin/downloads');
    }

    // -------------------------------------------------------------------------
    // DELETE
    // -------------------------------------------------------------------------

    public function delete(Request $request, array $params = []): void
    {
        $this->validateCsrf();

        $id       = (int) ($params['id'] ?? 0);
        $download = Download::findForAdmin($id);

        if (!$download) {
            set_flash('error', __('messages.not_found', ['item' => 'Download']));
            Response::redirect('/admin/downloads');
            return;
        }

        // Delete the stored download file from disk
        $this->deleteDownloadFile($download['file_path'] ?? null);

        Download::deleteById($id);
        set_flash('success', 'Download "' . $download['title'] . '" deleted.');
        Response::redirect('/admin/downloads');
    }

    public function bulk(Request $request, array $params = []): void
    {
        $this->validateCsrf();
        $ids    = array_map('intval', $request->input('ids', []));
        $action = $request->input('bulk_action', '');

        if (empty($ids)) {
            Response::redirect('/admin/downloads');
            return;
        }

        $count = 0;
        foreach ($ids as $id) {
            if ($action === 'delete') {
                $download = Download::findForAdmin($id);
                if ($download) {
                    $this->deleteDownloadFile($download['file_path'] ?? null);
                    Download::deleteById($id);
                    $count++;
                }
            } elseif ($action === 'publish') {
                Download::updateById($id, ['status' => 'published']);
                $count++;
            } elseif ($action === 'draft') {
                Download::updateById($id, ['status' => 'draft']);
                $count++;
            }
        }

        set_flash('success', __('messages.bulk_action_done', ['count' => $count]));
        Response::redirect('/admin/downloads');
    }

    // -------------------------------------------------------------------------
    // DIRECT FILE UPLOAD (for the downloadable file)
    // -------------------------------------------------------------------------

    /**
     * Upload a downloadable file from the download form.
     * Stores the file in uploads/downloads/ with a secure unique filename.
     * Returns JSON so the form JS can update the hidden fields.
     *
     * POST /admin/downloads/upload-file
     */
    public function uploadFile(Request $request, array $params = []): void
    {
        $this->validateCsrf();

        $file = $request->file('download_file') ?? [];

        if (empty($file['tmp_name'])) {
            Response::json(['ok' => false, 'error' => 'No file received.'], 400);
            return;
        }

        if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
            Response::json(['ok' => false, 'error' => 'Upload error (code ' . ($file['error'] ?? '?') . ').'], 422);
            return;
        }

        $fileSize = (int) ($file['size'] ?? 0);
        if ($fileSize > self::MAX_FILE_SIZE) {
            Response::json(['ok' => false, 'error' => 'File too large. Maximum: 50 MB.'], 422);
            return;
        }

        $mime = mime_content_type($file['tmp_name']) ?: '';
        if (!array_key_exists($mime, self::ALLOWED_FILE_MIMES)) {
            Response::json([
                'ok'    => false,
                'error' => 'File type not allowed. Allowed: PDF, ZIP, DOC(X), XLS(X), PPT(X), ODT/ODS, TXT, CSV and more.',
            ], 422);
            return;
        }

        $ext      = self::ALLOWED_FILE_MIMES[$mime];
        $original = $file['name'] ?? 'download';
        $safeName = 'dl_' . substr(md5(uniqid('', true)), 0, 12) . '.' . $ext;

        $uploadsBase = UploadService::uploadsDir();
        $subDir      = $uploadsBase . 'downloads/';

        if (!is_dir($subDir) && !mkdir($subDir, 0755, true)) {
            Response::json(['ok' => false, 'error' => 'Could not create upload directory.'], 500);
            return;
        }

        $dest = $subDir . $safeName;
        if (!move_uploaded_file($file['tmp_name'], $dest)) {
            Response::json(['ok' => false, 'error' => 'Could not save uploaded file.'], 500);
            return;
        }

        Response::json([
            'ok'        => true,
            'file_path' => 'downloads/' . $safeName,
            'file_name' => basename($original),
            'file_size' => (int) @filesize($dest),
            'file_type' => $mime,
        ]);
    }

    // -------------------------------------------------------------------------
    // DIRECT IMAGE UPLOAD (for featured image – same pattern as EventController)
    // -------------------------------------------------------------------------

    /**
     * Quick-upload a featured image directly from the download form.
     * Stores via UploadService, creates a Media record, returns JSON.
     *
     * POST /admin/downloads/upload-image
     */
    public function uploadImage(Request $request, array $params = []): void
    {
        $this->validateCsrf();

        $file = $request->file('image_file') ?? [];

        if (empty($file['tmp_name'])) {
            Response::json(['ok' => false, 'error' => 'No file received.'], 400);
            return;
        }

        $relPath = UploadService::upload($file, 'media', 'media', true, true);

        if ($relPath === null) {
            Response::json([
                'ok'    => false,
                'error' => 'Upload failed. Allowed types: JPG, PNG, GIF, WebP. Max size: 5 MB.',
            ], 422);
            return;
        }

        $rawTitle = pathinfo($file['name'] ?? '', PATHINFO_FILENAME);
        $title    = ucwords(strtolower(preg_replace('/[_\-]+/', ' ', $rawTitle)));

        $uploadsDir = UploadService::uploadsDir();
        $fileSize   = (int) @filesize($uploadsDir . $relPath);
        $mime       = mime_content_type($uploadsDir . $relPath) ?: 'application/octet-stream';

        $mediaId = Media::create([
            'folder_id'     => null,
            'filename'      => basename($relPath),
            'original_name' => $file['name'] ?? basename($relPath),
            'file_path'     => $relPath,
            'file_type'     => $mime,
            'file_size'     => $fileSize,
            'title'         => $title,
            'alt_text'      => null,
            'description'   => null,
            'uploaded_by'   => $this->auth()->userId(),
        ]);

        Response::json([
            'ok'        => true,
            'media_id'  => (int) $mediaId,
            'url'       => '/uploads/' . ltrim($relPath, '/'),
            'thumb_url' => thumb_url($relPath),
        ]);
    }

    // -------------------------------------------------------------------------
    // HELPERS
    // -------------------------------------------------------------------------

    /**
     * Extract and sanitise form data from the request.
     * Note: file_path / file_name / file_size / file_type come from AJAX upload,
     * not from a standard form field – they arrive as hidden inputs.
     */
    private function extractFormData(Request $request): array
    {
        $title          = trim($request->input('title', ''));
        $slug           = trim($request->input('slug', ''));
        $excerpt        = trim($request->input('excerpt', ''));
        $version        = trim($request->input('version', ''));
        $content        = $request->input('content', '');
        $categoryId     = (int) $request->input('category_id', 0) ?: null;
        $mediaId        = (int) $request->input('media_id', 0) ?: null;
        $filePath       = trim($request->input('file_path', '')) ?: null;
        $fileName       = trim($request->input('file_name', '')) ?: null;
        $fileSize       = (int) $request->input('file_size', 0) ?: null;
        $fileType       = trim($request->input('file_type', '')) ?: null;
        $status         = in_array($request->input('status'), ['draft', 'published'], true)
                            ? $request->input('status')
                            : 'draft';
        $sortOrder      = (int) $request->input('sort_order', 0);
        $seoTitle       = trim($request->input('seo_title', ''));
        $seoDescription = trim($request->input('seo_description', ''));

        return [
            'title'           => $title,
            'slug'            => $slug,
            'excerpt'         => $excerpt ?: null,
            'version'         => $version ?: null,
            'content'         => $content ?: null,
            'category_id'     => $categoryId,
            'media_id'        => $mediaId,
            'file_path'       => $filePath,
            'file_name'       => $fileName,
            'file_size'       => $fileSize,
            'file_type'       => $fileType,
            'status'          => $status,
            'sort_order'      => $sortOrder,
            'seo_title'       => $seoTitle ?: null,
            'seo_description' => $seoDescription ?: null,
        ];
    }

    /**
     * Safely delete a download file from uploads/downloads/.
     * Only deletes if the path is within the expected subdirectory.
     */
    private function deleteDownloadFile(?string $filePath): void
    {
        if (empty($filePath)) {
            return;
        }

        // Only delete files that live in downloads/
        if (!str_starts_with($filePath, 'downloads/')) {
            return;
        }

        $uploadsBase = UploadService::uploadsDir();
        $full        = realpath($uploadsBase . $filePath);
        $base        = realpath($uploadsBase . 'downloads');

        if ($full === false || $base === false) {
            return;
        }

        // Path-traversal guard
        if (!str_starts_with($full, $base . DIRECTORY_SEPARATOR)) {
            return;
        }

        if (is_file($full)) {
            @unlink($full);
        }
    }
}
