<?php

namespace App\Libraries;

use Aws\S3\S3Client;
use Aws\Exception\AwsException;
use Aws\Credentials\CredentialProvider;
use Aws\Credentials\Credentials;
use CodeIgniter\HTTP\Files\UploadedFile;

class MyUpload
{
    // ===== CI3 Upload 유사 설정값 =====
    public string $upload_path = '';
    public string $allowed_types = '*';      // 'jpg|png|gif|pdf' 형태
    public int $max_size = 0;             // KB 단위(0이면 무제한)
    public int $max_filename = 0;
    public bool $overwrite = false;
    public bool $remove_spaces = true;
    public bool $xss_clean = false;        // CI3의 file XSS 검사와 완전 동일하진 않음(아래 설명)
    public string $_file_name_override = '';

    // ===== 업로드 결과값(CI3 유사) =====
    public string $file_temp = '';
    public int $file_size = 0;
    public string $file_type = '';
    public string $file_name = '';
    public string $file_ext = '';
    public string $client_name = '';
    public string $orig_name = '';

    public int $image_width = 0;
    public int $image_height = 0;
    public string $image_type = '';
    public int $image_size_str = 0;

    protected array $errors = [];
    protected array $s3_data = [];

    public function __construct(array $config = [])
    {
        if ($config)
            $this->initialize($config);
    }

    public function initialize(array $config): self
    {
        foreach ($config as $k => $v) {
            if (property_exists($this, $k)) {
                $this->{$k} = $v;
            }
        }
        return $this;
    }

    /**
     * 파일 업로드 요청 (Object Storage 전용)
     * 추가일 2025.12.24
     * 작성자 - yangsh
     */
    public function do_upload2(UploadedFile $file, $filePath = null): array|false
    {
        if (!$file->isValid()) {
            $this->set_error('upload_invalid_file');
            return false;
        }

        // 업로드 전인데 hasMoved()가 true면 비정상 상태
        if ($file->hasMoved()) {
            $this->set_error('upload_file_already_moved');
            return false;
        }

        $newName = $file->getRandomName();

        // ✅ PHP 임시 업로드 파일 경로
        $tmpFile = $file->getTempName();
        if (!is_file($tmpFile)) {
            $this->set_error('upload_temp_file_missing');
            log_message('error', '[MyUpload] Temp file missing: ' . $tmpFile);
            return false;
        }

        // ✅ 클라우드에 올라갈 "Key"
        $objectKey = $filePath . $newName;

        // 클라우드 업로드 (필수)
        $up = $this->upload_object_storage($objectKey, $tmpFile, 'file');
        
        if ($up === false) {
            $this->set_error('cloud_upload_failed');
            log_message('error', '[MyUpload] Cloud upload failed: ' . $objectKey);
            @unlink($tmpFile);
            return false;
        }

        // tmp 파일 삭제
        @unlink($tmpFile);

        $this->s3_data = [
            'object_key' => $objectKey,
            'object_storage_url' => $up['object_storage_url'] ?? null,
            'origin_name' => $file->getClientName(),
            'file_name' => basename($objectKey),
            'base_name' => pathinfo($objectKey, PATHINFO_FILENAME),
            'ext' => pathinfo($objectKey, PATHINFO_EXTENSION),
        ];

        log_message('info', '[MyUpload] Cloud upload success: ' . $objectKey);

        return $this->s3_data;
    }

    /**
     * 
     */
    public function deleteFile($key)
    {
        $s3Client = $this->makeS3Client();

        try {

            $s3Client->deleteObject([
                'Bucket' => NCLOUD_S3_BUCKET,
                'Key' => ltrim($key, '/'),
            ]);

            return true;
        } catch (\Throwable $e) {
            return false;
        }
    }

    /**
     * S3(NCLOUD) 파일 업로드
     * 추가일 2025.12.24
     * 작성자 - yangsh
     */
    public function do_upload(string $field = 'userfile'): bool
    {

        $request = service('request');
        $file = $request->getFile($field);

        var_dump($file);

        if (!$file) {
            $this->set_error('upload_no_file_selected');
            return false;
        }

        if (!$file->isValid()) {
            // CI3의 $_FILES 에러코드 대응(가까운 메시지로 매핑)
            $err = $file->getError();
            switch ($err) {
                case UPLOAD_ERR_INI_SIZE:
                    $this->set_error('upload_file_exceeds_limit');
                    break;
                case UPLOAD_ERR_FORM_SIZE:
                    $this->set_error('upload_file_exceeds_form_limit');
                    break;
                case UPLOAD_ERR_PARTIAL:
                    $this->set_error('upload_file_partial');
                    break;
                case UPLOAD_ERR_NO_FILE:
                    $this->set_error('upload_no_file_selected');
                    break;
                case UPLOAD_ERR_NO_TMP_DIR:
                    $this->set_error('upload_no_temp_directory');
                    break;
                case UPLOAD_ERR_CANT_WRITE:
                    $this->set_error('upload_unable_to_write_file');
                    break;
                case UPLOAD_ERR_EXTENSION:
                    $this->set_error('upload_stopped_by_extension');
                    break;
                default:
                    $this->set_error('upload_no_file_selected');
                    break;
            }
            return false;
        }

        // upload_path가 비어있으면 CI3도 실패함
        if (!$this->upload_path) {
            $this->set_error('upload_no_file_selected');
            return false;
        }

        // (너가 올린 코드처럼) 디렉토리 생성
        $this->make_dirs($this->upload_path);

        // temp file / client file name
        $this->file_temp = $file->getTempName();
        $this->client_name = $file->getClientName();

        // file size (bytes)
        $this->file_size = (int) $file->getSize(); // bytes

        // mime / type
        $this->file_type = strtolower((string) $file->getClientMimeType());
        $this->file_name = $this->_prep_filename($this->client_name);
        $this->file_ext = $this->get_extension($this->file_name);

        // CI3처럼 override 처리
        if ($this->_file_name_override !== '') {
            $this->file_name = $this->_prep_filename($this->_file_name_override);

            if (strpos($this->_file_name_override, '.') === false) {
                $this->file_name .= $this->file_ext;
            } else {
                $this->file_ext = $this->get_extension($this->_file_name_override);
            }
        }

        // allowed type 체크
        if (!$this->is_allowed_filetype($file->getClientExtension(), $this->file_type)) {
            $this->set_error('upload_invalid_filetype');
            return false;
        }

        // max size 체크 (CI3는 KB 기반)
        if (!$this->is_allowed_filesize($this->file_size)) {
            $this->set_error('upload_invalid_filesize');
            return false;
        }

        // 파일명 정리/길이 제한/공백 제거
        $this->file_name = $this->clean_file_name($this->file_name);

        if ($this->max_filename > 0) {
            $this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);
        }

        if ($this->remove_spaces) {
            $this->file_name = preg_replace("/\s+/", "_", $this->file_name);
        }

        $this->orig_name = $this->file_name;

        // overwrite=false면 충돌 시 파일명 변경(CI3 유사)
        if (!$this->overwrite) {
            $this->file_name = $this->set_filename($this->upload_path, $this->file_name);
            if ($this->file_name === false)
                return false;
        }

        // (주의) CI3의 do_xss_clean 완전 동일 구현은 어려움
        // 여기서는 위험한 확장자/패턴 정도만 1차 차단 수준으로 처리(원하면 더 강화 가능)
        if ($this->xss_clean) {
            if (!$this->basic_xss_guard($this->file_name, $this->file_type)) {
                $this->set_error('upload_unable_to_write_file');
                return false;
            }
        }

        // ===== 너의 기존 로직 핵심: 로컬 move 대신 S3 업로드 =====
        $destKeyLikeCi3 = $this->upload_path . $this->file_name;

        if (!$this->upload_object_storage($destKeyLikeCi3, $this->file_temp, 'file')) {
            $this->set_error('upload_destination_cloud_error');
            return false;
        }

        // 이미지면 width/height
        $this->set_image_properties($destKeyLikeCi3, $this->file_temp);

        return true;
    }

    // CI3 스타일: data()
    public function data(): array
    {
        $data = [
            'file_name' => $this->file_name,
            'file_type' => $this->file_type,
            'file_path' => $this->upload_path,
            'full_path' => rtrim($this->upload_path, '/') . '/' . $this->file_name,
            'raw_name' => $this->raw_name($this->file_name),
            'orig_name' => $this->orig_name,
            'client_name' => $this->client_name,
            'file_ext' => $this->file_ext,
            'file_size' => $this->file_size,
            'is_image' => ($this->image_width > 0 && $this->image_height > 0),
            'image_width' => $this->image_width,
            'image_height' => $this->image_height,
            'image_type' => $this->image_type,
            'image_size_str' => $this->image_size_str,
        ];

        if (!empty($this->s3_data)) {
            $data = array_merge($data, $this->s3_data);
        }
        return $data;
    }

    public function display_errors(string $open = '', string $close = ''): string
    {
        if (!$this->errors)
            return '';
        return $open . implode($close . $open, $this->errors) . $close;
    }

    public function get_errors(): array
    {
        return $this->errors;
    }

    protected function set_error(string $msg): void
    {
        $this->errors[] = $msg;
    }


    /**
     * S3(NCLOUD) 파일 업로드
     * 수정일 2025.12.24
     * 작성자 - yangsh
     */
    public function upload_object_storage(string $key, string $temp_file, string $type = 'file'): array|false
    {
        // CI3 코드의 경로 치환 로직 유지 (FCPATH는 CI4에도 존재)
        $object_storage_upload_path = str_replace(FCPATH, '/', $key);
        $object_storage_upload_path = str_replace('/image/confirms_upload/', '/upload/', $object_storage_upload_path);
        $object_storage_upload_path = str_replace('//', '/', $object_storage_upload_path);
        $object_storage_upload_path = str_replace('/home/www/upload/confirms_upload/', '/upload/', $object_storage_upload_path);

        $s3Client = $this->makeS3Client();

        try {
            // $body = file_get_contents($temp_file);

            // $response = $s3Client->putObject([
            //     'Bucket' => NCLOUD_S3_BUCKET,
            //     'Key' => ltrim($object_storage_upload_path, '/'),
            //     'Body' => $body,
            //     'ACL' => 'public-read',
            // ]);

            $response = $s3Client->putObject([
                'Bucket' => NCLOUD_S3_BUCKET,
                'Key' => ltrim($object_storage_upload_path, '/'),
                'SourceFile' => $temp_file,          // ✅ 동영상도 OK
                'ACL' => 'public-read',

                // (선택) 타입별 ContentType 지정 (브라우저 재생/다운로드에 중요)
                // 'ContentType' => $this->guessMime($temp_file, $type),
            ]);

            // $this->s3_data = [
            //     'object_storage_upload_path' => $object_storage_upload_path,
            //     'object_storage_url' => $response['ObjectURL'] ?? null,
            // ];

            return [
                'object_storage_url' => $response['ObjectURL'] ?? null,
            ];

        } catch (\Throwable $e) {
            // 운영에서는 echo 지양. 로그로 남기는 걸 추천
            // log_message('error', $e->getMessage());
            log_message('error', '[S3 UPLOAD FAIL] ' . $e->getMessage());
            log_message('error', '[S3 UPLOAD TRACE] ' . $e->getTraceAsString());
            return false;
        }

    }

    /**
     * S3(NCLOUD) 파일 업로드 - 썸네일
     * 추가일 2025.12.24
     * 작성자 - yangsh
     */
    public function upload_object_storage_imagick2($key, $blobData): bool
    {
        $object_storage_upload_path = str_replace(FCPATH, '/', $key);
        $object_storage_upload_path = str_replace('/image/confirms_upload/', '/upload/', $object_storage_upload_path);
        $object_storage_upload_path = str_replace('//', '/', $object_storage_upload_path);
        $object_storage_upload_path = str_replace('/home/www/upload/confirms_upload/', '/upload/', $object_storage_upload_path);

        $s3Client = $this->makeS3Client();

        try {

            $response = $s3Client->putObject([
                'Bucket' => NCLOUD_S3_BUCKET,
                'Key' => ltrim($object_storage_upload_path, '/'),
                'Body' => $blobData,
                'ACL' => 'public-read',
                'ContentType' => 'image/jpeg',
            ]);


        } catch (\Throwable $e) {
            // 운영에서는 echo 지양. 로그로 남기는 걸 추천
            log_message('error', '[S3 UPLOAD FAIL] ' . $e->getMessage());
            log_message('error', '[S3 UPLOAD TRACE] ' . $e->getTraceAsString());
            return false;
        }

        return true;

    }

    public function upload_object_storage_imagick(string $key, $blobData): bool
    {
        $object_storage_upload_path = str_replace(FCPATH, '/', $key);
        $object_storage_upload_path = str_replace('/image/confirms_upload/', '/upload/', $object_storage_upload_path);
        $object_storage_upload_path = str_replace('//', '/', $object_storage_upload_path);
        $object_storage_upload_path = str_replace('/home/www/upload/confirms_upload/', '/upload/', $object_storage_upload_path);

        $s3Client = $this->makeS3Client();

        try {
            $s3Client->putObject([
                'Bucket' => NCLOUD_S3_BUCKET,
                'Key' => ltrim($object_storage_upload_path, '/'),
                'Body' => $blobData,
                'ACL' => 'public-read',
            ]);
        } catch (\Throwable $e) {
            return false;
        }
        return true;
    }

    public function upload_object_storage_rename(string $key, string $new_name): bool
    {
        $s3Client = $this->makeS3Client();

        try {
            $s3Client->copyObject([
                'Bucket' => NCLOUD_S3_BUCKET,
                'CopySource' => NCLOUD_S3_BUCKET . '/' . ltrim($key, '/'),
                'Key' => ltrim($new_name, '/'),
                'ACL' => 'public-read',
            ]);

            $s3Client->deleteObject([
                'Bucket' => NCLOUD_S3_BUCKET,
                'Key' => ltrim($key, '/'),
            ]);

            return true;
        } catch (\Throwable $e) {
            return false;
        }
    }

    protected function makeS3Client(): S3Client
    {
        $region = defined('NCLOUD_S3_REGION') ? NCLOUD_S3_REGION : 'ap-northeast-2';

        // ✅ credentials를 provider로 강제하면 provider-chain(IMDS) 안 탐
        $creds = new Credentials(NCLOUD_S3_KEY, NCLOUD_S3_SECRET);
        $provider = CredentialProvider::fromCredentials($creds);

        return new S3Client([
            'version' => 'latest',
            'region' => $region,
            'endpoint' => NCLOUD_S3_ENDPOINT,
            'credentials' => $provider,
            'use_path_style_endpoint' => true,
        ]);
    }

    // --------------------------------------------------------------------
    // === CI3 Upload 유사 유틸들 ===

    protected function is_allowed_filetype(string $ext, string $mime): bool
    {
        if ($this->allowed_types === '*' || $this->allowed_types === '')
            return true;

        $ext = strtolower(ltrim($ext, '.'));
        $allowed = array_map('strtolower', explode('|', $this->allowed_types));

        return in_array($ext, $allowed, true);
    }

    protected function is_allowed_filesize(int $bytes): bool
    {
        if ($this->max_size <= 0)
            return true;
        $kb = (int) ceil($bytes / 1024);
        return $kb <= $this->max_size;
    }

    protected function clean_file_name(string $filename): string
    {
        // CI3처럼 위험문자 제거
        $bad = ['<!--', '-->', '<', '>', '"', "'", '&', '$', '=', ';', '?', '/', '\\', '%', '#', '{', '}', '[', ']', ':', ',', '`', '|'];
        return str_replace($bad, '', $filename);
    }

    protected function limit_filename_length(string $filename, int $length): string
    {
        if (mb_strlen($filename) <= $length)
            return $filename;
        $ext = $this->get_extension($filename);
        $name = $this->raw_name($filename);
        $name = mb_substr($name, 0, max(1, $length - mb_strlen($ext)));
        return $name . $ext;
    }

    protected function set_filename(string $path, string $filename)
    {
        $path = rtrim($path, '/') . '/';
        $name = $this->raw_name($filename);
        $ext = $this->get_extension($filename);

        $i = 0;
        $candidate = $filename;
        while (is_file($path . $candidate)) {
            $i++;
            $candidate = $name . '_' . $i . $ext;
            if ($i > 9999) {
                $this->set_error('upload_filename_overflow');
                return false;
            }
        }
        return $candidate;
    }

    protected function set_image_properties(string $keyLike, string $tempFile): void
    {
        // 실제 파일은 S3로 올라갔으니 임시파일에서만 추출
        $info = @getimagesize($tempFile);
        if (!$info)
            return;

        $this->image_width = (int) $info[0];
        $this->image_height = (int) $info[1];
        $this->image_type = (string) ($info['mime'] ?? '');
        $this->image_size_str = (int) ($info[3] ?? 0);
    }

    protected function get_extension(string $filename): string
    {
        $x = strrchr($filename, '.');
        return $x === false ? '' : strtolower($x);
    }

    protected function raw_name(string $filename): string
    {
        $ext = $this->get_extension($filename);
        return $ext ? substr($filename, 0, -strlen($ext)) : $filename;
    }

    protected function _prep_filename(string $filename): string
    {
        // 다중 점 처리 등 CI3 유사
        return str_replace(' ', '_', $filename);
    }

    protected function basic_xss_guard(string $filename, string $mime): bool
    {
        // CI3 do_xss_clean 완전판은 아니고 최소 방어
        $lower = strtolower($filename);
        if (preg_match('/\.(php|phtml|phar|php\d|cgi|pl|asp|aspx|jsp)$/i', $lower)) {
            return false;
        }
        // mime이 text/html 같은 경우도 거부하고 싶으면 여기서 추가
        return true;
    }

    // --------------------------------------------------------------------
    // 너가 올린 make_dirs 로직 이식( VIEW_PATH 없는 부분 보완 )
    protected function make_dirs(string $path, bool $last_is_file = false)
    {
        $dir_arr = explode("/", $path);

        if (empty($dir_arr[0])) {
            $path = "";
        } elseif ($dir_arr[0] === "." || $dir_arr[0] === "..") {
            $path = realpath($dir_arr[0]) ?: '';
            $dir_arr[0] = "";
        } else {
            $path = "/";
        }

        foreach ($dir_arr as $dir) {
            $dir = trim($dir);
            if (!$dir)
                continue;

            if ($last_is_file) {
                if ($dir === $dir_arr[count($dir_arr) - 1]) {
                    return false;
                }
            }

            $path = isset($path) ? ($path . "/" . $dir) : $dir;

            if (@is_dir($path))
                continue;

            @mkdir($path, 0707, true);
            @chmod($path, 0707);

            // CI3는 VIEW_PATH/index.html 복사였는데 CI4에서는 없을 수 있으니 직접 생성
            $index = rtrim($path, '/') . '/index.html';
            if (!is_file($index)) {
                @file_put_contents($index, "<html><head><title>403 Forbidden</title></head><body><p>Directory access is forbidden.</p></body></html>");
                @chmod($index, 0606);
            }
        }
        return true;
    }
}
