<?php

namespace App\Services;

use App\Core\App;

/**
 * Two-way media sync: detects FTP-uploaded files and cleans up orphaned DB records.
 *
 * 1. Filesystem → DB: Files in public/uploads/media/ not in iw_media get imported.
 * 2. DB → Filesystem: iw_media records whose file no longer exists get removed.
 */
class MediaSyncService
{
    private string $uploadsDir;
    private string $mediaSubdir = 'media';

    /** File extensions considered media (images + videos). */
    private const ALLOWED_EXTENSIONS = [
        'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'ico',
        'mp4', 'webm', 'ogv', 'mov',
    ];

    /** Extensions that are derived variants – skip during import. */
    private const VARIANT_EXTENSIONS = ['webp', 'avif'];

    public function __construct()
    {
        $this->uploadsDir = UploadService::uploadsDir();
    }

    /**
     * Run the full two-way sync.
     *
     * @return array{imported: int, removed: int, errors: string[]}
     */
    public function sync(): array
    {
        $result = ['imported' => 0, 'removed' => 0, 'errors' => []];

        $app = App::getInstance();
        if (!$app || !$app->hasService('database')) {
            $result['errors'][] = 'Database not available';
            return $result;
        }

        $db = $app->db();

        // ── Phase 1: Import untracked files ─────────────────────────────────
        $imported = $this->importNewFiles($db);
        $result['imported'] = $imported;

        // ── Phase 2: Remove orphaned DB records ─────────────────────────────
        $removed = $this->removeOrphaned($db);
        $result['removed'] = $removed;

        return $result;
    }

    /**
     * Scan filesystem for media files not in the database and import them.
     */
    private function importNewFiles(\App\Core\Database $db): int
    {
        $mediaDir = $this->uploadsDir . $this->mediaSubdir;
        if (!is_dir($mediaDir)) {
            return 0;
        }

        // Get all known file_paths from DB
        $rows = $db->fetchAll("SELECT file_path FROM iw_media");
        $knownPaths = [];
        foreach ($rows as $row) {
            $knownPaths[$row['file_path']] = true;
        }

        // Also collect known WebP/AVIF variants so we don't import them
        $knownVariants = $this->collectVariantPaths($knownPaths);

        // Get folder slug → id mapping
        $folderRows = $db->fetchAll("SELECT id, slug FROM iw_media_folders");
        $folderMap = [];
        foreach ($folderRows as $f) {
            $folderMap[$f['slug']] = (int) $f['id'];
        }

        // Scan filesystem
        $imported = 0;
        $iter = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($mediaDir, \FilesystemIterator::SKIP_DOTS)
        );

        foreach ($iter as $fileInfo) {
            if (!$fileInfo->isFile()) {
                continue;
            }

            $absPath  = $fileInfo->getPathname();
            $relPath  = $this->toRelativePath($absPath);

            if ($relPath === null) {
                continue;
            }

            // Skip thumbs directories
            if (str_contains($relPath, '/thumbs/')) {
                continue;
            }

            $ext = strtolower(pathinfo($relPath, PATHINFO_EXTENSION));

            // Skip non-media files
            if (!in_array($ext, self::ALLOWED_EXTENSIONS, true)) {
                continue;
            }

            // Skip if already in DB
            if (isset($knownPaths[$relPath])) {
                continue;
            }

            // Skip WebP/AVIF files – these are always derived variants, never originals.
            // They accompany a JPG/PNG original and should not be imported separately.
            if (in_array($ext, self::VARIANT_EXTENSIONS, true)) {
                continue;
            }

            // Import this file (limit batch size to avoid timeout)
            $this->importFile($db, $absPath, $relPath, $folderMap);
            $imported++;

            if ($imported >= 50) {
                break;
            }
        }

        return $imported;
    }

    /**
     * Import a single untracked file into the iw_media table.
     */
    private function importFile(\App\Core\Database $db, string $absPath, string $relPath, array $folderMap): void
    {
        $filename = basename($relPath);
        $mime     = @mime_content_type($absPath) ?: 'application/octet-stream';
        $size     = @filesize($absPath) ?: 0;

        // Determine folder_id from path structure: media/{folder-slug}/{file}
        $folderId = null;
        $parts = explode('/', $relPath);
        if (count($parts) >= 3) {
            // e.g., ['media', 'folder-slug', 'file.jpg']
            $folderSlug = $parts[1];
            $folderId = $folderMap[$folderSlug] ?? null;
        }

        // Generate a human-readable title from the filename
        $title = pathinfo($filename, PATHINFO_FILENAME);
        $title = str_replace(['-', '_'], ' ', $title);
        $title = ucfirst($title);

        $db->query(
            "INSERT INTO iw_media (folder_id, filename, original_name, file_path, file_type, file_size, title, uploaded_by, created_at)
             VALUES (:folder_id, :filename, :original_name, :file_path, :file_type, :file_size, :title, :uploaded_by, :created_at)",
            [
                'folder_id'     => $folderId,
                'filename'      => $filename,
                'original_name' => $filename,
                'file_path'     => $relPath,
                'file_type'     => $mime,
                'file_size'     => $size,
                'title'         => $title,
                'uploaded_by'   => null,
                'created_at'    => date('Y-m-d H:i:s'),
            ]
        );

        // Generate thumbnail only (fast) – skip WebP/AVIF to avoid timeout.
        // Use "Bilder optimieren" for full variant generation.
        $isImage = str_starts_with($mime, 'image/')
                   && !in_array($mime, ['image/svg+xml', 'image/x-icon', 'image/vnd.microsoft.icon'], true);

        if ($isImage) {
            @UploadService::generateThumbnail($relPath);
        }
    }

    /**
     * Remove iw_media records whose files no longer exist on disk.
     */
    private function removeOrphaned(\App\Core\Database $db): int
    {
        $rows = $db->fetchAll("SELECT id, file_path FROM iw_media");
        $removed = 0;

        foreach ($rows as $row) {
            $absPath = $this->uploadsDir . ltrim($row['file_path'], '/');

            if (!is_file($absPath)) {
                $db->query("DELETE FROM iw_media WHERE id = :id", ['id' => (int) $row['id']]);
                $removed++;
            }
        }

        return $removed;
    }

    /**
     * Convert an absolute filesystem path to a relative upload path.
     * e.g., /var/www/public/uploads/media/logos/file.jpg → media/logos/file.jpg
     */
    private function toRelativePath(string $absPath): ?string
    {
        $absPath   = str_replace('\\', '/', $absPath);
        $uploadsNorm = str_replace('\\', '/', $this->uploadsDir);

        if (str_starts_with($absPath, $uploadsNorm)) {
            return substr($absPath, strlen($uploadsNorm));
        }

        return null;
    }

    /**
     * Collect derived variant paths (WebP/AVIF) from known originals.
     * These should NOT be imported as separate media items.
     */
    private function collectVariantPaths(array $knownPaths): array
    {
        $variants = [];

        foreach (array_keys($knownPaths) as $path) {
            $webp = UploadService::webpPath($path);
            if ($webp) {
                $variants[$webp] = true;
            }
            $avif = UploadService::avifPath($path);
            if ($avif) {
                $variants[$avif] = true;
            }
        }

        return $variants;
    }
}
