<?php
if (!defined('APP_ROOT')) exit;

const UPDATE_DIARY_MAX_CONTENT_LENGTH = 2000;
const UPDATE_DIARY_SIDEBAR_VISIBLE_COUNT = 10;
const UPDATE_DIARY_ADMIN_LIST_LIMIT = 100;

function update_diary_install(array $plugin): void
{
    $t = app_db_types();
    app_db_create_table('plugin_update_diary_records', "id {$t['id']},record_date {$t['key']} NOT NULL,content {$t['text']} NOT NULL,status {$t['key']} NOT NULL DEFAULT 'published',created_at {$t['uint']} NOT NULL,updated_at {$t['uint']} NOT NULL,published_at {$t['uint']} NOT NULL DEFAULT 0");
    app_db_ensure_columns('plugin_update_diary_records', [
        'status' => "{$t['key']} NOT NULL DEFAULT 'published'",
        'published_at' => "{$t['uint']} NOT NULL DEFAULT 0",
    ]);
    q("UPDATE plugin_update_diary_records SET status='published' WHERE status IS NULL OR status NOT IN ('draft','published')");
    q("UPDATE plugin_update_diary_records SET published_at=created_at WHERE status='published' AND published_at=0");
    app_db_create_index('idx_plugin_update_diary_records_date', 'plugin_update_diary_records(record_date DESC,id DESC)');
    app_db_create_index('idx_plugin_update_diary_records_published', 'plugin_update_diary_records(status,record_date DESC,id DESC)');
}

function update_diary_uninstall(array $plugin): void
{
    app_db_drop_index('idx_plugin_update_diary_records_published', 'plugin_update_diary_records');
    app_db_drop_index('idx_plugin_update_diary_records_date', 'plugin_update_diary_records');
    app_db_drop_table('plugin_update_diary_records');
}

function update_diary_post_record_date(): string
{
    $value = trim((string)($_POST['update_diary_record_date'] ?? ''));
    if (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $value, $matches) !== 1 || !checkdate((int)$matches[2], (int)$matches[3], (int)$matches[1])) {
        err('请选择有效的记录日期');
    }
    return $value;
}

function update_diary_post_content(): string
{
    $value = str_replace(["\r\n", "\r"], "\n", trim((string)($_POST['update_diary_content'] ?? '')));
    $value = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $value) ?? '';
    if ($value === '') err('更新内容不能为空');
    if (cut($value, UPDATE_DIARY_MAX_CONTENT_LENGTH) !== $value) err('更新内容不能超过 ' . UPDATE_DIARY_MAX_CONTENT_LENGTH . ' 个字符');
    return $value;
}

function update_diary_post_record_id(): int
{
    $value = trim((string)($_POST['update_diary_record_id'] ?? ''));
    if (!preg_match('/^\d+$/', $value) || (int)$value < 1) err('记录不存在');
    return (int)$value;
}

function update_diary_post_status(): string
{
    $status = (string)($_POST['update_diary_status'] ?? '');
    if (!in_array($status, ['draft', 'published'], true)) err('请选择有效的发布状态');
    return $status;
}

function update_diary_status_label(string $status): string
{
    return $status === 'draft' ? '草稿' : '已发布';
}

function update_diary_status_options(string $selected): string
{
    $published_selected = $selected === 'published' ? ' selected' : '';
    $draft_selected = $selected === 'draft' ? ' selected' : '';
    return '<option value="published"' . $published_selected . '>已发布</option><option value="draft"' . $draft_selected . '>草稿</option>';
}

function update_diary_datetime(int $time): string
{
    return $time > 0 ? date('Y-m-d H:i', $time) : '—';
}

function update_diary_record_by_id(int $record_id): ?array
{
    return one('SELECT id,record_date,status,published_at FROM plugin_update_diary_records WHERE id=?', [$record_id]);
}

function update_diary_record_by_date(string $record_date, int $exclude_id = 0): ?array
{
    $sql = 'SELECT id,status,published_at FROM plugin_update_diary_records WHERE record_date=?';
    $params = [$record_date];
    if ($exclude_id > 0) {
        $sql .= ' AND id<>?';
        $params[] = $exclude_id;
    }
    return one($sql . ' ORDER BY id DESC LIMIT 1', $params);
}

function update_diary_published_at(string $status, ?array $previous, int $at): int
{
    if ($status !== 'published') return 0;
    if ($previous && (string)($previous['status'] ?? '') === 'published' && (int)($previous['published_at'] ?? 0) > 0) {
        return (int)$previous['published_at'];
    }
    return $at;
}

function update_diary_add_or_update_record(string $record_date, string $content, string $status): bool
{
    $at = now();
    $existing = update_diary_record_by_date($record_date);
    if ($existing) {
        q('UPDATE plugin_update_diary_records SET content=?,status=?,updated_at=?,published_at=? WHERE id=?', [
            $content,
            $status,
            $at,
            update_diary_published_at($status, $existing, $at),
            (int)$existing['id'],
        ]);
        return false;
    }
    q('INSERT INTO plugin_update_diary_records(record_date,content,status,created_at,updated_at,published_at) VALUES(?,?,?,?,?,?)', [
        $record_date,
        $content,
        $status,
        $at,
        $at,
        update_diary_published_at($status, null, $at),
    ]);
    return true;
}

function update_diary_update_record(int $record_id, string $record_date, string $content, string $status): void
{
    $record = update_diary_record_by_id($record_id);
    if (!$record) err('记录不存在');
    if ($record_date !== (string)$record['record_date'] && update_diary_record_by_date($record_date, $record_id)) err('该日期已有更新记录');
    $at = now();
    q('UPDATE plugin_update_diary_records SET record_date=?,content=?,status=?,updated_at=?,published_at=? WHERE id=?', [
        $record_date,
        $content,
        $status,
        $at,
        update_diary_published_at($status, $record, $at),
        $record_id,
    ]);
}

function update_diary_sidebar_records(): array
{
    static $records = null;
    if (is_array($records)) return $records;
    return $records = q("SELECT id,record_date,content FROM plugin_update_diary_records WHERE status='published' ORDER BY record_date DESC,id DESC")->fetchAll();
}

function update_diary_sidebar_html(array $records): string
{
    if (!$records) {
        return '<section class="card sidebar-card update-diary-sidebar" aria-label="更新日记"><div class="update-diary-empty-head"><span>更新日记</span><span>暂无已发布记录</span></div></section>';
    }

    $items = '';
    foreach ($records as $record) {
        $date = (string)$record['record_date'];
        $items .= '<article class="update-diary-record" data-update-diary-record><time datetime="' . h($date) . '">' . h($date) . '</time><div class="update-diary-record-content">' . nl2br(h((string)$record['content'])) . '</div></article>';
    }
    $count = count($records);
    return '<section class="card sidebar-card update-diary-sidebar" aria-label="更新日记"><details class="update-diary-details"><summary class="update-diary-summary"><span class="update-diary-summary-title">更新日记</span><span class="update-diary-summary-meta">共 ' . $count . ' 条 <span class="update-diary-summary-open">展开</span><span class="update-diary-summary-close">收起</span></span></summary><div class="update-diary-records" data-update-diary-records data-update-diary-visible-count="' . UPDATE_DIARY_SIDEBAR_VISIBLE_COUNT . '">' . $items . '</div></details></section>';
}

function update_diary_sidebar(mixed $value, array $ctx): mixed
{
    if (!is_array($value) || !empty($ctx['is_admin'])) return $value;
    $value[] = update_diary_sidebar_html(update_diary_sidebar_records());
    return $value;
}

function update_diary_admin_tabs(mixed $value, array $ctx): mixed
{
    if (!is_array($value)) return $value;
    $value['update_diary'] = ['label' => '更新日记', 'href' => admin_url(['tab' => 'update_diary'])];
    return $value;
}

function update_diary_admin_records_html(array $records): string
{
    $html = '<section class="update-diary-admin-section"><div class="update-diary-admin-section-head"><h2>最近记录</h2><span>最多显示 ' . UPDATE_DIARY_ADMIN_LIST_LIMIT . ' 条</span></div><div class="update-diary-admin-records">';
    foreach ($records as $record) {
        $id = (int)$record['id'];
        $date = (string)$record['record_date'];
        $content = (string)$record['content'];
        $status = (string)$record['status'] === 'draft' ? 'draft' : 'published';
        $status_class = $status === 'draft' ? ' update-diary-record-status-draft' : '';
        $meta = '<span class="update-diary-record-status' . $status_class . '">' . h(update_diary_status_label($status)) . '</span><span>最后编辑 ' . h(update_diary_datetime((int)$record['updated_at'])) . '</span><span class="update-diary-edit-hint">编辑</span>';
        $fields = '<div class="update-diary-edit-grid"><label class="update-diary-edit-label"><span>记录日期</span><input name="update_diary_record_date" type="date" value="' . h($date) . '" required></label><label class="update-diary-edit-label"><span>发布状态</span><select name="update_diary_status">' . update_diary_status_options($status) . '</select></label></div>';
        $html .= '<article class="update-diary-admin-record"><details class="update-diary-admin-editor"><summary class="update-diary-admin-record-main"><time datetime="' . h($date) . '">' . h($date) . '</time><div>' . nl2br(h($content)) . '</div><span class="update-diary-admin-record-meta">' . $meta . '</span></summary><form method="post" class="update-diary-edit-form">' . form_token() . '<input type="hidden" name="update_diary_action" value="update_record"><input type="hidden" name="update_diary_record_id" value="' . $id . '">' . $fields . '<label class="update-diary-edit-label"><span>更新内容</span><textarea name="update_diary_content" rows="6" maxlength="' . UPDATE_DIARY_MAX_CONTENT_LENGTH . '" required>' . h($content) . '</textarea></label><div class="update-diary-edit-actions"><button type="submit">保存修改</button></div></form></details><form method="post" class="update-diary-delete-form" data-update-diary-confirm="确定删除这条更新记录吗？此操作无法恢复。">' . form_token() . '<input type="hidden" name="update_diary_action" value="delete_record"><input type="hidden" name="update_diary_record_id" value="' . $id . '"><button type="submit">删除</button></form></article>';
    }
    if (!$records) $html .= '<div class="update-diary-admin-empty">还没有更新记录。</div>';
    return $html . '</div></section>';
}

function update_diary_admin_page(array $plugin): string
{
    need_admin();
    if (is_post_request()) {
        require_post();
        $action = (string)($_POST['update_diary_action'] ?? '');
        if ($action === 'add_record') {
            $added = update_diary_add_or_update_record(update_diary_post_record_date(), update_diary_post_content(), update_diary_post_status());
            set_flash($added ? '更新记录已添加' : '当天的更新记录已更新');
        } elseif ($action === 'update_record') {
            update_diary_update_record(update_diary_post_record_id(), update_diary_post_record_date(), update_diary_post_content(), update_diary_post_status());
            set_flash('更新记录已保存');
        } elseif ($action === 'delete_record') {
            if (q('DELETE FROM plugin_update_diary_records WHERE id=?', [update_diary_post_record_id()])->rowCount() !== 1) err('记录不存在');
            set_flash('更新记录已删除');
        } else {
            err('更新日记操作无效');
        }
        go(admin_url(['tab' => 'update_diary']));
    }

    $records = q('SELECT id,record_date,content,status,updated_at,published_at FROM plugin_update_diary_records ORDER BY record_date DESC,id DESC LIMIT ' . UPDATE_DIARY_ADMIN_LIST_LIMIT)->fetchAll();
    $form = '<section class="form-panel update-diary-admin-form"><h2>添加更新记录</h2><p>同一日期再次提交会更新当天记录；草稿仅在后台可见。</p><form method="post">' . form_token() . '<input type="hidden" name="update_diary_action" value="add_record"><div class="update-diary-add-grid"><label class="grid"><span>记录日期</span><input name="update_diary_record_date" type="date" value="' . h(date('Y-m-d')) . '" required></label><label class="grid"><span>发布状态</span><select name="update_diary_status">' . update_diary_status_options('published') . '</select></label></div><label class="grid"><span>更新内容<small>最多 ' . UPDATE_DIARY_MAX_CONTENT_LENGTH . ' 个字符。</small></span><textarea name="update_diary_content" rows="7" maxlength="' . UPDATE_DIARY_MAX_CONTENT_LENGTH . '" required></textarea></label><div class="row"><button type="submit">保存记录</button></div></form></section>';
    return '<div class="update-diary-admin">' . $form . update_diary_admin_records_html($records) . '</div>';
}

function update_diary_css(): string
{
    return <<<'CSS'
.update-diary-sidebar{overflow:hidden}.update-diary-sidebar .update-diary-empty-head,.update-diary-sidebar .update-diary-summary{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:16px}.update-diary-sidebar .update-diary-empty-head,.update-diary-sidebar .update-diary-summary-meta{color:var(--text-muted);font-size:var(--font-size-sm)}.update-diary-sidebar .update-diary-details{display:grid}.update-diary-sidebar .update-diary-summary{cursor:pointer;list-style:none}.update-diary-sidebar .update-diary-summary::-webkit-details-marker{display:none}.update-diary-sidebar .update-diary-summary:focus-visible{outline:2px solid var(--focus-ring);outline-offset:-2px}.update-diary-sidebar .update-diary-summary-title{color:var(--text);font-weight:600}.update-diary-sidebar .update-diary-summary-meta{display:inline-flex;align-items:center;gap:4px;white-space:nowrap}.update-diary-sidebar .update-diary-summary-close{display:none}.update-diary-sidebar .update-diary-details[open] .update-diary-summary-open{display:none}.update-diary-sidebar .update-diary-details[open] .update-diary-summary-close{display:inline}.update-diary-sidebar .update-diary-records{display:grid;max-height:min(640px,calc(100vh - 180px));overflow-y:auto;overscroll-behavior:contain;scrollbar-gutter:stable;border-top:1px solid var(--line-soft)}.update-diary-sidebar .update-diary-record{display:grid;gap:5px;padding:12px 16px;border-bottom:1px solid var(--line-soft)}.update-diary-sidebar .update-diary-record:last-child{border-bottom:0}.update-diary-sidebar .update-diary-record time,.update-diary-admin .update-diary-admin-record time{color:var(--text-subtle);font-size:var(--font-size-xs);font-variant-numeric:tabular-nums}.update-diary-sidebar .update-diary-record-content,.update-diary-admin .update-diary-admin-record-main>div{overflow-wrap:anywhere;white-space:normal}.update-diary-admin{display:grid;gap:16px}.update-diary-admin .update-diary-admin-form{display:grid;gap:16px;max-width:760px}.update-diary-admin .update-diary-admin-form>p{margin:0;color:var(--text-muted)}.update-diary-admin .update-diary-admin-form form{display:grid;gap:16px}.update-diary-admin .update-diary-admin-form textarea,.update-diary-admin .update-diary-edit-form textarea{width:100%;box-sizing:border-box;resize:vertical}.update-diary-admin .update-diary-admin-section{overflow:hidden;border:1px solid var(--line);border-radius:12px;background:var(--panel)}.update-diary-admin .update-diary-admin-section-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;padding:16px;border-bottom:1px solid var(--line-soft)}.update-diary-admin .update-diary-admin-section-head h2{margin:0;font-size:var(--font-size-lg)}.update-diary-admin .update-diary-admin-section-head>span{color:var(--text-muted);font-size:var(--font-size-sm)}.update-diary-admin .update-diary-admin-records{display:grid}.update-diary-admin .update-diary-admin-record{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:14px 16px;border-bottom:1px solid var(--line-soft)}.update-diary-admin .update-diary-admin-record:last-child{border-bottom:0}.update-diary-admin .update-diary-admin-editor{flex:1;min-width:0}.update-diary-admin .update-diary-admin-editor>summary{cursor:pointer;list-style:none}.update-diary-admin .update-diary-admin-editor>summary::-webkit-details-marker{display:none}.update-diary-admin .update-diary-admin-editor>summary:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}.update-diary-admin .update-diary-admin-record-main{display:grid;gap:5px;min-width:0}.update-diary-admin .update-diary-admin-record-meta{display:flex;align-items:center;flex-wrap:wrap;gap:8px;color:var(--text-muted);font-size:var(--font-size-xs)}.update-diary-admin .update-diary-record-status{display:inline-flex;align-items:center;padding:2px 6px;border-radius:999px;background:var(--brand-soft);color:var(--brand);font-size:var(--font-size-xs)}.update-diary-admin .update-diary-record-status-draft{background:var(--bg);color:var(--text-muted)}.update-diary-admin .update-diary-edit-hint{color:var(--brand)}.update-diary-admin .update-diary-admin-editor[open] .update-diary-edit-hint{display:none}.update-diary-admin .update-diary-edit-form{display:grid;gap:10px;margin-top:12px;padding-top:12px;border-top:1px solid var(--line-soft)}.update-diary-admin .update-diary-add-grid,.update-diary-admin .update-diary-edit-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.update-diary-admin .update-diary-edit-label{display:grid;gap:6px;color:var(--text-muted);font-size:var(--font-size-sm)}.update-diary-admin .update-diary-edit-label input,.update-diary-admin .update-diary-edit-label select{width:100%;box-sizing:border-box}.update-diary-admin .update-diary-edit-actions{display:flex;justify-content:flex-end}.update-diary-admin .update-diary-edit-actions button{margin:0;font-size:var(--font-size-sm)}.update-diary-admin .update-diary-delete-form{margin:0}.update-diary-admin .update-diary-delete-form button{margin:0;font-size:var(--font-size-sm)}.update-diary-admin .update-diary-admin-empty{padding:20px 16px;color:var(--text-muted);text-align:center}
@media (max-width:760px){.update-diary-sidebar .update-diary-records{max-height:min(560px,calc(100vh - 160px))}.update-diary-admin .update-diary-admin-section-head{align-items:flex-start;flex-direction:column}.update-diary-admin .update-diary-admin-record{flex-direction:column}.update-diary-admin .update-diary-add-grid,.update-diary-admin .update-diary-edit-grid{grid-template-columns:1fr}.update-diary-admin .update-diary-delete-form{align-self:flex-end}}
CSS;
}

function update_diary_js(): string
{
    return <<<'JS'
(function update_diary_init(){
function update_diary_refresh_height(details){
    var list=details.querySelector('[data-update-diary-records]');
    if(!list)return;
    if(!details.open){list.style.removeProperty('max-height');return;}
    var records=list.querySelectorAll('[data-update-diary-record]');
    var requestedCount=parseInt(list.getAttribute('data-update-diary-visible-count')||'10',10)||10;
    if(records.length<=requestedCount){list.style.removeProperty('max-height');return;}
    var height=0;
    var visibleCount=Math.min(Math.max(1,requestedCount),records.length);
    for(var index=0;index<visibleCount;index++)height+=Math.ceil(records[index].getBoundingClientRect().height);
    var viewportHeight=Math.max(320,document.documentElement.clientHeight||window.innerHeight||0);
    list.style.maxHeight=Math.min(height,Math.max(280,viewportHeight-180))+'px';
}
function update_diary_schedule_height(details){
    if(window.requestAnimationFrame)window.requestAnimationFrame(function update_diary_after_frame(){update_diary_refresh_height(details);});
    else window.setTimeout(function update_diary_after_timeout(){update_diary_refresh_height(details);},0);
}
function update_diary_bind_delete_confirmations(){
    document.querySelectorAll('[data-update-diary-confirm]').forEach(function update_diary_bind_delete_form(form){
        form.addEventListener('submit',function update_diary_confirm_delete(event){
            if(!window.confirm(form.getAttribute('data-update-diary-confirm')||'确定删除这条更新记录吗？'))event.preventDefault();
        });
    });
}
function update_diary_bind(){
    var detailsList=document.querySelectorAll('.update-diary-sidebar .update-diary-details');
    detailsList.forEach(function update_diary_bind_details(details){
        details.addEventListener('toggle',function update_diary_toggle(){update_diary_schedule_height(details);});
        update_diary_schedule_height(details);
    });
    window.addEventListener('resize',function update_diary_resize(){
        detailsList.forEach(function update_diary_resize_details(details){update_diary_schedule_height(details);});
    });
    update_diary_bind_delete_confirmations();
}
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',update_diary_bind);else update_diary_bind();
})();
JS;
}

return [
    'id' => 'update_diary',
    'name' => '更新日记',
    'version' => '1.1.0',
    'description' => '管理每日更新内容，并在边栏展示已发布的最近更新。',
    'author' => 'george',
    'assets' => ['css' => 'update_diary_css', 'js' => 'update_diary_js'],
    'hooks' => [
        'sidebar.stack' => 'update_diary_sidebar',
        'admin.tabs' => 'update_diary_admin_tabs',
    ],
    'admin_tabs' => ['update_diary' => 'update_diary_admin_page'],
    'install' => 'update_diary_install',
    'uninstall' => 'update_diary_uninstall',
];
