<?php

namespace Modules\SystemTool\Services;

use App\Exceptions\HttpResponseException;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Modules\SystemTool\Enum\FileType;
use Modules\SystemTool\Models\SysFileModel;
use Modules\SystemTool\Settings\StorageSettings;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
 * 文件服务类
 */
class SysFileService
{

    /**
     * 获取回收站列表
     * @param array $params
     * @return array
     */
    public function getTrashedList(array $params = []): array
    {
        $pageSize = $params['pageSize'] ?? 10;
        return SysFileModel::onlyTrashed()
            ->paginate($pageSize)
            ->toArray();
    }

    /**
     * 获取存储磁盘实例
     */
    protected function disk($disk = null): FilesystemAdapter
    {
        if(!$disk) {
            $disk = config('filesystems.default');
        }
        if ($disk === 's3' && !self::isS3Configured()) {
            throw new HttpResponseException(['success' => false, 'msg' => __('system.storage.s3_not_configured')]);
        }
        /** @var FilesystemAdapter */
        return Storage::disk($disk);
    }

    /**
     * 生成存储路径
     */
    protected function generateStoragePath(string $extension): string
    {
        return date('Ymd') . '/' . uniqid() . '.' . $extension;
    }

    /**
     * 上传文件
     * @param UploadedFile $file 文件
     * @param int $groupId 分组 ID
     * @param int $channel 上传来源 0：匿名用户，10：后台用户，20：APP用户
     * @param int|null $user_id 上传用户 ID
     * @return array
     */
    public function upload(UploadedFile $file, int $groupId = 0, int $channel = 0, ?int $user_id = null): array
    {
        // 文件扩展名
        $fileExt = strtolower($file->getClientOriginalExtension() ?: $file->extension());

        if (empty($fileExt)) {
            throw new HttpResponseException(['success' => false, 'msg' => '无法识别的文件扩展名']);
        }
        // 推断文件类型
        $fileType = FileType::guessFromExtension($fileExt);
        // 获取储存路径
        $storagePath = $this->generateStoragePath($fileExt);
        // 获取磁盘
        $disk = StorageSettings::get('filesystems.default', 'local');
        // 存储文件并设置可见性
        $stored = $this->disk($disk)->put($storagePath, $file->getContent(), 'public');
        if (!$stored) {
            throw new HttpResponseException(['success' => false, 'msg' => __('system.file.upload_failed')]);
        }
        // 保存到数据库
        $model = new SysFileModel();
        $model->disk = $disk;
        $model->group_id = $groupId;
        $model->channel = $channel;
        $model->file_type = $fileType->value;
        $model->file_path = $storagePath;
        $model->file_name = $file->getClientOriginalName();
        $model->file_size = $file->getSize();
        $model->file_ext = $fileExt;
        $model->uploader_id = $user_id;
        $model->save();
        return $model->toArray();
    }

    /**
     * 软删除文件（移入回收站）
     */
    public function delete(int $id): bool
    {
        $file = SysFileModel::find($id);
        if (!$file) {
            throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
        }

        return (bool) $file->delete();
    }

    /**
     * 批量软删除文件
     */
    public function batchDelete(array $fileIds): int
    {
        return SysFileModel::whereIn('id', $fileIds)->delete();
    }

    /**
     * 恢复已删除的文件
     */
    public function restore(int $fileId): bool
    {
        $file = SysFileModel::withTrashed()->find($fileId);
        if (!$file) {
            throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
        }

        return $file->restore();
    }

    /**
     * 批量恢复文件
     */
    public function batchRestore(array $fileIds): int
    {
        return SysFileModel::withTrashed()->whereIn('id', $fileIds)->restore();
    }

    /**
     * 彻底删除文件（含物理删除）
     */
    public function forceDelete(int $fileId): bool
    {
        $file = SysFileModel::withTrashed()->find($fileId);
        if (!$file) {
            throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
        }

        // 删除物理文件
        $this->disk($file->disk)->delete($file->file_path);

        return (bool) $file->forceDelete();
    }

    /**
     * 批量彻底删除文件
     */
    public function batchForceDelete(array $fileIds): int
    {
        $files = SysFileModel::withTrashed()->whereIn('id', $fileIds)->get();
        $count = 0;

        foreach ($files as $file) {
            $this->disk($file->disk)->delete($file->file_path);
            $file->forceDelete();
            $count++;
        }

        return $count;
    }

    /**
     * 下载文件
     */
    public function download(int $fileId, ?string $filename = null): StreamedResponse
    {
        $file = SysFileModel::find($fileId);
        if (!$file) {
            throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
        }

        $downloadName = $filename ?? $file->file_name;
        return $this->disk($file->disk)->download($file->file_path, $downloadName);
    }

    /**
     * 获取文件流式响应（用于在线预览等场景）
     */
    public function stream(int $fileId): StreamedResponse
    {
        $file = SysFileModel::find($fileId);
        if (!$file) {
            throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
        }

        $mimeType = $this->getMimeType($file->file_ext);

        return response()->stream(
            function () use ($file) {
                $stream = $this->disk($file->disk)->readStream($file->file_path);
                fpassthru($stream);
                if (is_resource($stream)) {
                    fclose($stream);
                }
            },
            200,
            [
                'Content-Type' => $mimeType,
                'Content-Disposition' => 'inline; filename="' . $file->file_name . '"',
            ]
        );
    }

    /**
     * 获取文件内容
     */
    public function getContent(int $fileId): string
    {
        $file = SysFileModel::find($fileId);
        if (!$file) {
            throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
        }

        return $this->disk($file->disk)->get($file->file_path);
    }

    /**
     * 获取文件访问URL
     */
    public function getUrl(int $fileId): ?string
    {
        $file = SysFileModel::find($fileId);
        if (!$file) {
            return null;
        }

        return $this->disk($file->disk)->url($file->file_path);
    }

    /**
     * 根据路径获取访问URL
     */
    public function getUrlByPath(string $path, ?string $disk = null): string
    {
        return $this->disk($disk)->url($path);
    }

    /**
     * 获取文件元数据信息
     */
    public function getMetadata(int $fileId): ?array
    {
        $file = SysFileModel::find($fileId);
        if (!$file) {
            return null;
        }

        $disk = $this->disk($file->disk);

        return [
            'id' => $file->id,
            'name' => $file->file_name,
            'path' => $file->file_path,
            'disk' => $file->disk,
            'size' => $file->file_size,
            'extension' => $file->file_ext,
            'mime_type' => $this->getMimeType($file->file_ext),
            'last_modified' => $disk->exists($file->file_path)
                ? date('Y-m-d H:i:s', $disk->lastModified($file->file_path))
                : null,
            'url' => $this->getUrl($file->id),
            'group_id' => $file->group_id,
            'uploader_id' => $file->uploader_id,
            'created_at' => $file->created_at?->format('Y-m-d H:i:s'),
            'updated_at' => $file->updated_at?->format('Y-m-d H:i:s'),
        ];
    }

    /**
     * 复制文件
     */
    public function copy(int $fileId, int $targetGroupId = 0): array
    {
        $file = SysFileModel::find($fileId);
        if (!$file) {
            throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
        }

        $newPath = $this->generateStoragePath($file->file_ext);

        $this->disk($file->disk)->copy($file->file_path, $newPath);

        // 创建新的文件记录
        $newFile = SysFileModel::create([
            'disk' => $file->disk,
            'group_id' => $targetGroupId,
            'channel' => $file->channel,
            'file_name' => $file->file_name,
            'file_type' => $file->file_type,
            'file_path' => $newPath,
            'file_size' => $file->file_size,
            'file_ext' => $file->file_ext,
            'uploader_id' => Auth::id(),
        ]);

        return $newFile->toArray();
    }

    /**
     * 批量复制文件
     */
    public function batchCopy(array $fileIds, int $targetGroupId = 0): bool
    {
        $files = SysFileModel::whereIn('id', $fileIds)->get();
        $fileArray = [];

        foreach ($files as $file) {

            $newPath = $this->generateStoragePath($file->file_ext);

            $this->disk($file->disk)->copy($file->file_path, $newPath);

            $fileArray[] =[
                'disk' => $file->disk,
                'group_id' => $targetGroupId,
                'channel' => $file->channel,
                'file_name' => $file->file_name,
                'file_type' => $file->file_type,
                'file_path' => $newPath,
                'file_size' => $file->file_size,
                'file_ext' => $file->file_ext,
                'uploader_id' => Auth::id(),
                'created_at' => now(),
                'updated_at' => now(),
            ];
        }
        return SysFileModel::insert($fileArray);
    }

    /**
     * 移动文件
     */
    public function move(int $fileId, int $groupId): bool
    {
        $file = SysFileModel::find($fileId);
        return $file->update(['group_id' => $groupId]);
    }

    /**
     * 批量移动
     */
    public function batchMove(array $fileIds, int $groupId): int
    {
        return SysFileModel::whereIn('id', $fileIds)->update(['group_id' => $groupId]);
    }

    /**
     * 重命名文件
     */
    public function rename(int $fileId, string $newName): bool
    {
        $file = SysFileModel::find($fileId);
        if (!$file) {
            throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
        }

        return $file->update(['file_name' => $newName]);
    }

    /**
     * 检查文件是否存在
     */
    public function exists(int $fileId): bool
    {
        $file = SysFileModel::find($fileId);
        if (!$file) {
            return false;
        }

        return $this->disk($file->disk)->exists($file->file_path);
    }

    /**
     * 获取文件大小（字节）
     */
    public function getSize(int $fileId): ?int
    {
        $file = SysFileModel::find($fileId);
        if (!$file) {
            return null;
        }

        return $this->disk($file->disk)->size($file->file_path);
    }

    /**
     * 获取MIME类型
     */
    protected function getMimeType(string $extension): string
    {
        $mimeTypes = [
            // 图片
            'jpg' => 'image/jpeg',
            'jpeg' => 'image/jpeg',
            'png' => 'image/png',
            'gif' => 'image/gif',
            'webp' => 'image/webp',
            'avif' => 'image/avif',
            'bmp' => 'image/bmp',
            'svg' => 'image/svg+xml',
            // 音频
            'mp3' => 'audio/mpeg',
            'wav' => 'audio/wav',
            'ogg' => 'audio/ogg',
            'flac' => 'audio/flac',
            'aac' => 'audio/aac',
            // 视频
            'mp4' => 'video/mp4',
            'webm' => 'video/webm',
            'mkv' => 'video/x-matroska',
            'mov' => 'video/quicktime',
            'avi' => 'video/x-msvideo',
            // 文档
            'pdf' => 'application/pdf',
            'doc' => 'application/msword',
            'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'xls' => 'application/vnd.ms-excel',
            'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            // 压缩包
            'zip' => 'application/zip',
            'rar' => 'application/vnd.rar',
            '7z' => 'application/x-7z-compressed',
            // 其他
            'json' => 'application/json',
            'xml' => 'application/xml',
            'txt' => 'text/plain',
        ];

        return $mimeTypes[strtolower($extension)] ?? 'application/octet-stream';
    }

    /**
     * 清空回收站文件
     */
    public function cleanTrashed(): int
    {
        $expiredFiles = SysFileModel::onlyTrashed()->get();
        $count = 0;
        foreach ($expiredFiles as $file) {
            $this->disk($file->disk)->delete($file->file_path);
            $file->forceDelete();
            $count++;
        }
        return $count;
    }


    /**
     * 检查 S3 配置是否有效
     * @return bool
     */
    public static function isS3Configured(): bool
    {
        $storageConfig = config('filesystems.disks.s3');

        if (empty($storageConfig)) {
            return false;
        }

        return !empty($storageConfig['key'])
            && !empty($storageConfig['secret'])
            && !empty($storageConfig['bucket'])
            && !empty($storageConfig['region']);
    }
}
