<!DOCTYPE html>
<?php
/* Storage Connections (owner + superadmin). Vars: $connections, $flash, $redirectUri, $this->config */
use PpPhp\Core\Security;
$baseUrl = rtrim((string) $this->config->get('base_url'), '/');
$assetsUrl = $baseUrl . '/assets';
$token = htmlspecialchars(Security::CSRF()->Token());
$driverLabel = ['s3' => 'S3-compatible', 'gdrive' => 'Google Drive', 'onedrive' => 'OneDrive'];
$driverIcon = ['s3' => 'bi-hdd-network', 'gdrive' => 'bi-google', 'onedrive' => 'bi-microsoft'];
$mask = static fn($v) => $v !== '' ? '••••••••' : '';

// ── Guide content (rendered by core/views/owner-ux.php as a searchable modal) ──
$ruri = htmlspecialchars($redirectUri);
$ownerGuide = [
    'id' => 'storageGuide', 'icon' => 'cloud-arrow-up', 'eyebrow' => 'Guide',
    'title' => 'Connecting cloud storage',
    'subtitle' => 'Step-by-step setup for Amazon S3, Google Drive and OneDrive — with examples.',
    'topics' => [
        ['id' => 'overview', 'label' => 'Overview', 'icon' => 'info-circle', 'keywords' => 'basics private public store on files self hosted', 'body' => <<<HTML
            <h3>How storage connections work</h3>
            <div class="og-lead"><i class="bi bi-lightbulb"></i><div>Every <b>File/Image</b> field uploads to a <b>connection</b>. <b>This server (self-hosted)</b> always exists and needs no setup. Add a cloud connection here and it appears in that field's <b>“Store on”</b> dropdown — the field, uploader and views need zero changes.</div></div>
            <h4>Where files land</h4>
            <ul>
                <li><b>Public</b> files → a direct link (fast, cacheable, anyone with the URL can view).</li>
                <li><b>Private</b> files → streamed through the app after a permission check (via <code>api/file.php</code>) or a short-lived signed cloud URL.</li>
            </ul>
            <h4>Three steps for any provider</h4>
            <div class="og-steps">
                <div class="og-step"><b>1</b><strong>Create credentials</strong><span>In the provider's console (keys for S3, an OAuth app for Drive/OneDrive).</span></div>
                <div class="og-step"><b>2</b><strong>Fill this form</strong><span>Pick the type, paste the values, give it a friendly name.</span></div>
                <div class="og-step"><b>3</b><strong>Save &amp; Test</strong><span>Use <b>Test</b> to confirm, then pick it in a field's “Store on”.</span></div>
            </div>
            <div class="og-tip"><i class="bi bi-check-circle"></i><div>Secrets and refresh tokens are stored <b>encrypted</b>. Only owner and superadmin can view this page.</div></div>
HTML],
        ['id' => 's3', 'label' => 'Amazon S3', 'icon' => 'amazon', 'keywords' => 'aws s3 bucket iam access key secret region policy', 'body' => <<<HTML
            <h3>Amazon S3</h3>
            <div class="og-lead"><i class="bi bi-hdd-network"></i><div>Best all-round choice: cheap, fast, scalable. You'll create a <b>bucket</b> and an <b>IAM user</b> with keys limited to that bucket.</div></div>
            <h4>1 · Create a bucket</h4>
            <ul>
                <li>AWS console → <b>S3</b> → <b>Create bucket</b>. Note its <b>name</b> and <b>region</b> (e.g. <code>eu-west-1</code>).</li>
                <li>Keep <b>Block all public access</b> ON if you want private files; turn it off only for a public-assets bucket.</li>
            </ul>
            <h4>2 · Create an IAM user + keys</h4>
            <ul>
                <li>IAM → <b>Users</b> → add a user → <b>Attach policies</b> → create an inline policy scoped to your bucket:</li>
            </ul>
            <pre class="og-code">{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:PutObject","s3:GetObject","s3:DeleteObject"],
    "Resource": "arn:aws:s3:::YOUR-BUCKET/*"
  }]
}</pre>
            <ul><li>Open the user → <b>Security credentials</b> → <b>Create access key</b> → copy the <b>Access key ID</b> and <b>Secret access key</b>.</li></ul>
            <h4>3 · Fill the form</h4>
            <div class="og-steps">
                <div class="og-step"><b>▸</b><strong>Access key ID</strong><span>e.g. <code>AKIA…</code></span></div>
                <div class="og-step"><b>▸</b><strong>Secret access key</strong><span>the long secret (shown once)</span></div>
                <div class="og-step"><b>▸</b><strong>Bucket</strong><span>your bucket name</span></div>
                <div class="og-step"><b>▸</b><strong>Region</strong><span>e.g. <code>eu-west-1</code></span></div>
                <div class="og-step"><b>▸</b><strong>Endpoint</strong><span>leave <b>blank</b> for AWS</span></div>
                <div class="og-step"><b>▸</b><strong>Public bucket</strong><span>tick only if files should be world-readable</span></div>
            </div>
            <div class="og-warn"><i class="bi bi-exclamation-triangle"></i><div>For browser <b>direct uploads</b>, add a CORS rule on the bucket allowing <code>PUT</code> from your app's origin. Server-proxied uploads work without CORS.</div></div>
            <div class="og-tip"><i class="bi bi-check-circle"></i><div>Save, then hit <b>Test</b> — it verifies the keys can reach the bucket.</div></div>
HTML],
        ['id' => 's3compat', 'label' => 'R2 · MinIO · Backblaze', 'icon' => 'hdd-stack', 'keywords' => 'cloudflare r2 minio backblaze b2 wasabi endpoint path style s3 compatible', 'body' => <<<HTML
            <h3>S3-compatible providers</h3>
            <div class="og-lead"><i class="bi bi-hdd-stack"></i><div>Cloudflare R2, MinIO, Backblaze B2 and Wasabi all speak the S3 API. Use the <b>Amazon S3</b> type and just set the <b>Endpoint</b> (and usually <b>Path-style</b>).</div></div>
            <h4>Endpoint examples</h4>
            <ul>
                <li><b>Cloudflare R2</b> — <code>https://&lt;account-id&gt;.r2.cloudflarestorage.com</code>, region <code>auto</code>.</li>
                <li><b>Backblaze B2</b> — <code>https://s3.&lt;region&gt;.backblazeb2.com</code> (e.g. <code>s3.us-west-004.backblazeb2.com</code>).</li>
                <li><b>Wasabi</b> — <code>https://s3.&lt;region&gt;.wasabisys.com</code>.</li>
                <li><b>MinIO (self-run)</b> — <code>https://minio.example.com</code>, tick <b>Path-style</b>.</li>
            </ul>
            <h4>Path-style vs virtual-hosted</h4>
            <p><b>Path-style</b> puts the bucket in the URL path (<code>endpoint/bucket/key</code>). MinIO and some setups need it; R2/Backblaze/Wasabi generally don't. If uploads fail with a DNS/host error, tick Path-style.</p>
            <div class="og-tip"><i class="bi bi-check-circle"></i><div>Everything else (keys, bucket, public toggle) is identical to Amazon S3.</div></div>
HTML],
        ['id' => 'gdrive', 'label' => 'Google Drive', 'icon' => 'google', 'keywords' => 'google drive oauth client id secret consent screen scope drive.file folder id', 'body' => <<<HTML
            <h3>Google Drive</h3>
            <div class="og-lead"><i class="bi bi-google"></i><div>You'll register an <b>OAuth app</b> in Google Cloud, then click <b>Connect</b> here to authorize your Drive account.</div></div>
            <h4>1 · Google Cloud Console</h4>
            <ul>
                <li><a href="https://console.cloud.google.com/" target="_blank" rel="noopener">console.cloud.google.com</a> → create/pick a <b>project</b>.</li>
                <li><b>APIs &amp; Services → Library</b> → enable <b>Google Drive API</b>.</li>
                <li><b>OAuth consent screen</b> → <b>External</b> → fill app name + your email. Add scope <code>.../auth/drive.file</code>. Add your Google account under <b>Test users</b> (or Publish the app).</li>
            </ul>
            <h4>2 · Create the OAuth client</h4>
            <ul>
                <li><b>Credentials → Create credentials → OAuth client ID → Web application</b>.</li>
                <li>Under <b>Authorized redirect URIs</b> add exactly:<br><code class="sc-uri">$ruri</code></li>
                <li>Copy the <b>Client ID</b> and <b>Client secret</b>.</li>
            </ul>
            <h4>3 · Fill the form &amp; connect</h4>
            <ul>
                <li>Paste <b>Client ID</b> + <b>Client secret</b>. <b>Folder ID</b> is optional.</li>
                <li>Click <b>Save &amp; connect account</b> → approve on Google's screen (grant <b>offline</b> access) → you'll return here marked <b>connected</b>.</li>
            </ul>
            <div class="og-tip"><i class="bi bi-folder"></i><div><b>Folder ID</b> targets a specific folder: open it in Drive and copy the last part of the URL — <code>drive.google.com/drive/folders/<b>THIS_ID</b></code>. Blank = the app's own space.</div></div>
            <div class="og-warn"><i class="bi bi-exclamation-triangle"></i><div>The redirect URI must match <b>character-for-character</b> (including https and no trailing slash), or Google rejects the sign-in with <code>redirect_uri_mismatch</code>.</div></div>
HTML],
        ['id' => 'onedrive', 'label' => 'OneDrive', 'icon' => 'microsoft', 'keywords' => 'onedrive microsoft azure app registration graph files.readwrite offline access client secret base folder', 'body' => <<<HTML
            <h3>Microsoft OneDrive</h3>
            <div class="og-lead"><i class="bi bi-microsoft"></i><div>Register an app in <b>Azure</b>, grant Microsoft Graph file permissions, then <b>Connect</b> your OneDrive account here.</div></div>
            <h4>1 · Register an app</h4>
            <ul>
                <li><a href="https://entra.microsoft.com/" target="_blank" rel="noopener">Microsoft Entra / Azure portal</a> → <b>App registrations → New registration</b>.</li>
                <li>Supported accounts: pick <b>Accounts in any organizational directory and personal Microsoft accounts</b> for consumer OneDrive.</li>
                <li><b>Redirect URI</b> → platform <b>Web</b> → paste exactly:<br><code class="sc-uri">$ruri</code></li>
            </ul>
            <h4>2 · Permissions &amp; secret</h4>
            <ul>
                <li><b>API permissions → Add → Microsoft Graph → Delegated</b> → add <code>Files.ReadWrite</code> and <code>offline_access</code>.</li>
                <li><b>Certificates &amp; secrets → New client secret</b> → copy the secret <b>Value</b> (not the ID) immediately.</li>
                <li>From <b>Overview</b>, copy the <b>Application (client) ID</b>.</li>
            </ul>
            <h4>3 · Fill the form &amp; connect</h4>
            <ul>
                <li><b>Client ID</b> = Application (client) ID. <b>Client secret</b> = the secret Value. <b>Base folder</b> is optional (e.g. <code>AppUploads</code>).</li>
                <li>Click <b>Save &amp; connect account</b> → approve on Microsoft's screen → you'll return marked <b>connected</b>.</li>
            </ul>
            <div class="og-warn"><i class="bi bi-exclamation-triangle"></i><div>Copy the secret <b>Value</b> right after creating it — Azure hides it afterwards. Files over 4&nbsp;MB need upload sessions (not yet enabled) — keep large files on S3 for now.</div></div>
HTML],
        ['id' => 'ftp', 'label' => 'Custom FTP / SFTP', 'icon' => 'hdd-network', 'keywords' => 'ftp sftp ftps hosting space cpanel host public url base path passive test connection', 'body' => <<<HTML
            <h3>Custom FTP / SFTP — use your hosting space</h3>
            <p>Store uploads in a folder on any FTP/SFTP server you already have (your web host, a NAS, a VPS). Great when you'd rather use space you're already paying for than a cloud bucket.</p>
            <ol class="og-list">
                <li><b>Protocol</b> — pick <b>SFTP</b> if your host offers SSH (most secure); otherwise FTPS, or plain FTP as a last resort.</li>
                <li><b>Host / Port / Username / Password</b> — the FTP/SFTP login from your host (often the same as cPanel, or an FTP account you create). Leave port blank for the default (22 SFTP, 21 FTP).</li>
                <li><b>Remote folder (base path)</b> — the absolute folder files go into, e.g. <code>/home/acct/public_html/media</code>. It's created automatically if missing.</li>
                <li><b>Public base URL</b> — the web address that serves that folder, e.g. <code>https://yoursite.com/media</code>. This is required: uploaded files are served straight from here.</li>
                <li>Click <b>Test connection</b> — it logs in and checks the folder is writable. It only <b>saves once the test passes</b>.</li>
            </ol>
            <div class="og-tip"><i class="bi bi-lightbulb"></i><div>Point the <b>base path</b> and the <b>public URL</b> at the same folder. Example: base path <code>/home/acct/public_html/media</code> served at <code>https://yoursite.com/media</code>.</div></div>
            <div class="og-warn"><i class="bi bi-exclamation-triangle"></i><div>FTP files are served over a public URL, so use this for public assets. Don't point a private/sensitive field at an FTP connection whose folder is web-exposed.</div></div>
HTML],
        ['id' => 'troubleshoot', 'label' => 'Troubleshooting', 'icon' => 'wrench-adjustable', 'keywords' => 'error problem no refresh token invalid_grant access denied 403 mismatch not connected ftp sftp writable', 'body' => <<<HTML
            <h3>Troubleshooting</h3>
            <h4>“Not connected” after OAuth</h4>
            <p>The provider didn't return a <b>refresh token</b>. Re-run <b>Connect</b> and make sure you granted <b>offline</b> access. For Google, add your account as a <b>Test user</b> (or publish the consent screen).</p>
            <h4><code>redirect_uri_mismatch</code></h4>
            <p>The redirect URI in the provider console must equal this app's callback <b>exactly</b>:<br><code class="sc-uri">$ruri</code></p>
            <h4>S3 <code>AccessDenied</code> / <code>403</code></h4>
            <p>The IAM policy is missing an action or points at the wrong bucket ARN. Confirm <code>s3:PutObject</code>, <code>s3:GetObject</code>, <code>s3:DeleteObject</code> on <code>arn:aws:s3:::YOUR-BUCKET/*</code>.</p>
            <h4>S3 host/DNS error on upload</h4>
            <p>Wrong <b>region</b> or missing <b>endpoint</b> for an S3-compatible provider — tick <b>Path-style</b> and set the endpoint.</p>
            <div class="og-tip"><i class="bi bi-check-circle"></i><div>The <b>Test</b> button next to each saved connection reports the exact provider error message — start there.</div></div>
HTML],
    ],
];
?>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?= htmlspecialchars(\PpPhp\Core\Brand::name()) ?> · Storage Connections</title>
    <?php include constant('BASE_PATH') . '/core/views/favicon.php'; ?>
    <link href="<?= $assetsUrl ?>/bootstrap/css/bootstrap.min.css" rel="stylesheet">
    <link href="<?= $assetsUrl ?>/icons/bootstrap-icons.css" rel="stylesheet">
    <?php include constant('BASE_PATH') . '/core/views/theme-head.php'; ?>
    <style>
        body { background: var(--pp-app-bg, #f5f6fa); color: var(--pp-text, #172033); }
        .ppb-topbar { background: var(--pp-surface, #fff); border-bottom: 1px solid var(--pp-border, #e6e9f0); }
        .ppb-wrap { max-width: 860px; margin: 2rem auto; padding: 0 1rem; }
        .sc-card { background: var(--pp-surface, #fff); border: 1px solid var(--pp-border, #e6e9f0); border-radius: 14px; padding: 1.1rem 1.3rem; margin-bottom: 1rem; }
        .sc-conn { display: flex; align-items: center; gap: .9rem; }
        .sc-conn .ic { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 1.2rem; background: var(--pp-primary-soft, #eef2ff); color: var(--pp-primary, #4f46e5); flex: 0 0 auto; }
        .sc-conn .meta { flex: 1 1 auto; min-width: 0; }
        .sc-conn .meta b { display: block; }
        .sc-conn .meta small { color: var(--pp-muted, #6b7280); }
        .sc-status { font-size: .82rem; }
        .sc-status.ok { color: #16a34a; } .sc-status.bad { color: #dc2626; }
        .sc-form label { font-size: .82rem; font-weight: 600; margin-bottom: .2rem; }
        .sc-form .form-control, .sc-form .form-select { font-size: .9rem; }
        .sc-hide { display: none !important; }  /* beat Bootstrap's .d-flex (display:flex !important) on hidden groups */
        .sc-note { font-size: .82rem; color: var(--pp-muted, #6b7280); background: var(--pp-app-bg, #f5f6fa); border-radius: 10px; padding: .7rem .9rem; }
        code.sc-uri { background: #0f172a; color: #e2e8f0; padding: .15rem .4rem; border-radius: 6px; font-size: .8rem; word-break: break-all; display: inline-block; margin-top: .2rem; }
        .og-code { background: #0f172a; color: #e2e8f0; padding: .7rem .85rem; border-radius: 9px; font-size: .78rem; line-height: 1.5; overflow-x: auto; margin: .5rem 0; white-space: pre; }
        .sc-form label .bi-question-circle { color: var(--pp-muted, #9aa4b2); cursor: help; font-size: .8rem; }

        /* ── Phone (native-app feel) ─────────────────────────────────
           The Type <select> ships inline style="width:auto", so its very long
           option ("Amazon S3 / S3-compatible …") makes it 388px and pushes the
           page sideways. Force every control full-width and stop the overflow. */
        @media (max-width: 767.98px) {
            html, body { overflow-x: hidden; }
            .ppb-wrap { margin: 1.2rem auto; padding: 0 .85rem; }
            #scDriver { width: 100% !important; }
            .sc-form .form-select, .sc-form .form-control, .sc-form select, .sc-form input, .sc-form textarea { width: 100%; max-width: 100%; }
            .sc-form input, .sc-form select, .sc-form textarea { font-size: 16px; }  /* iOS: no focus-zoom */
            .sc-card { padding: 1rem; }
        }
    </style>
</head>
<body>
<div class="ppb-topbar py-2 px-3 d-flex align-items-center">
    <a href="<?= $baseUrl ?>/settings" class="btn btn-sm btn-light"><i class="bi bi-arrow-left"></i> Settings</a>
    <strong class="ms-3"><i class="bi bi-cloud-arrow-up"></i> Storage Connections</strong>
    <div class="ms-auto d-flex align-items-center gap-2">
        <?php include constant('BASE_PATH') . '/core/views/theme-toggle.php'; ?>
        <button type="button" class="btn btn-sm btn-outline-primary" data-guide-open="storageGuide" data-tip="Full setup instructions with examples for each provider">
            <i class="bi bi-question-circle"></i> Guide
        </button>
    </div>
</div>

<div class="ppb-wrap">
    <?php if ($flash): ?>
        <div class="alert alert-<?= ($flash['type'] ?? '') === 'error' ? 'danger' : 'success' ?> py-2"><?= htmlspecialchars($flash['message'] ?? '') ?></div>
    <?php endif; ?>

    <p class="text-muted">Add a cloud target here and it becomes selectable in every File/Image field's <b>“Store on”</b> dropdown. This server (self-hosted) is always available and needs no setup.</p>

    <!-- Existing connections -->
    <?php if ($connections): ?>
        <h6 class="text-uppercase text-muted small mt-3">Your connections</h6>
        <?php foreach ($connections as $c): $d = (string) ($c['driver'] ?? ''); ?>
            <div class="sc-card sc-conn">
                <span class="ic"><i class="bi <?= $driverIcon[$d] ?? 'bi-hdd' ?>"></i></span>
                <div class="meta">
                    <b><?= htmlspecialchars((string) ($c['label'] ?? $c['id'])) ?></b>
                    <small><?= htmlspecialchars($driverLabel[$d] ?? $d) ?>
                        <?php if ($d === 's3'): ?> · <?= htmlspecialchars((string) ($c['bucket'] ?? '')) ?> (<?= htmlspecialchars((string) ($c['region'] ?? '')) ?>)
                        <?php elseif (($c['refreshToken'] ?? '') !== ''): ?> · connected
                        <?php else: ?> · <span class="text-warning">not connected</span><?php endif; ?>
                    </small>
                    <span class="sc-status" data-status-for="<?= htmlspecialchars((string) $c['id']) ?>"></span>
                </div>
                <button class="btn btn-sm btn-outline-secondary" data-tip="Check the credentials can reach this storage right now." onclick="scTest('<?= htmlspecialchars((string) $c['id']) ?>', this)">Test</button>
                <?php if ($d !== 's3' && ($c['refreshToken'] ?? '') === '' && ($c['clientId'] ?? '') !== ''): ?>
                    <a class="btn btn-sm btn-primary" href="<?= $baseUrl ?>/settings/storage/oauth/start?id=<?= urlencode((string) $c['id']) ?>">Connect</a>
                <?php endif; ?>
                <form method="post" action="<?= $baseUrl ?>/settings/storage/delete" onsubmit="return confirm('Remove this connection?')" class="d-inline">
                    <input type="hidden" name="_token" value="<?= $token ?>"><input type="hidden" name="id" value="<?= htmlspecialchars((string) $c['id']) ?>">
                    <button class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></button>
                </form>
            </div>
        <?php endforeach; ?>
    <?php endif; ?>

    <!-- Add a connection -->
    <div class="sc-card mt-3">
        <h6 class="mb-3"><i class="bi bi-plus-circle"></i> Add a connection</h6>
        <div class="mb-3">
            <label class="me-2">Type</label>
            <select id="scDriver" class="form-select form-select-sm d-inline-block" style="width:auto" onchange="scSwitch()" data-tip="Where files upload to. S3 uses keys; Google Drive / OneDrive connect your account with OAuth.">
                <option value="s3">Amazon S3 / S3-compatible (R2, MinIO, Backblaze…)</option>
                <option value="gdrive">Google Drive</option>
                <option value="onedrive">Microsoft OneDrive</option>
                <option value="ftp">Custom FTP / SFTP (use your hosting space)</option>
            </select>
            <a href="#" class="small ms-2" data-guide-open="storageGuide"><i class="bi bi-question-circle"></i> How do I set this up?</a>
        </div>

        <form method="post" action="<?= $baseUrl ?>/settings/storage/save" class="sc-form row g-2">
            <input type="hidden" name="_token" value="<?= $token ?>">
            <input type="hidden" name="driver" id="scDriverField" value="s3">
            <div class="col-12"><label>Name <i class="bi bi-question-circle" data-tip="A friendly label you'll recognise in a field's Store on dropdown."></i></label><input class="form-control form-control-sm" name="label" placeholder="e.g. Product images bucket" data-tip="A friendly label you'll recognise in a field's Store on dropdown." required></div>

            <!-- S3 -->
            <div class="sc-grp sc-s3 col-md-6"><label>Access key ID <i class="bi bi-question-circle" data-tip="From your IAM user's access key. Starts with AKIA… for AWS."></i></label><input class="form-control form-control-sm" name="accessKey" placeholder="AKIA…" data-tip="From your IAM user's access key. Starts with AKIA… for AWS."></div>
            <div class="sc-grp sc-s3 col-md-6"><label>Secret access key <i class="bi bi-question-circle" data-tip="The secret shown once when you created the access key. Leave blank when editing to keep the saved one."></i></label><input class="form-control form-control-sm" type="password" name="secretKey" autocomplete="new-password" data-tip="The secret shown once when you created the access key. Leave blank when editing to keep the saved one."></div>
            <div class="sc-grp sc-s3 col-md-6"><label>Bucket <i class="bi bi-question-circle" data-tip="The exact bucket name you created in S3."></i></label><input class="form-control form-control-sm" name="bucket" data-tip="The exact bucket name you created in S3."></div>
            <div class="sc-grp sc-s3 col-md-6"><label>Region <i class="bi bi-question-circle" data-tip="The bucket's region, e.g. us-east-1 or eu-west-1. Use 'auto' for Cloudflare R2."></i></label><input class="form-control form-control-sm" name="region" value="us-east-1" data-tip="The bucket's region, e.g. us-east-1 or eu-west-1. Use 'auto' for Cloudflare R2."></div>
            <div class="sc-grp sc-s3 col-md-8"><label>Endpoint <span class="text-muted">(blank for AWS; set for R2/MinIO)</span> <i class="bi bi-question-circle" data-tip="Leave blank for Amazon S3. For R2/MinIO/Backblaze/Wasabi paste their S3 endpoint URL."></i></label><input class="form-control form-control-sm" name="endpoint" placeholder="https://&lt;account&gt;.r2.cloudflarestorage.com" data-tip="Leave blank for Amazon S3. For R2/MinIO/Backblaze/Wasabi paste their S3 endpoint URL."></div>
            <div class="sc-grp sc-s3 col-md-4 d-flex align-items-end gap-3 pb-1">
                <div class="form-check"><input class="form-check-input" type="checkbox" name="pathStyle" id="scPath" data-tip="Puts the bucket in the URL path. Needed for MinIO and some self-hosted S3; tick if uploads fail with a host/DNS error."><label class="form-check-label small" for="scPath">Path-style</label></div>
                <div class="form-check"><input class="form-check-input" type="checkbox" name="public" id="scPub" data-tip="Serve files via a direct public URL. Leave off for private files streamed through the app."><label class="form-check-label small" for="scPub">Public bucket</label></div>
            </div>

            <!-- OAuth (Google Drive / OneDrive) -->
            <div class="sc-grp sc-oauth sc-hide col-12">
                <div class="sc-note mb-2">
                    Register an OAuth app in the provider's console and add this <b>redirect URI</b> <i class="bi bi-question-circle" data-tip="Paste this EXACTLY into your provider's Authorized redirect URIs — character-for-character."></i>:<br>
                    <code class="sc-uri"><?= htmlspecialchars($redirectUri) ?></code>
                    <div class="mt-1"><a href="#" data-guide-open="storageGuide">Open the step-by-step guide →</a></div>
                </div>
            </div>
            <div class="sc-grp sc-oauth sc-hide col-md-6"><label>Client ID <i class="bi bi-question-circle" data-tip="Google: OAuth client ID. OneDrive: the Application (client) ID from Azure Overview."></i></label><input class="form-control form-control-sm" name="clientId" data-tip="Google: OAuth client ID. OneDrive: the Application (client) ID from Azure Overview."></div>
            <div class="sc-grp sc-oauth sc-hide col-md-6"><label>Client secret <i class="bi bi-question-circle" data-tip="The OAuth client secret (Azure: the secret Value, not its ID). Leave blank when editing to keep the saved one."></i></label><input class="form-control form-control-sm" type="password" name="clientSecret" autocomplete="new-password" data-tip="The OAuth client secret (Azure: the secret Value, not its ID). Leave blank when editing to keep the saved one."></div>
            <div class="sc-grp sc-oauth sc-hide col-md-6 sc-onedrive-only"><label>Base folder <span class="text-muted">(OneDrive)</span> <i class="bi bi-question-circle" data-tip="Optional sub-folder in your OneDrive to store uploads, e.g. AppUploads. Blank = drive root."></i></label><input class="form-control form-control-sm" name="folder" placeholder="e.g. AppUploads" data-tip="Optional sub-folder in your OneDrive to store uploads, e.g. AppUploads. Blank = drive root."></div>
            <div class="sc-grp sc-oauth sc-hide col-md-6 sc-gdrive-only"><label>Folder ID <span class="text-muted">(Google Drive, optional)</span> <i class="bi bi-question-circle" data-tip="Optional. Open a Drive folder and copy the last part of its URL to upload into it. Blank = the app's own space."></i></label><input class="form-control form-control-sm" name="folderId" data-tip="Optional. Open a Drive folder and copy the last part of its URL to upload into it. Blank = the app's own space."></div>

            <!-- Custom FTP / SFTP -->
            <div class="sc-grp sc-ftp sc-hide col-12"><div class="sc-note mb-1"><i class="bi bi-hdd-network"></i> Store uploads in a folder on an FTP/SFTP server you already have (e.g. your hosting space). Files are served from the <b>Public base URL</b> you map to that folder.</div></div>
            <div class="sc-grp sc-ftp sc-hide col-md-4"><label>Protocol <i class="bi bi-question-circle" data-tip="SFTP (over SSH) is the most secure — use it whenever your host offers SSH. FTPS is FTP over TLS; plain FTP is a last resort."></i></label>
                <select class="form-select form-select-sm" name="protocol">
                    <option value="sftp">SFTP (SSH) — recommended</option>
                    <option value="ftps">FTPS (FTP over TLS)</option>
                    <option value="ftp">FTP (plain)</option>
                </select></div>
            <div class="sc-grp sc-ftp sc-hide col-md-5"><label>Host <i class="bi bi-question-circle" data-tip="The server's address, e.g. ftp.yoursite.com or an IP. Your host provides this — it is NOT your website URL."></i></label><input class="form-control form-control-sm" name="host" placeholder="ftp.yoursite.com"></div>
            <div class="sc-grp sc-ftp sc-hide col-md-3"><label>Port <i class="bi bi-question-circle" data-tip="Leave blank for the default: 22 for SFTP, 21 for FTP/FTPS."></i></label><input class="form-control form-control-sm" name="port" type="number" placeholder="22"></div>
            <div class="sc-grp sc-ftp sc-hide col-md-6"><label>Username <i class="bi bi-question-circle" data-tip="The FTP/SFTP login (often your hosting/cPanel username or an FTP account you created)."></i></label><input class="form-control form-control-sm" name="user" autocomplete="off"></div>
            <div class="sc-grp sc-ftp sc-hide col-md-6"><label>Password <i class="bi bi-question-circle" data-tip="The FTP/SFTP password. Stored encrypted. Leave blank when editing to keep the saved one."></i></label><input class="form-control form-control-sm" type="password" name="password" autocomplete="new-password"></div>
            <div class="sc-grp sc-ftp sc-hide col-md-6"><label>Remote folder (base path) <i class="bi bi-question-circle" data-tip="Absolute folder on the server where files are stored, e.g. /home/acct/public_html/media. It's created if missing."></i></label><input class="form-control form-control-sm" name="basePath" placeholder="/home/acct/public_html/media"></div>
            <div class="sc-grp sc-ftp sc-hide col-md-6"><label>Public base URL <i class="bi bi-question-circle" data-tip="The web address that maps to the remote folder, e.g. https://yoursite.com/media. Files are served from here. Required."></i></label><input class="form-control form-control-sm" name="publicBase" placeholder="https://yoursite.com/media"></div>
            <div class="sc-grp sc-ftp sc-hide col-12 d-flex align-items-center gap-3 flex-wrap pb-1">
                <div class="form-check"><input class="form-check-input" type="checkbox" name="passive" id="scPassive" checked data-tip="Passive mode for plain FTP/FTPS — leave on unless your host requires active mode."><label class="form-check-label small" for="scPassive">Passive mode</label></div>
                <button type="button" class="btn btn-outline-secondary btn-sm" onclick="scTestForm(this)"><i class="bi bi-plug"></i> Test connection</button>
                <span class="sc-status" id="scFtpStatus"></span>
            </div>

            <div class="col-12 mt-2">
                <button class="btn btn-primary btn-sm" type="submit"><i class="bi bi-save"></i> Save</button>
                <button class="btn btn-success btn-sm sc-oauth sc-hide" type="submit" name="connect" value="1"><i class="bi bi-box-arrow-up-right"></i> Save &amp; connect account</button>
                <span class="text-muted small sc-ftp sc-hide ms-1">Saved only if the connection test passes.</span>
            </div>
        </form>
    </div>
</div>

<script>
function scSwitch() {
    var d = document.getElementById('scDriver').value;
    document.getElementById('scDriverField').value = d;
    var isOauth = (d === 'gdrive' || d === 'onedrive');
    document.querySelectorAll('.sc-s3').forEach(function (e) { e.classList.toggle('sc-hide', d !== 's3'); });
    document.querySelectorAll('.sc-oauth').forEach(function (e) { e.classList.toggle('sc-hide', !isOauth); });
    document.querySelectorAll('.sc-ftp').forEach(function (e) { e.classList.toggle('sc-hide', d !== 'ftp'); });
    document.querySelectorAll('.sc-onedrive-only').forEach(function (e) { e.classList.toggle('sc-hide', d !== 'onedrive'); });
    document.querySelectorAll('.sc-gdrive-only').forEach(function (e) { e.classList.toggle('sc-hide', d !== 'gdrive'); });
    // required only on the visible type's key fields
    document.querySelectorAll('.sc-s3 [name=accessKey], .sc-s3 [name=bucket]').forEach(function (e) { e.required = d === 's3'; });
    document.querySelectorAll('.sc-ftp [name=host], .sc-ftp [name=user], .sc-ftp [name=publicBase]').forEach(function (e) { e.required = d === 'ftp'; });
}
// Test the CURRENT form (before saving) — used by Custom FTP/SFTP.
function scTestForm(btn) {
    var form = btn.closest('form');
    var el = document.getElementById('scFtpStatus');
    btn.disabled = true; var orig = btn.innerHTML; btn.innerHTML = '<i class="bi bi-arrow-repeat"></i> Testing…';
    el.className = 'sc-status'; el.textContent = '';
    fetch('<?= $baseUrl ?>/settings/storage/test', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams(new FormData(form)) })
        .then(function (r) { return r.json(); })
        .then(function (res) { el.className = 'sc-status ' + (res.ok ? 'ok' : 'bad'); el.textContent = (res.ok ? '✓ ' : '✕ ') + (res.message || ''); })
        .catch(function () { el.className = 'sc-status bad'; el.textContent = '✕ Test failed to run'; })
        .finally(function () { btn.disabled = false; btn.innerHTML = orig; });
}
function scTest(id, btn) {
    var el = document.querySelector('[data-status-for="' + id + '"]');
    btn.disabled = true; var orig = btn.textContent; btn.textContent = 'Testing…';
    fetch('<?= $baseUrl ?>/settings/storage/test', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: 'id=' + encodeURIComponent(id) + '&_token=<?= $token ?>' })
        .then(function (r) { return r.json(); })
        .then(function (res) { el.className = 'sc-status ' + (res.ok ? 'ok' : 'bad'); el.textContent = (res.ok ? '✓ ' : '✕ ') + (res.message || ''); })
        .catch(function () { el.className = 'sc-status bad'; el.textContent = '✕ Test failed'; })
        .finally(function () { btn.disabled = false; btn.textContent = orig; });
}
scSwitch();
</script>
<?php include constant('BASE_PATH') . '/core/views/owner-ux.php'; ?>
</body>
</html>
