<script lang="ts">
import { onMount } from "svelte";
import { mergeConfig } from "@/admin/override-utils";
import Icon from "@/components/common/Icon.svelte";
import { renderMarkdownPreview } from "./markdown-preview";

type BuildState = {
	status: string;
	startedAt: string | null;
	finishedAt: string | null;
	lastBuiltAt: string | null;
	lastError: string;
	logs: string[];
};

type DashboardState = {
	posts: number;
	drafts: number;
	pages: number;
	assets: number;
	tags: number;
	categories: number;
	build: BuildState;
};

type PostSummary = {
	slug: string;
	title: string;
	description: string;
	draft: boolean;
	pinned: boolean;
};

type PageRecord = {
	slug: string;
	content: string;
	filePath: string;
};

type AssetRecord = {
	path: string;
	url: string;
	name: string;
	size: number;
	modifiedAt: string;
};

type SectionKey =
	| "site"
	| "navbar"
	| "sidebar"
	| "profile"
	| "background"
	| "announcement"
	| "footer"
	| "comment"
	| "font"
	| "friends"
	| "sponsor"
	| "ads"
	| "music"
	| "pio"
	| "license"
	| "cover"
	| "sakura";

let { initialConfig } = $props<{ initialConfig: Record<string, any> }>();
const baseConfig = structuredClone(initialConfig);

let session = $state({
	authenticated: false,
	configured: true,
	username: "",
});
let activeTab = $state("dashboard");
let notice = $state({ text: "", tone: "info" });
let loading = $state(false);
let saving = $state(false);
let dashboard = $state<DashboardState>({
	posts: 0,
	drafts: 0,
	pages: 0,
	assets: 0,
	tags: 0,
	categories: 0,
	build: {
		status: "idle",
		startedAt: null,
		finishedAt: null,
		lastBuiltAt: null,
		lastError: "",
		logs: [],
	},
});
let buildState = $state<BuildState>(dashboard.build);
let editors = $state<Record<SectionKey, string>>({
	site: "{}",
	navbar: "{}",
	sidebar: "{}",
	profile: "{}",
	background: "{}",
	announcement: "{}",
	footer: "{}",
	comment: "{}",
	font: "{}",
	friends: "{}",
	sponsor: "{}",
	ads: "{}",
	music: "{}",
	pio: "{}",
	license: "{}",
	cover: "{}",
	sakura: "{}",
});
let footerHtml = $state(initialConfig.footerHtml || "");
let loginForm = $state({ username: "", password: "" });
let pages = $state<PageRecord[]>([]);
let selectedPageSlug = $state("");
let pageContent = $state("");
let posts = $state<PostSummary[]>([]);
let selectedPostSlug = $state<string | null>(null);
let postSearch = $state("");
let postForm = $state(createBlankPost());
let postTagsText = $state("");
let assets = $state<AssetRecord[]>([]);
let uploadFolder = $state("uploads");
let uploadName = $state("");
let uploadDataUrl = $state("");

function createBlankPost() {
	return {
		slug: "",
		title: "",
		published: new Date().toISOString().slice(0, 10),
		updated: "",
		description: "",
		image: "",
		tags: [],
		category: "",
		draft: false,
		lang: "",
		pinned: false,
		author: "",
		sourceLink: "",
		licenseName: "",
		licenseUrl: "",
		comment: true,
		content: "",
		extension: "md",
	};
}

function setNotice(text: string, tone = "info") {
	notice = { text, tone };
}

function prettyJson(value: unknown) {
	return JSON.stringify(value, null, 2);
}

function slugify(value: string) {
	return value
		.normalize("NFKD")
		.toLowerCase()
		.trim()
		.replace(/[^\p{Letter}\p{Number}]+/gu, "-")
		.replace(/^-+|-+$/g, "");
}

function parseJson(text: string, label: string) {
	try {
		return JSON.parse(text || "{}");
	} catch (error) {
		throw new Error(`${label} JSON 格式错误: ${error instanceof Error ? error.message : "unknown error"}`);
	}
}

function fillEditors(config: Record<string, any>) {
	for (const key of Object.keys(editors) as SectionKey[]) {
		editors[key] = prettyJson(config[key] || {});
	}
}

function hydratePost(post: Record<string, any>) {
	postForm = { ...createBlankPost(), ...post };
	postTagsText = Array.isArray(post.tags) ? post.tags.join(", ") : "";
}

function selectPage(page: PageRecord) {
	selectedPageSlug = page.slug;
	pageContent = page.content;
}

const filteredPosts = $derived.by(() => {
	const keyword = postSearch.trim().toLowerCase();
	if (!keyword) {
		return posts;
	}
	return posts.filter((post) =>
		`${post.title} ${post.slug} ${post.description}`.toLowerCase().includes(keyword),
	);
});

const pagePreview = $derived(renderMarkdownPreview(pageContent));
const postPreview = $derived(renderMarkdownPreview(postForm.content));

async function api<T>(url: string, options: RequestInit = {}): Promise<T> {
	const response = await fetch(url, {
		...options,
		headers: {
			"Content-Type": "application/json",
			...(options.headers || {}),
		},
	});
	const text = await response.text();
	const data = text ? JSON.parse(text) : {};
	if (!response.ok) {
		throw new Error(data.error || "Request failed");
	}
	return data as T;
}

async function refreshSession() {
	session = await api("/api/admin/session");
}

async function loadData() {
	loading = true;
	try {
		const [overrideData, dashboardData, pagesData, postsData, assetsData, currentBuild] =
			await Promise.all([
				api<{ overrides: Record<string, any>; footerHtml: string }>("/api/admin/overrides"),
				api<DashboardState>("/api/admin/dashboard"),
				api<PageRecord[]>("/api/admin/pages"),
				api<PostSummary[]>("/api/admin/posts"),
				api<AssetRecord[]>("/api/admin/assets"),
				api<BuildState>("/api/admin/build"),
			]);
		const mergedConfig = mergeConfig(structuredClone(baseConfig), overrideData.overrides || {});
		fillEditors(mergedConfig);
		footerHtml = overrideData.footerHtml || baseConfig.footerHtml || "";
		dashboard = dashboardData;
		buildState = currentBuild;
		pages = pagesData;
		posts = postsData;
		assets = assetsData;
		if (!selectedPageSlug && pages.length > 0) {
			selectPage(pages[0]);
		}
	} finally {
		loading = false;
	}
}

async function login() {
	try {
		loading = true;
		await api("/api/admin/login", {
			method: "POST",
			body: JSON.stringify(loginForm),
		});
		await refreshSession();
		await loadData();
		setNotice("登录成功", "success");
	} catch (error) {
		setNotice(error instanceof Error ? error.message : "登录失败", "error");
	} finally {
		loading = false;
		loginForm.password = "";
	}
}

async function logout() {
		await api("/api/admin/logout", { method: "POST" });
		session = { authenticated: false, configured: session.configured, username: "" };
		setNotice("已退出登录", "info");
}

async function saveSection(key: SectionKey, label: string) {
	try {
		saving = true;
		await api(`/api/admin/config/${key}`, {
			method: "PUT",
			body: JSON.stringify(parseJson(editors[key], label)),
		});
		setNotice(`${label}已保存，重建后生效`, "success");
	} catch (error) {
		setNotice(error instanceof Error ? error.message : "保存失败", "error");
	} finally {
		saving = false;
	}
}

async function saveFooter() {
	try {
		saving = true;
		await Promise.all([
			api("/api/admin/config/footer", {
				method: "PUT",
				body: editors.footer,
			}),
			api("/api/admin/footer-html", {
				method: "PUT",
				body: JSON.stringify({ content: footerHtml }),
			}),
		]);
		setNotice("页脚已保存，重建后生效", "success");
	} catch (error) {
		setNotice(error instanceof Error ? error.message : "页脚保存失败", "error");
	} finally {
		saving = false;
	}
}

async function savePage(publish = false) {
	if (!selectedPageSlug) {
		setNotice("请先选择页面", "error");
		return;
	}
	try {
		saving = true;
		const response = await api<{ build: BuildState }>(
			`/api/admin/pages/${encodeURIComponent(selectedPageSlug)}`,
			{
				method: "PUT",
				body: JSON.stringify({ content: pageContent, publish }),
			},
		);
		pages = pages.map((item) =>
			item.slug === selectedPageSlug ? { ...item, content: pageContent } : item,
		);
		if (publish) {
			buildState = response.build;
		}
		await api("/api/admin/dashboard").then((data) => {
			dashboard = data as DashboardState;
		});
		setNotice(publish ? "页面已保存并发布" : "页面已保存", "success");
	} catch (error) {
		setNotice(error instanceof Error ? error.message : "页面保存失败", "error");
	} finally {
		saving = false;
	}
}

async function openPost(slug: string) {
	const post = await api<Record<string, any>>(`/api/admin/posts/${encodeURIComponent(slug)}`);
	selectedPostSlug = slug;
	hydratePost(post);
}

async function savePost(publish = false) {
	try {
		saving = true;
		postForm.tags = postTagsText.split(",").map((item) => item.trim()).filter(Boolean);
		const route = selectedPostSlug
			? `/api/admin/posts/${encodeURIComponent(selectedPostSlug)}`
			: "/api/admin/posts";
		const method = selectedPostSlug ? "PUT" : "POST";
		const response = await api<{ post: Record<string, any>; build: BuildState }>(route, {
			method,
			body: JSON.stringify({ ...postForm, publish }),
		});
		selectedPostSlug = response.post.slug;
		hydratePost(response.post);
		posts = await api("/api/admin/posts");
		if (publish) {
			buildState = response.build;
		}
		dashboard = await api("/api/admin/dashboard");
		setNotice(publish ? "文章已保存并发布" : "文章已保存", "success");
	} catch (error) {
		setNotice(error instanceof Error ? error.message : "文章保存失败", "error");
	} finally {
		saving = false;
	}
}

async function deletePost() {
	if (!selectedPostSlug || !window.confirm(`确认删除 ${selectedPostSlug} 吗？`)) {
		return;
	}
	try {
		saving = true;
		await api(`/api/admin/posts/${encodeURIComponent(selectedPostSlug)}`, { method: "DELETE" });
		selectedPostSlug = null;
		hydratePost(createBlankPost());
		posts = await api("/api/admin/posts");
		dashboard = await api("/api/admin/dashboard");
		setNotice("文章已删除", "success");
	} catch (error) {
		setNotice(error instanceof Error ? error.message : "文章删除失败", "error");
	} finally {
		saving = false;
	}
}

function chooseFile(event: Event) {
	const input = event.currentTarget as HTMLInputElement;
	const file = input.files?.[0];
	uploadName = file?.name || "";
	if (!file) {
		uploadDataUrl = "";
		return;
	}
	const reader = new FileReader();
	reader.onload = () => {
		uploadDataUrl = String(reader.result || "");
	};
	reader.readAsDataURL(file);
}

async function uploadAsset() {
	if (!uploadName || !uploadDataUrl) {
		setNotice("请先选择文件", "error");
		return;
	}
	try {
		saving = true;
		await api("/api/admin/assets", {
			method: "POST",
			body: JSON.stringify({ name: uploadName, folder: uploadFolder, dataUrl: uploadDataUrl }),
		});
		assets = await api("/api/admin/assets");
		dashboard = await api("/api/admin/dashboard");
		setNotice("资源上传成功", "success");
		uploadName = "";
		uploadDataUrl = "";
	} catch (error) {
		setNotice(error instanceof Error ? error.message : "资源上传失败", "error");
	} finally {
		saving = false;
	}
}

async function rebuildSite() {
	const response = await api<{ state: BuildState }>("/api/admin/rebuild", { method: "POST" });
	buildState = response.state;
	setNotice("已触发站点重建", "success");
}

onMount(() => {
	const timer = window.setInterval(async () => {
		if (!session.authenticated) {
			return;
		}
		try {
			buildState = await api("/api/admin/build");
		} catch {}
	}, 3000);

	(async () => {
		await refreshSession();
		if (session.authenticated) {
			await loadData();
		}
	})();

	return () => window.clearInterval(timer);
});
</script>

<div class="admin-shell">
	{#if notice.text}
		<div class={`admin-notice ${notice.tone}`}>{notice.text}</div>
	{/if}

	{#if !session.configured}
		<div class="card-base admin-card">
			<h2 class="admin-title">请先设置 Space 环境变量</h2>
			<p class="text-75">需要配置 `ADMIN` 和 `PASSWORD` 才能启用后台登录。</p>
		</div>
	{:else if !session.authenticated}
		<div class="card-base admin-card admin-login">
			<h2 class="admin-title">后台登录</h2>
			<input class="admin-input" placeholder="管理员账号" bind:value={loginForm.username} />
			<input class="admin-input" type="password" placeholder="密码" bind:value={loginForm.password} />
			<button class="btn-regular admin-button" onclick={login} disabled={loading}>登录</button>
		</div>
	{:else}
		<div class="admin-toolbar card-base">
			<div class="admin-tabs">
				{#each [
					["dashboard", "控制台"],
					["config", "配置"],
					["content", "内容"],
					["media", "资源"],
					["advanced", "高级"],
				] as [id, label]}
					<button class:active={activeTab === id} class="btn-card admin-tab" onclick={() => (activeTab = id)}>{label}</button>
				{/each}
			</div>
			<div class="admin-actions">
				<button class="btn-card admin-button secondary" onclick={loadData}>刷新</button>
				<button class="btn-regular admin-button" onclick={rebuildSite}>重建站点</button>
				<button class="btn-card admin-button secondary" onclick={logout}>退出</button>
			</div>
		</div>

		{#if activeTab === "dashboard"}
			<div class="admin-grid">
				{#each [
					["文章", dashboard.posts],
					["草稿", dashboard.drafts],
					["页面", dashboard.pages],
					["图片", dashboard.assets],
					["标签", dashboard.tags],
					["分类", dashboard.categories],
				] as [label, value]}
					<div class="card-base admin-card metric">
						<span>{label}</span>
						<strong>{value}</strong>
					</div>
				{/each}
			</div>
			<div class="card-base admin-card">
				<h3 class="admin-title">构建状态：{buildState.status}</h3>
				<p class="text-75">最近成功构建：{buildState.lastBuiltAt || "尚未构建"}</p>
				<p class="text-75">最近错误：{buildState.lastError || "无"}</p>
				<pre class="admin-code build-log">{buildState.logs.join("\n") || "这里会显示构建日志。"}</pre>
			</div>
		{/if}

		{#if activeTab === "config"}
			<div class="admin-stack">
				{#each [
					["site", "站点配置"],
					["profile", "个人资料"],
					["announcement", "公告配置"],
					["navbar", "导航配置"],
					["sidebar", "侧栏配置"],
					["background", "壁纸配置"],
				] as [key, label]}
					<div class="card-base admin-card">
						<div class="row">
							<h3 class="admin-title">{label}</h3>
							<button class="btn-regular admin-button" onclick={() => saveSection(key as SectionKey, label)} disabled={saving}>保存</button>
						</div>
						<textarea class="admin-code section-editor" rows="14" bind:value={editors[key as SectionKey]}></textarea>
					</div>
				{/each}
				<div class="card-base admin-card">
					<div class="row">
						<h3 class="admin-title">页脚 HTML</h3>
						<button class="btn-regular admin-button" onclick={saveFooter} disabled={saving}>保存页脚</button>
					</div>
					<textarea class="admin-code section-editor" rows="10" bind:value={footerHtml}></textarea>
				</div>
			</div>
		{/if}

		{#if activeTab === "content"}
			<div class="card-base admin-card">
				<div class="row">
					<h3 class="admin-title">独立页面</h3>
					<div class="admin-actions">
						<button class="btn-card admin-button secondary" onclick={() => savePage(false)} disabled={saving}>保存</button>
						<button class="btn-regular admin-button" onclick={() => savePage(true)} disabled={saving}>保存并发布</button>
					</div>
				</div>
				<div class="editor-layout">
					<div class="editor-list">
						{#each pages as page}
							<button class:active={selectedPageSlug === page.slug} class="btn-card editor-item" onclick={() => selectPage(page)}>{page.slug}</button>
						{/each}
					</div>
					<textarea class="admin-code editor-input" bind:value={pageContent}></textarea>
					<div class="editor-preview custom-md prose dark:prose-invert max-w-none">
						{@html pagePreview}
					</div>
				</div>
			</div>

			<div class="card-base admin-card">
				<div class="row">
					<h3 class="admin-title">文章编辑器</h3>
					<div class="admin-actions">
						<button class="btn-card admin-button secondary" onclick={() => { selectedPostSlug = null; hydratePost(createBlankPost()); }}>新建</button>
						<button class="btn-card admin-button secondary" onclick={() => savePost(false)} disabled={saving}>保存</button>
						<button class="btn-regular admin-button" onclick={() => savePost(true)} disabled={saving}>保存并发布</button>
						<button class="btn-card admin-button secondary" onclick={deletePost} disabled={!selectedPostSlug || saving}>删除</button>
					</div>
				</div>
				<div class="editor-layout posts">
					<div class="editor-list">
						<input class="admin-input" placeholder="搜索文章" bind:value={postSearch} />
						{#each filteredPosts as post}
							<button class:active={selectedPostSlug === post.slug} class="btn-card editor-item post-item" onclick={() => openPost(post.slug)}>
								<span>{post.title || post.slug}</span>
								<small>{post.slug}</small>
							</button>
						{/each}
					</div>
					<div class="post-form">
						<div class="post-meta-grid">
							<input class="admin-input" placeholder="标题" bind:value={postForm.title} />
							<div class="slug-row">
								<input class="admin-input" placeholder="slug" bind:value={postForm.slug} />
								<button class="btn-card admin-button secondary" onclick={() => (postForm.slug = slugify(postForm.title || postForm.slug))}>生成</button>
							</div>
							<input class="admin-input" placeholder="发布日期 YYYY-MM-DD" bind:value={postForm.published} />
							<input class="admin-input" placeholder="更新日期 YYYY-MM-DD" bind:value={postForm.updated} />
							<input class="admin-input" placeholder="封面地址" bind:value={postForm.image} />
							<input class="admin-input" placeholder="分类" bind:value={postForm.category} />
							<input class="admin-input" placeholder="标签，逗号分隔" bind:value={postTagsText} />
							<input class="admin-input" placeholder="语言" bind:value={postForm.lang} />
							<textarea class="admin-code" rows="3" placeholder="描述" bind:value={postForm.description}></textarea>
						</div>
						<div class="check-row">
							<label><input type="checkbox" bind:checked={postForm.draft} /> 草稿</label>
							<label><input type="checkbox" bind:checked={postForm.pinned} /> 置顶</label>
							<label><input type="checkbox" bind:checked={postForm.comment} /> 评论</label>
						</div>
						<textarea class="admin-code editor-input" bind:value={postForm.content}></textarea>
					</div>
					<div class="editor-preview custom-md prose dark:prose-invert max-w-none">
						{@html postPreview}
					</div>
				</div>
			</div>
		{/if}

		{#if activeTab === "media"}
			<div class="card-base admin-card">
				<div class="row">
					<h3 class="admin-title">图片与静态资源</h3>
					<button class="btn-regular admin-button" onclick={uploadAsset} disabled={saving}>上传</button>
				</div>
				<div class="upload-row">
					<input class="admin-input" placeholder="上传目录，如 uploads/posts" bind:value={uploadFolder} />
					<input class="admin-input" type="file" onchange={chooseFile} />
				</div>
				<div class="asset-grid">
					{#each assets as asset}
						<div class="btn-card asset-item">
							{#if asset.url.match(/\.(png|jpe?g|webp|gif|svg)$/i)}
								<img src={asset.url} alt={asset.name} />
							{/if}
							<strong>{asset.name}</strong>
							<small>{asset.url}</small>
						</div>
					{/each}
				</div>
			</div>
		{/if}

		{#if activeTab === "advanced"}
			<div class="admin-stack">
				{#each [
					["comment", "评论系统"],
					["font", "字体"],
					["friends", "友链"],
					["sponsor", "赞助"],
					["ads", "广告"],
					["music", "音乐播放器"],
					["pio", "看板娘"],
					["license", "许可证"],
					["cover", "封面图配置"],
					["sakura", "樱花特效"],
				] as [key, label]}
					<div class="card-base admin-card">
						<div class="row">
							<h3 class="admin-title">{label}</h3>
							<button class="btn-regular admin-button" onclick={() => saveSection(key as SectionKey, label)} disabled={saving}>保存</button>
						</div>
						<textarea class="admin-code section-editor" rows="14" bind:value={editors[key as SectionKey]}></textarea>
					</div>
				{/each}
			</div>
		{/if}
	{/if}
</div>

<style>
.admin-shell, .admin-stack { display:flex; flex-direction:column; gap:1rem; }
.admin-card { padding:1.25rem; }
.admin-title { margin:0; font-size:1.2rem; font-weight:700; }
.admin-login { max-width:28rem; margin:0 auto; }
.admin-toolbar, .row, .admin-actions, .admin-tabs, .upload-row, .slug-row { display:flex; gap:.75rem; align-items:center; }
.admin-toolbar, .row { justify-content:space-between; }
.admin-tabs { flex-wrap:wrap; }
.admin-button { padding:.7rem 1rem; border-radius:.9rem; }
.admin-button.secondary { background:var(--btn-card-bg-hover); }
.admin-tab.active { background:var(--btn-regular-bg-hover); color:var(--primary); }
.admin-grid { display:grid; gap:.75rem; grid-template-columns:repeat(6,minmax(0,1fr)); }
.metric { display:flex; flex-direction:column; gap:.4rem; }
.metric strong { font-size:1.8rem; }
.admin-input, .admin-code { width:100%; border:1px solid var(--line-divider); background:color-mix(in oklch, var(--card-bg) 90%, var(--btn-card-bg-hover) 10%); border-radius:.9rem; padding:.8rem .95rem; color:inherit; }
.admin-code { resize:vertical; font-family:"JetBrains Mono Variable",ui-monospace,Consolas,monospace; font-size:.85rem; line-height:1.6; }
.section-editor { min-height:18rem; }
.admin-notice { padding:.85rem 1rem; border-radius:1rem; border:1px solid var(--line-divider); }
.admin-notice.success { background:color-mix(in oklch, var(--primary) 14%, var(--card-bg) 86%); }
.admin-notice.error { background:color-mix(in oklch, oklch(0.68 0.18 25) 14%, var(--card-bg) 86%); }
.build-log { max-height:18rem; overflow:auto; }
.editor-layout { display:grid; gap:1rem; grid-template-columns:14rem minmax(0,1fr) minmax(0,1fr); }
.editor-layout.posts { grid-template-columns:16rem minmax(0,1.1fr) minmax(0,.9fr); }
.editor-list { display:flex; flex-direction:column; gap:.6rem; }
.editor-item { justify-content:flex-start; text-align:left; padding:.8rem .95rem; border-radius:.9rem; }
.editor-item.active { background:var(--btn-regular-bg-hover); }
.post-item { display:flex; flex-direction:column; align-items:flex-start; }
.editor-input { min-height:26rem; }
.editor-preview { min-height:26rem; overflow:auto; padding:1rem; border-radius:1rem; border:1px solid var(--line-divider); background:color-mix(in oklch, var(--card-bg) 92%, var(--btn-card-bg-hover) 8%); }
.post-form { display:flex; flex-direction:column; gap:.75rem; }
.post-meta-grid { display:grid; gap:.75rem; grid-template-columns:repeat(2,minmax(0,1fr)); }
.check-row { display:flex; gap:1rem; flex-wrap:wrap; }
.asset-grid { display:grid; gap:.75rem; grid-template-columns:repeat(3,minmax(0,1fr)); margin-top:1rem; }
.asset-item { flex-direction:column; align-items:flex-start; padding:.8rem; border-radius:1rem; gap:.45rem; }
.asset-item img { width:100%; aspect-ratio:16/10; object-fit:cover; border-radius:.8rem; }
.text-75 { color:rgba(0,0,0,.75); }
.dark .text-75 { color:rgba(255,255,255,.75); }
@media (max-width: 1100px) {
	.admin-grid, .asset-grid, .post-meta-grid, .editor-layout, .editor-layout.posts { grid-template-columns:1fr 1fr; }
	.editor-layout, .editor-layout.posts { grid-template-columns:1fr; }
}
@media (max-width: 720px) {
	.admin-grid, .asset-grid, .post-meta-grid { grid-template-columns:1fr; }
	.admin-toolbar, .row, .admin-actions, .upload-row, .slug-row { flex-direction:column; align-items:stretch; }
}
</style>
