<template>
  <div class="container">
    <div class="card">
      <h1>🔑 字节对象 → Hex 密钥</h1>
      <p class="sub">
        将键为数字索引的 JSON 对象转换为 32 位十六进制字符串（适用于 AES‑128 密钥）
      </p>

      <label for="jsonInput">输入 JSON 对象</label>
      <textarea
        id="jsonInput"
        v-model="inputText"
        placeholder='例如：{"0":14,"1":3,"2":121,...}'
        @keydown.ctrl.enter="performConversion"
        @keydown.meta.enter="performConversion"
      ></textarea>

      <div class="actions">
        <button @click="performConversion">🔄 转换</button>
        <button class="secondary" :disabled="!copyEnabled" @click="copyResult">
          📋 复制 Hex
        </button>
        <span class="copy-feedback" :class="{ show: copyFeedbackVisible }"
          >已复制！</span
        >
      </div>

      <div class="result-area">
        <div class="result-label">
          <span>转换结果</span>
        </div>
        <div
          class="result-box"
          :class="resultClass"
          v-text="resultText"
        ></div>
      </div>

      <div class="example-hint">
        💡 点击 <button class="link-btn" @click="loadExample">加载示例</button> 快速体验
      </div>
    </div>

    <div class="footer">
      按数字键升序取值，每个字节补零为两位十六进制
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'

// 示例数据
const EXAMPLE_OBJ: Record<string, number> = {
  '0': 14,
  '1': 3,
  '2': 121,
  '3': 117,
  '4': 7,
  '5': 245,
  '6': 22,
  '7': 34,
  '8': 136,
  '9': 163,
  '10': 201,
  '11': 114,
  '12': 195,
  '13': 230,
  '14': 109,
  '15': 204,
}

// 响应式数据
const inputText = ref<string>('')
const resultText = ref<string>('等待转换…')
const resultClass = ref<string>('') // 用于 success / error
const copyFeedbackVisible = ref<boolean>(false)
const copyEnabled = ref<boolean>(false)

// 核心转换函数
function convertToHex(obj: Record<string, unknown>): string {
  // 获取所有键并转为数字排序
  const keys = Object.keys(obj)
    .map((k) => Number(k))
    .sort((a, b) => a - b)

  // 检查是否有非数字键
  if (keys.some((k) => isNaN(k))) {
    throw new Error('对象包含非数字键，请确保键为 "0", "1", … 格式')
  }

  // 检查长度是否为16
  if (keys.length !== 16) {
    throw new Error(`期望 16 个字节，实际获得 ${keys.length} 个`)
  }

  // 按顺序取值并转为两位十六进制
  const hex = keys
    .map((key) => {
      const val = obj[key]
      if (typeof val !== 'number' || isNaN(val)) {
        throw new Error(`键 "${key}" 对应的值不是有效数字 (${val})`)
      }
      if (val < 0 || val > 255) {
        throw new Error(`键 "${key}" 的值 ${val} 超出字节范围 (0-255)`)
      }
      return val.toString(16).padStart(2, '0')
    })
    .join('')

  return hex
}

// 执行转换
function performConversion(): void {
  const raw = inputText.value.trim()
  if (!raw) {
    resultText.value = '⚠️ 请输入 JSON 数据'
    resultClass.value = 'error'
    copyEnabled.value = false
    copyFeedbackVisible.value = false
    return
  }

  try {
    const obj = JSON.parse(raw)
    if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
      throw new Error('输入必须是 JSON 对象（不是数组或原始类型）')
    }
    const hex = convertToHex(obj)
    resultText.value = hex
    resultClass.value = 'success'
    copyEnabled.value = true
    copyFeedbackVisible.value = false
  } catch (err: any) {
    resultText.value = `❌ 转换失败：${err.message}`
    resultClass.value = 'error'
    copyEnabled.value = false
    copyFeedbackVisible.value = false
  }
}

// 加载示例
function loadExample(): void {
  inputText.value = JSON.stringify(EXAMPLE_OBJ, null, 2)
  performConversion() // 自动转换
}

// 复制结果
function copyResult(): void {
  const text = resultText.value
  if (!text || resultClass.value === 'error') return

  if (navigator.clipboard && navigator.clipboard.writeText) {
    navigator.clipboard
      .writeText(text)
      .then(() => {
        copyFeedbackVisible.value = true
        setTimeout(() => (copyFeedbackVisible.value = false), 2000)
      })
      .catch(() => {
        fallbackCopy(text)
      })
  } else {
    fallbackCopy(text)
  }
}

function fallbackCopy(text: string): void {
  const textarea = document.createElement('textarea')
  textarea.value = text
  document.body.appendChild(textarea)
  textarea.select()
  try {
    document.execCommand('copy')
    copyFeedbackVisible.value = true
    setTimeout(() => (copyFeedbackVisible.value = false), 2000)
  } catch (_) {
    alert('复制失败，请手动复制')
  }
  document.body.removeChild(textarea)
}

// 组件挂载时加载示例
onMounted(() => {
  loadExample()
})
</script>

<style lang="scss" scoped>
// 变量（可根据需要调整）
$primary: #3b82f6;
$primary-hover: #2563eb;
$bg: #f6f8fa;
$card-bg: #ffffff;
$text-dark: #1f2937;
$text-gray: #6b7280;
$border: #d1d5db;
$radius: 16px;

* {
  box-sizing: border-box;
  font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
}

// 根容器：占满视口，flex列
.container {
  min-height: calc(100vh - 211px);
  width: 100%;
  display: flex;
  flex-direction: column;
  background: $bg;
  padding: 20px;
}

// 卡片区域：flex:1 撑满剩余空间，居中显示
.card {
  background: $card-bg;
  //max-width: 720px;
  width: 100%;
  border-radius: $radius;
  //box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
  padding: 28px 32px;
  margin: 0 auto;
  flex: 1; // 让卡片占据尽可能多的空间，但最大宽度限制
  align-self: center; // 水平居中
  width: 100%;
}

h1 {
  font-size: 22px;
  font-weight: 600;
  margin-top: 0;
  margin-bottom: 8px;
  color: $text-dark;
}

.sub {
  color: $text-gray;
  font-size: 14px;
  margin-top: -4px;
  margin-bottom: 20px;
}

label {
  font-weight: 500;
  font-size: 14px;
  color: #374151;
  display: block;
  margin-bottom: 6px;
}

textarea {
  width: 100%;
  height: 200px;
  padding: 12px 14px;
  border: 1px solid $border;
  border-radius: 10px;
  font-family: 'Menlo', 'Cascadia Code', 'Consolas', monospace;
  font-size: 13px;
  line-height: 1.6;
  resize: vertical;
  transition: border 0.2s, background 0.2s;
  background: #fafbfc;

  &:focus {
    border-color: $primary;
    outline: none;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
    background: white;
  }
}

.actions {
  display: flex;
  gap: 12px;
  margin: 18px 0 14px;
  flex-wrap: wrap;
  align-items: center;
}

button {
  background: $primary;
  color: white;
  border: none;
  padding: 10px 24px;
  border-radius: 8px;
  font-weight: 500;
  font-size: 14px;
  cursor: pointer;
  transition: background 0.15s, transform 0.1s;
  display: inline-flex;
  align-items: center;
  gap: 6px;

  &:hover {
    background: $primary-hover;
  }
  &:active {
    transform: scale(0.96);
  }
  &.secondary {
    background: #e5e7eb;
    color: $text-dark;
    &:hover {
      background: #d1d5db;
    }
  }
  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
    &:hover {
      background: #e5e7eb;
    }
  }
  &.link-btn {
    background: none;
    border: none;
    color: $primary;
    font-weight: 500;
    padding: 0;
    font-size: 13px;
    text-decoration: underline;
    display: inline;
    &:hover {
      color: $primary-hover;
      background: none;
      transform: none;
    }
  }
}

.copy-feedback {
  font-size: 13px;
  color: #10b981;
  margin-left: 12px;
  opacity: 0;
  transition: opacity 0.3s;
  &.show {
    opacity: 1;
  }
}

.result-area {
  margin-top: 8px;
}

.result-label {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 4px;
  span {
    font-weight: 500;
    font-size: 14px;
    color: #374151;
  }
}

.result-box {
  background: #f3f4f6;
  border-radius: 10px;
  padding: 14px 16px;
  font-family: 'Menlo', 'Cascadia Code', 'Consolas', monospace;
  font-size: 14px;
  word-break: break-all;
  min-height: 48px;
  border: 1px solid #e5e7eb;
  color: $text-dark;
  transition: background 0.2s, border-color 0.2s;

  &.error {
    background: #fee2e2;
    border-color: #fca5a5;
    color: #991b1b;
  }
  &.success {
    background: #ecfdf5;
    border-color: #6ee7b7;
  }
}

.example-hint {
  font-size: 13px;
  color: $text-gray;
  margin-top: 6px;
}

// 底部：置于页面最底部，自动与卡片分离
.footer {
  margin-top: auto; // 自动推到底部
  font-size: 13px;
  color: #9ca3af;
  text-align: center;
  padding: 16px 0 8px;
  border-top: 1px solid #e5e7eb;
  max-width: 720px;
  width: 100%;
  align-self: center;
}
</style>
