<?php
namespace lib;

class File {
    private $db;

    public function __construct($db) {
        $this->db = $db;
    }

    // 重命名文件
    public function rename($uid, $fileId, $newName) {
        // 1. Basic validation
        if (preg_match('/[\\/:*?"<>|]/', $newName)) {
            return ['code' => -1, 'msg' => '名称包含非法字符'];
        }
        
        // 2. Get file info
        $file = $this->db->getRow("SELECT * FROM pre_file WHERE id=:id AND uid=:uid", [':id' => $fileId, ':uid' => $uid]);
        if (!$file) {
            return ['code' => -1, 'msg' => '文件不存在'];
        }

        // 3. Check for conflict in the same folder
        $ext = $file['type']; // Keep original extension? Or allow user to change? Usually renaming preserves extension or user provides full name. 
        // Let's assume user provides full name including extension if they want, or we append original extension if newName doesn't have it?
        // Better: Assume newName is the full filename desired.
        
        // But if user only changes "name" part, extension handling is tricky.
        // Let's check if newName has extension. If not, append original.
        $newExt = get_file_ext($newName);
        if (!$newExt && $file['type']) {
            $newName .= '.' . $file['type'];
        }

        // Check duplicate
        $exists = $this->db->getRow("SELECT id FROM pre_file WHERE uid=:uid AND folder_id=:fid AND name=:name AND id!=:id", 
            [':uid' => $uid, ':fid' => $file['folder_id'], ':name' => $newName, ':id' => $fileId]);
        
        if ($exists) {
            return ['code' => -1, 'msg' => '同名文件已存在'];
        }

        // 4. Update
        if ($this->db->update('file', ['name' => $newName], ['id' => $fileId])) {
            $this->log($uid, 'rename_file', "Renamed file {$fileId} to '$newName'");
            return ['code' => 0, 'msg' => '重命名成功'];
        }
        return ['code' => -1, 'msg' => '重命名失败'];
    }

    // 设置/修改密码
    public function setPassword($uid, $fileId, $password, $oldPassword = null) {
        $file = $this->db->getRow("SELECT * FROM pre_file WHERE id=:id AND uid=:uid", [':id' => $fileId, ':uid' => $uid]);
        if (!$file) {
            return ['code' => -1, 'msg' => '文件不存在'];
        }

        // Verify old password if exists and it is hashed (secure mode)
        // If old pwd was plain text, we just compare.
        // If requesting to set new password, we must verify ownership (which we did by uid) 
        // AND if there was a password, verifying it adds security.
        if (!empty($file['pwd'])) {
            if (empty($oldPassword)) {
                 return ['code' => -1, 'msg' => '请输入原密码进行验证'];
            }
            if (!$this->verifyPassword($oldPassword, $file['pwd'])) {
                 return ['code' => -1, 'msg' => '原密码错误'];
            }
        }

        // Validate password strength (Simple check: length >= 4)
        if (!empty($password) && strlen($password) < 4) {
            return ['code' => -1, 'msg' => '密码长度至少4位'];
        }

        // Store password in plaintext as requested for retrieval display
        // Note: For higher security, consider reversible encryption or hashing without display requirement
        $pwdToStore = empty($password) ? null : $password;

        if ($this->db->update('file', ['pwd' => $pwdToStore], ['id' => $fileId])) {
            $this->log($uid, 'set_password', "Set password for file {$fileId}");
            return ['code' => 0, 'msg' => '密码设置成功'];
        }
        return ['code' => -1, 'msg' => '设置失败'];
    }

    // 获取密码 (仅返回明文存储的密码)
    public function getPassword($uid, $fileId) {
        $file = $this->db->getRow("SELECT pwd FROM pre_file WHERE id=:id AND uid=:uid", [':id' => $fileId, ':uid' => $uid]);
        if (!$file) return null;
        
        $pwd = $file['pwd'];
        if (empty($pwd)) return '';
        
        // Check if it is a hash (bcrypt usually starts with $2y$)
        // If it looks like a hash, do not return it
        $info = password_get_info($pwd);
        if ($info['algo'] != 0) {
            return ''; // Cannot decrypt hash
        }
        
        return $pwd;
    }

    // 验证密码 (支持明文和Hash)
    public function verifyPassword($input, $stored) {
        if (empty($stored)) return true;
        // Check if stored is hash
        $info = password_get_info($stored);
        if ($info['algo'] != 0) {
            return password_verify($input, $stored);
        } else {
            return $input === $stored;
        }
    }

    // 批量重命名
    public function batchRename($uid, $fileIds, $namePattern) {
        // namePattern could be "Image_{i}"
        $count = 0;
        $files = $this->db->getAll("SELECT * FROM pre_file WHERE uid=:uid AND id IN (".implode(',', array_map('intval', $fileIds)).")", [':uid'=>$uid]);
        
        foreach ($files as $index => $file) {
            $newName = str_replace('{i}', $index + 1, $namePattern);
            $newName = str_replace('{name}', pathinfo($file['name'], PATHINFO_FILENAME), $newName);
            
            // Handle extension
            $ext = get_file_ext($file['name']);
            if($ext) $newName .= '.'.$ext;

            // Handle duplicate
            $newName = $this->getUniqueName($uid, $file['folder_id'], $newName, $file['id']);
            
            if ($this->db->update('file', ['name' => $newName], ['id' => $file['id']])) {
                $count++;
            }
        }
        return ['code' => 0, 'msg' => "成功重命名 {$count} 个文件"];
    }

    // 批量设置密码
    public function batchSetPassword($uid, $fileIds, $password) {
        $pwdHash = empty($password) ? null : password_hash($password, PASSWORD_DEFAULT);
        // Using exec for batch update is faster
        $ids = implode(',', array_map('intval', $fileIds));
        $sql = "UPDATE pre_file SET pwd=:pwd WHERE uid=:uid AND id IN ($ids)";
        $res = $this->db->exec($sql, [':pwd'=>$pwdHash, ':uid'=>$uid]);
        
        $this->log($uid, 'batch_set_password', "Batch set password for " . count($fileIds) . " files");
        return ['code' => 0, 'msg' => "批量设置成功"];
    }

    // 复制文件
    public function copy($uid, $fileId, $targetFolderId) {
        $file = $this->db->getRow("SELECT * FROM pre_file WHERE id=:id AND uid=:uid", [':id' => $fileId, ':uid' => $uid]);
        if (!$file) return ['code' => -1, 'msg' => '文件不存在'];
        
        // Handle name conflict in target folder
        $newName = $this->getUniqueName($uid, $targetFolderId, $file['name']);
        
        $data = $file;
        unset($data['id']);
        $data['folder_id'] = $targetFolderId;
        $data['name'] = $newName;
        $data['addtime'] = date('Y-m-d H:i:s');
        
        // We reuse the same hash since it is the same content. 
        // Note: if user modifies one copy, does it affect others? 
        // In this system, 'hash' is MD5 of content. If content changes, it's a new upload usually.
        // If we support editing, we would need new hash. 
        // But here we are just copying the reference (and storage file is shared by hash usually).
        // Since pre_file has 'hash', and storage uses hash, it's fine.
        
        if ($this->db->insert('file', $data)) {
            $newId = $this->db->lastInsertId();
            if ($uid > 0) {
                $this->db->exec("UPDATE pre_user SET storage_used=storage_used+:size WHERE uid=:uid", [':size' => $file['size'], ':uid' => $uid]);
            }
            $this->log($uid, 'copy_file', "Copied file $fileId to folder $targetFolderId (New ID: $newId)");
            return ['code' => 0, 'msg' => '复制成功', 'new_id' => $newId];
        }
        return ['code' => -1, 'msg' => '复制失败'];
    }

    // 批量复制
    public function batchCopy($uid, $fileIds, $targetFolderId) {
        $count = 0;
        foreach ($fileIds as $fid) {
            $res = $this->copy($uid, $fid, $targetFolderId);
            if ($res['code'] == 0) $count++;
        }
        return ['code' => 0, 'msg' => "成功复制 {$count} 个文件"];
    }

    // 获取唯一文件名 (用于冲突处理)
    public function getUniqueName($uid, $folderId, $name, $excludeId = 0) {
        $baseName = pathinfo($name, PATHINFO_FILENAME);
        $ext = pathinfo($name, PATHINFO_EXTENSION);
        $extStr = $ext ? '.'.$ext : '';
        
        $newName = $name;
        $i = 1;
        
        while (true) {
            $sql = "SELECT id FROM pre_file WHERE uid=:uid AND folder_id=:fid AND name=:name";
            $params = [':uid' => $uid, ':fid' => $folderId, ':name' => $newName];
            
            if ($excludeId) {
                $sql .= " AND id!=:id";
                $params[':id'] = $excludeId;
            }
            
            if (!$this->db->getRow($sql, $params)) {
                break;
            }
            
            $newName = $baseName . "($i)" . $extStr;
            $i++;
            if ($i > 100) break; // prevent infinite loop
        }
        return $newName;
    }

    // 获取唯一文件夹名 (用于冲突处理)
    public function getUniqueFolderName($uid, $parentId, $name) {
        $newName = $name;
        $i = 1;
        
        while (true) {
            if (!$this->db->getRow("SELECT folder_id FROM pre_folder WHERE uid=:uid AND parent_id=:pid AND name=:name", 
                [':uid' => $uid, ':pid' => $parentId, ':name' => $newName])) {
                break;
            }
            $newName = $name . "($i)";
            $i++;
            if ($i > 100) break;
        }
        return $newName;
    }

    private function log($uid, $action, $details) {
        $ip = function_exists('real_ip') ? real_ip() : $_SERVER['REMOTE_ADDR'];
        $this->db->insert('log', [
            'uid' => $uid,
            'action' => $action,
            'details' => $details,
            'ip' => $ip,
            'created_at' => date('Y-m-d H:i:s')
        ]);
    }
}
?>
