<?php

declare(strict_types=1);

/**
 * 會員頭像等延伸資料（存在 fate 資料庫，不改動主會員表）。
 */

function fate_member_profile_table_ddl(): string
{
    return <<<'SQL'
CREATE TABLE IF NOT EXISTS `fate_site_member_profiles` (
  `member_id` INT UNSIGNED NOT NULL,
  `avatar_rel` VARCHAR(512) NOT NULL DEFAULT '',
  `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
SQL;
}

function fate_member_profile_ensure_table(PDO $pdo): void
{
    static $done = false;
    if ($done) {
        return;
    }
    try {
        $pdo->exec(fate_member_profile_table_ddl());
        $done = true;
    } catch (Throwable) {
        /* 無建表權限時略過；後續查詢會失敗並由呼叫端處理 */
    }
}

function fate_member_profile_avatar_dir(): string
{
    return dirname(__DIR__) . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'avatars';
}

/** Web 相對於 fate 根目錄之 URL（index.php 同層）。 */
function fate_member_profile_avatar_url(?string $avatarRel): ?string
{
    $r = trim((string) $avatarRel);
    if ($r === '') {
        return null;
    }
    $r = str_replace('\\', '/', $r);
    if (str_contains($r, '..')) {
        return null;
    }

    return './' . ltrim($r, '/');
}

/** 將頭像路徑寫入 Session（若 DB 無列則為空字串）。 */
function fate_member_profile_hydrate_session(PDO $pdo, int $memberId): void
{
    if ($memberId <= 0) {
        return;
    }
    fate_public_session_start();
    fate_member_profile_ensure_table($pdo);
    try {
        $st = $pdo->prepare('SELECT `avatar_rel` FROM `fate_site_member_profiles` WHERE `member_id` = ? LIMIT 1');
        $st->execute([$memberId]);
        $rel = $st->fetchColumn();
        $_SESSION['fate_site_avatar_rel'] = is_string($rel) ? trim($rel) : '';
    } catch (Throwable) {
        $_SESSION['fate_site_avatar_rel'] = '';
    }
}

/**
 * 解析 data URL（image/jpeg;base64,...）為二進位與 MIME。
 *
 * @return array{ok:bool,bytes:string,mime:string,error:?string}
 */
function fate_member_profile_parse_avatar_data_url(string $dataUrl): array
{
    $raw = trim($dataUrl);
    if ($raw === '' || !str_starts_with($raw, 'data:image/')) {
        return ['ok' => false, 'bytes' => '', 'mime' => '', 'error' => '剪裁資料格式不正確。'];
    }
    if (!preg_match('#^data:(image/(?:jpeg|png|webp));base64,(.+)$#i', $raw, $m)) {
        return ['ok' => false, 'bytes' => '', 'mime' => '', 'error' => '僅支援 JPEG／PNG／WebP 剪裁輸出。'];
    }
    $mime = strtolower($m[1]);
    $b64 = $m[2];
    $bytes = base64_decode($b64, true);
    if ($bytes === false || $bytes === '') {
        return ['ok' => false, 'bytes' => '', 'mime' => '', 'error' => '無法解碼剪裁圖片。'];
    }
    if (strlen($bytes) > 2 * 1024 * 1024) {
        return ['ok' => false, 'bytes' => '', 'mime' => '', 'error' => '剪裁圖片過大（請縮小或降低品質）。'];
    }

    return ['ok' => true, 'bytes' => $bytes, 'mime' => $mime, 'error' => null];
}

/**
 * 儲存已剪裁的圖片位元組（統一轉為 JPEG、最長邊 512px）。
 *
 * @return array{ok:bool, error:?string, rel:?string}
 */
function fate_member_profile_save_avatar_bytes(PDO $pdo, int $memberId, string $bytes): array
{
    if ($memberId <= 0) {
        return ['ok' => false, 'error' => '無效的會員編號。', 'rel' => null];
    }
    if ($bytes === '') {
        return ['ok' => false, 'error' => '沒有圖片資料。', 'rel' => null];
    }
    if (strlen($bytes) > 3 * 1024 * 1024) {
        return ['ok' => false, 'error' => '檔案過大（請小於 3MB）。', 'rel' => null];
    }

    if (!function_exists('imagecreatetruecolor')) {
        return ['ok' => false, 'error' => '伺服器未啟用 GD 圖片函式，無法處理頭像。', 'rel' => null];
    }

    $src = @imagecreatefromstring($bytes);
    if ($src === false) {
        return ['ok' => false, 'error' => '無法讀取圖片（格式可能不支援）。', 'rel' => null];
    }

    $sw = imagesx($src);
    $sh = imagesy($src);
    if ($sw < 1 || $sh < 1) {
        imagedestroy($src);

        return ['ok' => false, 'error' => '圖片尺寸無效。', 'rel' => null];
    }

    $outSize = 512;
    $dst = imagecreatetruecolor($outSize, $outSize);
    if ($dst === false) {
        imagedestroy($src);

        return ['ok' => false, 'error' => '無法建立輸出圖片。', 'rel' => null];
    }

    $ratio = max($outSize / $sw, $outSize / $sh);
    $dw = (int) round($sw * $ratio);
    $dh = (int) round($sh * $ratio);
    $dx = (int) round(($outSize - $dw) / 2);
    $dy = (int) round(($outSize - $dh) / 2);

    imagecopyresampled($dst, $src, $dx, $dy, 0, 0, $dw, $dh, $sw, $sh);
    imagedestroy($src);

    $dir = fate_member_profile_avatar_dir();
    if (!is_dir($dir)) {
        if (!@mkdir($dir, 0775, true) && !is_dir($dir)) {
            imagedestroy($dst);

            return ['ok' => false, 'error' => '無法建立上傳目錄（請確認 fate/uploads/avatars 可寫入）。', 'rel' => null];
        }
    }

    $basename = 'u' . $memberId . '_' . bin2hex(random_bytes(6)) . '.jpg';
    $destFs = $dir . DIRECTORY_SEPARATOR . $basename;
    $rel = 'uploads/avatars/' . $basename;

    if (!imagejpeg($dst, $destFs, 88)) {
        imagedestroy($dst);

        return ['ok' => false, 'error' => '無法儲存檔案。', 'rel' => null];
    }
    imagedestroy($dst);

    return fate_member_profile_persist_avatar_rel($pdo, $memberId, $rel, $destFs);
}

/**
 * @return array{ok:bool, error:?string, rel:?string}
 */
function fate_member_profile_persist_avatar_rel(PDO $pdo, int $memberId, string $rel, string $destFs): array
{
    fate_member_profile_ensure_table($pdo);
    try {
        $prev = null;
        $st = $pdo->prepare('SELECT `avatar_rel` FROM `fate_site_member_profiles` WHERE `member_id` = ? LIMIT 1');
        $st->execute([$memberId]);
        $prevRaw = $st->fetchColumn();
        if (is_string($prevRaw) && trim($prevRaw) !== '') {
            $prev = trim($prevRaw);
        }

        $up = $pdo->prepare(
            'INSERT INTO `fate_site_member_profiles` (`member_id`, `avatar_rel`) VALUES (:mid, :rel)
             ON DUPLICATE KEY UPDATE `avatar_rel` = :rel_u'
        );
        $up->execute(['mid' => $memberId, 'rel' => $rel, 'rel_u' => $rel]);

        if ($prev !== null && $prev !== $rel && preg_match('#^uploads/avatars/u[0-9]+_[a-f0-9]+\.(jpg|jpeg|png|webp|gif)$#', strtolower($prev)) === 1) {
            $dir = fate_member_profile_avatar_dir();
            $oldFs = dirname(__DIR__) . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $prev);
            $realOld = is_file($oldFs) ? realpath($oldFs) : false;
            $realDir = realpath($dir);
            if ($realOld !== false && $realDir !== false && str_starts_with($realOld, $realDir)) {
                @unlink($realOld);
            }
        }
    } catch (Throwable $e) {
        @unlink($destFs);

        return ['ok' => false, 'error' => '無法寫入資料庫：' . $e->getMessage(), 'rel' => null];
    }

    fate_public_session_start();
    $_SESSION['fate_site_avatar_rel'] = $rel;

    return ['ok' => true, 'error' => null, 'rel' => $rel];
}

/**
 * @return array{ok:bool, error:?string, rel:?string}
 */
function fate_member_profile_save_avatar(PDO $pdo, int $memberId, array $file): array
{
    if ($memberId <= 0) {
        return ['ok' => false, 'error' => '無效的會員編號。', 'rel' => null];
    }
    if (!isset($file['error']) || (int) $file['error'] !== UPLOAD_ERR_OK) {
        return ['ok' => false, 'error' => '上傳未完成（請選擇圖片後再試）。', 'rel' => null];
    }
    if (!isset($file['tmp_name']) || !is_uploaded_file((string) $file['tmp_name'])) {
        return ['ok' => false, 'error' => '上傳檔無效。', 'rel' => null];
    }

    $tmp = (string) $file['tmp_name'];
    $fi = new finfo(FILEINFO_MIME_TYPE);
    $mime = $fi->file($tmp);
    $allowed = [
        'image/jpeg' => 'jpg',
        'image/png' => 'png',
        'image/webp' => 'webp',
        'image/gif' => 'gif',
    ];
    if (!is_string($mime) || !isset($allowed[$mime])) {
        return ['ok' => false, 'error' => '僅支援 JPEG、PNG、WebP、GIF 圖片。', 'rel' => null];
    }
    $size = filesize($tmp);
    if ($size === false || $size > 3 * 1024 * 1024) {
        return ['ok' => false, 'error' => '檔案過大（請小於 3MB）。', 'rel' => null];
    }

    $dir = fate_member_profile_avatar_dir();
    if (!is_dir($dir)) {
        if (!@mkdir($dir, 0775, true) && !is_dir($dir)) {
            return ['ok' => false, 'error' => '無法建立上傳目錄（請確認 fate/uploads/avatars 可寫入）。', 'rel' => null];
        }
    }

    $ext = $allowed[$mime];
    $basename = 'u' . $memberId . '_' . bin2hex(random_bytes(6)) . '.' . $ext;
    $destFs = $dir . DIRECTORY_SEPARATOR . $basename;
    $rel = 'uploads/avatars/' . $basename;

    if (!move_uploaded_file($tmp, $destFs)) {
        return ['ok' => false, 'error' => '無法儲存檔案。', 'rel' => null];
    }

    $bytes = file_get_contents($destFs);
    if ($bytes === false) {
        @unlink($destFs);

        return ['ok' => false, 'error' => '無法讀取上傳檔案。', 'rel' => null];
    }
    @unlink($destFs);

    return fate_member_profile_save_avatar_bytes($pdo, $memberId, $bytes);
}
