'use client';

import React, { createContext, useCallback, useContext, useEffect, useState, ReactNode } from 'react';

type Lang = 'zh' | 'en';

type TranslationKey = string;

const DICT: Record<'zh' | 'en', Record<string, string>> = {
  zh: {
    // ── nav
    'Memory': '记忆',
    'Recall': '召回',
    'Drilldown': '下钻',
    'Cleanup': '清理',
    'Dream': '梦境',
    'Settings': '设置',

    // ── common
    'Run': '运行',
    'Running…': '运行中…',
    'Save': '保存',
    'Saving…': '保存中…',
    'Cancel': '取消',
    'Edit': '编辑',
    'Delete': '删除',
    'Refresh': '刷新',
    'Loading…': '加载中…',
    'Reset': '重置',
    'Apply': '应用',
    'Close': '关闭',
    'Collapse': '收起',
    'Expand': '展开',
    'Confirm': '确认',
    'Open': '打开',
    'Back': '返回',
    'Create': '创建',
    'Creating…': '创建中…',
    'New': '新建',
    'Move': '移动',
    'Moving…': '移动中…',
    'Rebuild': '重建',
    'Title': '标题',
    'Actions': '操作',
    'Read': '读取',
    'Max': '上限',
    'Top': '最高',
    'Difference': '差值',
    'Suggested': '建议',
    'Ready': '就绪',
    'Insufficient': '不足',
    'Unused p75': '未采用样本 p75',
    'None': '无',
    'Save failed': '保存失败',
    'Create failed': '创建失败',
    'Move failed': '移动失败',
    'Delete failed': '删除失败',
    'Status': '状态',
    'Summary': '摘要',
    'Not found': '未找到',
    'View': '视图',
    'Connecting…': '连接中…',
    'Continue': '继续',
    'Skip': '跳过',
    'Try Again': '重试',
    'Unable to connect': '连接失败',
    'Check that the backend service is running.': '请确认后端服务已启动。',
    'Created': '已创建',
    'Updated': '已更新',
    'Unchanged': '未变更',
    'Complete': '完成',
    'Content': '内容',
    'Remaining': '剩余',
    'Available': '可用',
    'Manual only': '仅手动填写',
    'Refreshing…': '刷新中…',
    'Generating…': '生成中…',
    'Rebuilding…': '重建中…',
    'Rebuild completed': '重建完成',
    'Restore': '恢复',
    'Current step': '当前步骤',
    'Setup progress': '初始化进度',
    'Step': '步骤',
    'Runtime not ready': '运行时未就绪',
    'Rebuild Index': '重建索引',
    '1 unsaved change': '1 项未保存变更',
    'unsaved changes': '项未保存变更',
    'Test connection': '测试联通',
    'Testing…': '测试中…',
    'Embedding connection OK': 'Embedding 联通正常',
    'OK': '成功',
    'Fail': '失败',
    'View LLM connection OK': 'View LLM 联通正常',
    'Changing the embedding model will invalidate all existing embeddings and trigger a full rebuild. Continue?':
      '修改 Embedding 模型会让现有向量全部失效，并触发一次完整重建。是否继续？',

    // ── auth
    'Memory management console': '记忆管理控制台',
    'Lore memory console': 'Lore 记忆控制台',
    'API Token': 'API 令牌',
    'Enter your token': '输入您的令牌',
    'Invalid token': '令牌无效',
    'Failed': '失败',
    'Remove': '移除',
    'Admin': '管理',
    'Unknown time': '时间未知',
    'Unknown source': '来源未知',
    '1 update': '1 次更新',
    'updates': '次更新',
    'Updater': '更新来源',
    'Updaters': '更新来源',
    'No textual changes.': '文本没有变化。',
    'Pending': '等待中',
    'Rule': '规则',
    'true': '是',
    'false': '否',
    'range': '范围',
    'Reset failed': '重置失败',
    'Rebuild failed': '重建失败',
    'Reset completed': '重置完成',
    'Changes saved': '已保存更改',
    'Default saved': '已保存默认值',
    'Debug request failed': '调试请求失败',

    // ── setup
    'Setup required': '需要初始化',
    'First-run setup': '首次初始化',
    'Lore will guide you through the next required step automatically.': 'Lore 会自动带你进入当前还未完成的下一步。',
    'Next step': '下一步',
    'Required fields missing': '必填内容未填写',
    'Fill every field on this page before continuing.': '请先填完当前页面的必填内容再继续。',
    'Embedding setup': 'Embedding 配置',
    'Configure the vector endpoint Lore uses for embeddings before continuing.': '先配置 Lore 用于向量化的 Embedding 服务，再继续后续步骤。',
    'Configure the vector endpoint Lore uses for embeddings before continuing. Example: http://127.0.0.1:8090/v1':
      '先配置 Lore 用于向量化的 Embedding 服务，再继续后续步骤。示例：http://127.0.0.1:8090/v1',
    'View LLM setup': 'View LLM 配置',
    'Configure the model Lore uses for view refinement and dream workflows.': '配置 Lore 用于视图精炼和 dream 流程的模型。',
    'Configure the model Lore uses for view refinement and dream workflows. Example: http://127.0.0.1:8090/v1':
      '配置 Lore 用于视图精炼和 dream 流程的模型。示例：http://127.0.0.1:8090/v1',
    'Agent boot memory': 'Agent 启动记忆',
    'Write the fixed workflow-constraints node that Lore always loads at startup.': '填写 Lore 每次启动都会固定加载的工作规则节点。',
    'Use this as the shared AGENTS.md / CLAUDE.md-style working rules: repository instructions, collaboration boundaries, safety checks, coding workflow, and verification expectations that every Lore agent loads at startup.':
      '这里适合放类似 AGENTS.md / CLAUDE.md 的通用工作规则：仓库指令、协作边界、安全检查、编码流程和验证要求；每个 Lore agent 启动时都会加载。',
    'Soul boot memory': 'Soul 启动记忆',
    'Write the fixed persona baseline that Lore carries into every session.': '填写 Lore 每次会话都会带上的人格基线节点。',
    'User boot memory': 'User 启动记忆',
    'Write the stable user profile Lore should remember across future sessions.': '填写 Lore 未来会话中都应记住的稳定用户画像。',
    'Claude Code boot memory': 'Claude Code 启动记忆',
    'Write the Claude Code-specific agent rules that load together with core://agent.': '填写会与 core://agent 一起加载的 Claude Code 专属 agent 规则。',
    'OpenClaw boot memory': 'OpenClaw 启动记忆',
    'Write the OpenClaw-specific agent rules that load together with core://agent.': '填写会与 core://agent 一起加载的 OpenClaw 专属 agent 规则。',
    'Hermes boot memory': 'Hermes 启动记忆',
    'Write the Hermes-specific agent rules that load together with core://agent.': '填写会与 core://agent 一起加载的 Hermes 专属 agent 规则。',
    'Codex boot memory': 'Codex 启动记忆',
    'Write the Codex-specific agent rules that load together with core://agent.': '填写会与 core://agent 一起加载的 Codex 专属 agent 规则。',
    'Pi boot memory': 'Pi 启动记忆',
    'Write the Pi-specific agent rules that load together with core://agent.': '填写会与 core://agent 一起加载的 Pi 专属 agent 规则。',
    'Channel agent setup': 'Channel agent 配置',
    'Review every supported channel in one page. Each channel keeps only its runtime-specific delta; shared rules stay in core://agent.':
      '在同一个页面检查所有支持的 channel。每个 channel 只保留运行时专属差异；通用规则放在 core://agent。',
    'No channel agent boot nodes found.': '未找到 channel agent 启动节点。',
    'Lore needs first-run setup before you can enter the normal workspace.':
      '进入正常工作区之前，需要先完成 Lore 首次初始化。',
    'Refresh status': '刷新状态',
    'Open settings': '打开设置',
    'Generate all drafts': '全部生成草稿',
    'Save all': '全部保存',
    'Startup nodes loaded': '已加载启动节点',
    'Draft model': '草稿模型',
    'Fixed boot baseline': '固定启动基线',
    'These global boot paths load at startup for every Lore instance.':
      '这些全局 boot 路径会在每个 Lore 实例启动时加载。',
    'You can write these manually, or use the configured View LLM to draft a first pass and then edit it before saving.':
      '你可以手动填写，也可以用已配置的 View LLM 先生成初稿，再编辑后保存。',
    'Shared draft context': '共享草稿上下文',
    'Optional shared context for all boot draft generations': '给所有 boot 草稿生成共享的可选上下文',
    'Draft generation unavailable': '草稿生成不可用',
    'You can still complete setup manually, or open Settings first and configure the default View LLM.':
      '你仍然可以手动完成初始化，或者先打开设置配置默认 View LLM。',
    'Embedding is configured, but runtime calls still need the LORE_EMBEDDING_API_KEY environment variable before vectors can run.':
      'Embedding 已完成配置，但真正运行前仍需要设置 LORE_EMBEDDING_API_KEY 环境变量。',
    'View LLM settings are saved, but runtime access is still unavailable. Draft generation and dream workflows will stay disabled until the runtime is ready.':
      'View LLM 设置已保存，但运行时仍不可用。在运行时就绪前，草稿生成和 dream 流程会继续保持禁用。',
    'View LLM settings are incomplete. Draft generation and dream workflows stay disabled until base URL, API key, and model are all configured in Settings.':
      'View LLM 设置尚未完整。只有在 Settings 中同时配置 base URL、API key 和 model 后，草稿生成与 dream 流程才会启用。',
    'Missing': '缺失',
    'Empty content': '内容为空',
    'Initialized': '已初始化',
    'Content length': '内容长度',
    'Draft prompt': '草稿提示',
    'Optional extra guidance for this node': '给这个节点补充可选提示',
    'Write the final memory content here': '在这里填写最终要保存的记忆内容',
    'Draft generated': '草稿已生成',
    'Generate draft': '生成草稿',
    'Agent boot': 'Agent 启动',
    'Soul boot': 'Soul 启动',
    'User boot': 'User 启动',
    'workflow constraints': '工作规则 / 协作约束',
    'claude code runtime constraints': 'Claude Code 运行时约束',
    'openclaw runtime constraints': 'OpenClaw 运行时约束',
    'hermes runtime constraints': 'Hermes 运行时约束',
    'codex runtime constraints': 'Codex 运行时约束',
    'pi runtime constraints': 'Pi 运行时约束',
    'style / persona / self-definition': '风格 / 人格 / 自我定义',
    'stable user definition': '稳定用户定义',
    'Working rules, collaboration constraints, and execution protocol.':
      '工作规则、协作约束与执行协议。',
    'Claude Code-specific tools, session hooks, and runtime workflow constraints.':
      'Claude Code 专属工具、会话 hook 与运行时工作流约束。',
    'OpenClaw-specific tools, plugin behavior, and runtime workflow constraints.':
      'OpenClaw 专属工具、插件行为与运行时工作流约束。',
    'Hermes-specific memory-provider behavior, tools, and runtime workflow constraints.':
      'Hermes 专属 memory provider 行为、工具与运行时工作流约束。',
    'Codex-specific plugins, hooks, MCP behavior, and runtime workflow constraints.':
      'Codex 专属插件、hook、MCP 行为与运行时工作流约束。',
    'Pi-specific extensions, tools, prompt injection, and runtime workflow constraints.':
      'Pi 专属扩展、工具、prompt 注入与运行时工作流约束。',
    'Agent style, persona, and self-cognition baseline.':
      '代理的风格、人格与自我认知基线。',
    'Stable user information, user preferences, and durable collaboration context.':
      '稳定用户信息、用户偏好与可长期复用的协作上下文。',

    // ── recall workbench
    'Workbench': '工作台',
    'Inspect every stage of the retrieval pipeline — from raw path hits through merged ranking to prompt injection.':
      '检视召回管线的每一层 —— 从路径命中到融合排序，再到注入 prompt。',
    'Ask the archive…': '向档案发问…',
    'Exclude boot': '排除 boot',
    'More options': '更多选项',
    'Hide options': '收起选项',
    'Session': '会话',
    'Limit': '上限',
    'Min score': '最小分',
    'Max shown': '最多展示',
    'Threshold': '展示阈值',
    'Precision': '精度',
    'Runtime': '运行时',
    'Configuration at time of query': '查询时的实时配置',
    'Services': '服务',
    'Weights': '权重',
    'Run a query to inspect each stage of retrieval.': '运行一次查询以检视召回各阶段。',
    'Query tokens': '查询 token 数',
    'Embedding': '嵌入模型',
    'View LLM': '视图 LLM',
    'Boot URIs': '启动 URI 数',
    'soft': '宽松',
    'hard': '严格',

    // ── recall stages
    'Withheld': '未展示',
    'Exact': '精确',
    'Semantic': '语义',
    'Lexical': '词法',
    'Merged': '融合',
    'Candidate': '候选',
    'Final': '最终分',
    'Breakdown': '分解',
    'Quoted': '引用',
    'Raw': '原始分',
    'Weight': '权重',
    'Weighted': '加权',
    'Cues': '线索',
    'Exact sources': '精确来源',
    'Glossary sources': '术语来源',
    'Semantic sources': '语义来源',
    'Lexical sources': '词法来源',
    'No matching source records.': '无匹配来源记录。',
    'No data yet.': '暂无数据。',
    'No exact hits.': '无精确命中。',
    'No glossary hits.': '无术语命中。',
    'No semantic hits.': '无语义命中。',
    'No lexical hits.': '无词法命中。',
    'No merged candidates.': '无融合候选。',
    'Nothing to show.': '无展示内容。',
    'Path exact match': '路径完全命中',
    'Glossary exact match': '术语完全命中',
    'Query contains glossary term': '查询包含术语',
    'Glossary text match': '术语文本命中',
    'Glossary FTS match': '术语 FTS 命中',
    'Keyword': '关键词',
    'LLM refined': 'LLM 精炼',
    'Rule-based': '规则匹配',
    'Separation': '分离',
    'Session label': '会话',
    'Threshold label': '阈值',
    'Max label': '上限',
    'Top label': '最高',
    'FTS': 'FTS',
    'Text': '文本',
    'URI': 'URI',
    'Lex': '词法',
    'Gist': '摘要',
    'Question': '问题',
    'Unknown view': '未知视图',
    'Suppressed by boot': 'boot',
    'Suppressed by score': 'score',
    'hits': '命中',
    'candidates': '候选',
    'Suppressed': '抑制',
    'Recall block · prompt injection': '召回注入块',

    // ── drilldown
    'Analytics': '分析',
    'Events': '事件',
    'Shown': '展示',
    'Queries': '查询',
    'Used': '采用',
    'Days': '天数',
    'days': '天',
    'Query text': '查询文本',
    'Node URI': '节点 URI',
    'Fragment…': '片段…',
    'uri…': 'uri…',
    'Hide filters': '收起筛选',
    'Show filters': '展开筛选',
    'Adjust filters': '调整筛选',
    'Reset filters': '重置筛选',
    'Recent queries': '最近查询',
    'No queries recorded yet.': '尚无查询记录。',
    'Query': '查询',
    'When': '时间',
    'Entry': '条目',
    'Path': '路径',
    'Score': '分数',
    'Avg': '均值',
    'Source': '来源',
    'All sources': '全部来源',
    'Previous': '上一页',
    'Next': '下一页',
    'Showing range': '显示',
    'of': '共',
    'Failed to load': '加载失败',
    'No path statistics.': '暂无路径统计。',
    'No view statistics.': '暂无视图统计。',
    'No noisy nodes.': '暂无噪声节点。',
    'No queries for this node.': '该节点暂无查询。',
    'Legacy': '旧版',
    'Separation gap': '分离差',
    'Used p25 minus unused shown p75': '采用样本 p25 减去未采用展示样本 p75',
    'Display threshold analysis': '展示阈值分析',
    'Overview': '概览',
    'Memory created': '新增记忆',
    'Memory updated': '更新记忆',
    'Memory deleted': '删除记忆',
    'Insufficient data': '数据不足',
    'Used candidates': '采用候选',
    'Unused shown': '未采用的展示候选',
    'Unused shown p75': '未采用展示样本 p75',
    'Used p25': '采用样本 p25',
    'Used median': '采用样本中位数',
    'By source': '按来源',
    'No source-specific threshold samples yet.': '暂无按来源划分的阈值样本。',
    'No source overview data yet.': '暂无按来源划分的概览数据。',

    // ── memory
    'Archive': '档案',
    'Domains': '域',
    'Current': '当前',
    'Disclosure: ': '披露：',
    'Also:': '别名：',
    'Return to root': '返回根目录',
    'This folder is empty.': '此目录为空。',
    'Clusters': '聚类',
    'Children': '子项',
    'Empty': '空',
    'Editing': '编辑中',
    'Priority': '优先级',
    'Disclosure': '披露',
    'When should this memory be recalled?': '何时召回这条记忆？',
    'Glossary': '术语',
    'keyword': '关键词',
    'Add': '添加',
    'Switch to light': '切换到亮色',
    'Switch to dark': '切换到暗色',
    'Enable Aurora Background': '打开流光背景',
    'Disable Aurora Background': '关闭流光背景',
    'Tree': '目录',
    'Hide tree': '收起目录',
    'Agent memory graph': '代理记忆图谱',
    'Retrieval views': '检索视图',
    'New Child Node': '新建子节点',
    'Memory content…': '记忆内容…',
    'Will create': '将创建',
    'Path segment': '路径片段',
    'Move / Rename': '移动 / 重命名',
    'New URI': '新 URI',
    'Child nodes will follow automatically.': '子节点会自动一并移动。',
    'Last updated by': '最后更新来源',
    'Delete this node and all its children? This cannot be undone.': '确认删除此节点及其所有子节点？此操作无法撤销。',
    'Memory History': '记忆历史',
    'History': '历史',
    'View history changes': '查看历史变更',
    'Back to memory': '返回记忆',
    'Operation': '操作',
    'Time': '时间',
    'Failed to load node history': '加载节点历史失败',
    'Rollback this node to the selected history event?': '确认将此节点回滚到选中的历史事件？',
    'Rollback completed': '回滚完成',
    'Rollback failed': '回滚失败',
    'No history events for this node.': '此节点暂无历史事件。',

    // ── maintenance
    'Deprecated': '已废弃',
    'Orphaned': '孤立',
    'Orphans': '孤立记忆',
    'Memories that no longer connect to the active graph. Review and remove what is safe to drop.':
      '不再挂在图里的记忆。审核后可移除。',
    'Scanning…': '扫描中…',
    'Rescan': '重新扫描',
    'Deleting…': '删除中…',
    'All clear. No orphans to review.': '一切清爽，暂无孤立项。',
    'migrated to': '迁移至',
    'Migrated to': '迁移至',
    'Diff': '差异',
    'Full text': '全文',
    'Paths': '路径',
    'Orphan Detail': '孤立详情',
    'Content unchanged': '内容未变',
    'Both versions have identical content. Only routes or metadata differ.': '两个版本内容相同，仅路径或元数据有差异。',
    'Permanently delete this memory? This cannot be undone.': '永久删除此记忆？此操作无法撤销。',
    'Delete {n} memories?': '确认删除所选 {n} 条记忆？',

    // ── dream
    'Structural Audit': '结构审计',
    'Dream Diary': '梦境日记',
    'Memory Maintenance': '记忆整理',
    'Dream audits Lore structure — path placement, split needs, retrieval-path issues, and safe move/update decisions.':
      'Dream 会检查 Lore 的结构，判断节点该放在哪里、是否需要拆分，以及哪些调整适合安全执行。',
    'System dreams daily to organize memories — index refresh, health checks, and LLM-driven consolidation.':
      '系统每天自动整理记忆——刷新索引、检查健康状况，并用模型辅助归纳整合。',
    'Run started': '运行开始',
    'Phase started': '阶段开始',
    'Phase completed': '阶段完成',
    'LLM turn': '模型回合',
    'Tool started': '工具开始',
    'Tool finished': '工具完成',
    'Assistant note': '模型备注',
    'Run completed': '运行完成',
    'Run failed': '运行失败',
    'Index refresh': '索引刷新',
    'Data collection': '数据收集',
    'Agent loop': '执行循环',
    'Path recs': '路径建议',
    'Run Dream Now': '立即运行',
    'Dreaming…': '运行中…',
    'Dream Summary': '梦境摘要',
    'Agent Stages': '执行阶段',
    'Collapse agent stages': '收起执行阶段',
    'Expand agent stages': '展开执行阶段',
    'Missed recalls': '疑似漏召回',
    'Reviewed queries': '已审查询',
    'Recall queries': '召回查询',
    'Metadata queries': '元数据查询',
    'Write events': '写入事件',
    'Enriched': '已增强',
    'Maintenance events': '维护事件',
    'Index updated': '索引更新',
    'Index deleted': '索引删除',
    'Last Dream': '最近一次',
    'Total Entries': '总条目',
    'Last Status': '上次状态',
    'Schedule': '定时计划',
    'Off': '关闭',
    'completed': '完成',
    'error': '错误',
    'running': '运行中',
    'Narrative': '日记',
    'Diary': '日记',
    'Original Diary': '原始日记',
    'View original diary': '查看原始日记',
    'View diary': '查看日记',
    'Dream Audit': '梦境审计',
    'Primary focus': '主要焦点',
    'Confidence': '置信度',
    'Changed nodes': '变更节点',
    'Evidence': '证据',
    'Candidates': '候选',
    'Focus': '焦点',
    'Recall repairs': '召回修复',
    'Validated': '已验证',
    'Recommended apply': '建议执行',
    'Applied changes': '已执行变更',
    'Skipped': '已跳过',
    'Why not more changes': '为何不做更多变更',
    'Expected effect': '预期效果',
    'Tool Calls': '工具调用',
    'Health Report': '健康报告',
    'healthy': '健康',
    'underperforming': '低效',
    'dead': '沉睡',
    'noisy': '噪声',
    'Rollback': '回滚',
    'Rolling back…': '回滚中…',
    'rolled_back': '已回滚',
    'Confirm rollback? This will reverse all changes from this dream.': '确认回滚？这将撤销此次梦境整理的所有修改。',
    'Approve': '通过',
    'Dismiss': '忽略',
    'Approved': '已通过',
    'Dismissed': '已忽略',
    'Pending review': '待审核',
    'Viewed': '查看',
    'Modified': '修改',
    'Deleted': '删除',
    'Date': '日期',
    'Duration': '耗时',
    'No diary entries yet. Run your first dream!': '暂无日记。立即运行一次。',
    'calls': '次调用',
    'dead writes': '无效写入',
    'Memory Changes': '记忆变更',
    'Agent Workflow': '执行过程',
    'Waiting for workflow events…': '等待执行过程事件…',
    'turns': '回合',
    'Read-only diagnosis': '只读诊断',
    'Structured candidate plan': '结构化候选计划',
    'Preflight validation': '预检验证',
    'Bounded apply': '限量执行',
    'Structured audit': '结构化审计',
    'Poetic diary rewrite': '日记',
    'Moved': '已移动',
    'Disclosure changed': '披露已变更',
    '(none)': '（无）',
    'Protected': '保护拦截',
    'Protected boot block': '固定启动节点拦截',
    'create': '新增',
    'update': '更新',
    'delete': '删除',
    'move': '移动',
    'alias': '别名',
    'glossary_add': '术语新增',
    'glossary_remove': '术语移除',
    'No changes': '无变更',
    'Before': '修改前',
    'After': '修改后',
    'Policy blocks': '策略拦截',
    'Policy warnings': '策略警告',
    'Policy validation block': '策略校验拦截',
    'Policy warning': '策略警告',
    'Protected boot node': '固定启动节点',
    'cannot be updated': '不允许更新',
    'cannot be deleted': '不允许删除',
    'cannot be moved': '不允许移动',
    'Cannot move': '不能把',
    'to protected boot path': '移动到固定启动路径',
    'another node': '其他节点',
    'blocked the action': '已拦截',

    // ── backup
    'Backup Actions': '备份操作',
    'Manual backup and restore operations': '手动备份与恢复操作',
    'Contents': '目录',
    'Automation': '自动化',
    'Operations': '操作',
    'Last backup': '上次备份',
    'Run Backup Now': '立即备份',
    'Backing up…': '备份中…',
    'Export & Download': '导出下载',
    'Exporting…': '导出中…',
    'Export completed': '导出完成',
    'Import & Restore': '导入恢复',
    'Restoring…': '恢复中…',
    'Backup completed': '备份完成',
    'Restore completed': '恢复完成',
    'Confirm restore? This will replace ALL current data.': '确认恢复？这将替换所有当前数据。',
    'Size': '大小',
    'Stored': '已保存',

    // ── settings
    'Default': '默认',
    'From env': '环境变量',
    'Unsaved': '未保存',
    'Configuration': '配置',
    'Runtime parameters for the recall pipeline. Changes take effect immediately.':
      '召回管线的运行时参数，修改立即生效。',
    'Discard': '丢弃',
  },
  en: {}, // English uses keys verbatim
};

interface LanguageContextValue {
  lang: Lang;
  setLang: (lang: Lang) => void;
  t: (key: TranslationKey) => string;
}

const LanguageContext = createContext<LanguageContextValue>({
  lang: 'zh',
  setLang: () => {},
  t: (k: string) => k,
});

interface LanguageProviderProps {
  children: ReactNode;
}

export function LanguageProvider({ children }: LanguageProviderProps): React.JSX.Element {
  const [lang, setLangState] = useState<Lang>('zh');

  useEffect(() => {
    try {
      const saved = typeof window !== 'undefined' && window.localStorage.getItem('lore-lang');
      if (saved === 'zh' || saved === 'en') setLangState(saved);
    } catch { /* ignore */ }
  }, []);

  useEffect(() => {
    if (typeof document === 'undefined') return;
    document.documentElement.lang = lang;
  }, [lang]);

  const setLang = useCallback((next: Lang) => {
    setLangState(next);
    try { window.localStorage.setItem('lore-lang', next); } catch { /* ignore */ }
  }, []);

  const t = useCallback((key: TranslationKey): string => {
    if (lang === 'en') return key;
    return DICT.zh[key] ?? key;
  }, [lang]);

  return (
    <LanguageContext.Provider value={{ lang, setLang, t }}>
      {children}
    </LanguageContext.Provider>
  );
}

export function useT(): LanguageContextValue {
  return useContext(LanguageContext);
}
