<template>
  <teleport to="body">
    <transition name="edit-icon-fade" appear>
      <div v-if="open" class="edit-icon-backdrop" @click.self="closeDialog">
        <div class="edit-icon-dialog" role="dialog" aria-modal="true" aria-label="编辑图标">
          <button class="dialog-close" type="button" @click="closeDialog">
            <Icon icon="lucide:x" aria-hidden="true" />
          </button>
          <div class="form-row">
            <label>地址</label>
            <div class="input-group">
              <input ref="urlInputRef" v-model="url" type="text" placeholder="https://"/>
              <button
                class="ghost-button"
                type="button"
                :disabled="!url.trim()"
                @click="fetchFavicon"
              >
                获取图标
              </button>
            </div>
          </div>
          <div class="form-row">
            <label>名称</label>
            <input v-model="name" type="text" placeholder="网站名称" maxlength="16" />
          </div>
          <div class="form-row">
            <label>图标颜色</label>
            <div class="color-row">
              <button
                v-for="item in colorOptions"
                :key="item.id"
                class="color-dot"
                :class="{ selected: selectedColorId === item.id }"
                type="button"
                :style="item.previewStyle"
                @click="selectColor(item)"
              ></button>
              <input
                ref="colorInputRef"
                v-model="customColor"
                type="color"
                class="color-input"
                @input="updateCustomColor"
              />
            </div>
          </div>
          <div class="form-row">
            <label>图标文字</label>
            <input v-model="iconText" type="text" placeholder="A" maxlength="2" />
          </div>
          <div class="icon-type-row">
            <button
              class="icon-type-card"
              :class="{ active: iconType === 'current' }"
              type="button"
              @click="selectType('current')"
            >
              <div class="icon-preview" :style="currentPreviewStyle">
                <span v-if="!currentImg">{{ currentPreviewText }}</span>
              </div>
              <span class="icon-type-label">当前图标</span>
            </button>
            <button
              class="icon-type-card"
              :class="{ active: iconType === 'text' }"
              type="button"
              @click="selectType('text')"
            >
              <div class="icon-preview" :style="{ backgroundColor: previewColor }">
                <span>{{ previewText }}</span>
              </div>
              <span class="icon-type-label">文字图标</span>
            </button>
            <button
              class="icon-type-card"
              :class="{ active: iconType === 'image' }"
              type="button"
              @click="triggerUpload"
            >
              <div class="icon-preview image" :style="imagePreviewStyle">
                <Icon v-if="!previewImage" icon="lucide:plus" aria-hidden="true" />
              </div>
              <span class="icon-type-label">上传</span>
            </button>
            <input
              ref="fileInputRef"
              class="file-input"
              type="file"
              accept="image/*"
              @change="handleFileChange"
            />
          </div>
          <div class="form-actions">
            <button class="btn btn-primary" type="button" :disabled="!canSubmit" @click="submit">
              保存
            </button>
          </div>
          <div v-if="errorMessage" class="form-error">{{ errorMessage }}</div>
        </div>
      </div>
    </transition>
  </teleport>
</template>

<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { Icon } from '@iconify/vue';

type IconItem = {
  id: string;
  name: string;
  url: string;
  img?: string;
  text?: string;
  bgColor?: string;
};

type ColorOption = {
  id: string;
  value: string;
  previewStyle: Record<string, string>;
  isCustom?: boolean;
};

const props = defineProps<{
  open: boolean;
  icon: IconItem | null;
}>();

const emit = defineEmits<{
  (event: 'update:open', value: boolean): void;
  (event: 'submit', payload: { id: string; name: string; url: string; bgColor: string; img?: string; text?: string }): void;
}>();

const colorOptions: ColorOption[] = [
  { id: 'blue', value: '#2f80ed', previewStyle: { backgroundColor: '#2f80ed' } },
  { id: 'orange', value: '#f2c94c', previewStyle: { backgroundColor: '#f2c94c' } },
  { id: 'red', value: '#eb5757', previewStyle: { backgroundColor: '#eb5757' } },
  { id: 'brown', value: '#4f4f4f', previewStyle: { backgroundColor: '#4f4f4f' } },
  { id: 'green', value: '#6fcf97', previewStyle: { backgroundColor: '#6fcf97' } },
  { id: 'navy', value: '#2d9cdb', previewStyle: { backgroundColor: '#2d9cdb' } },
  { id: 'gold', value: '#bb8f3e', previewStyle: { backgroundColor: '#bb8f3e' } },
  { id: 'plum', value: '#9b51e0', previewStyle: { backgroundColor: '#9b51e0' } },
  { id: 'rose', value: '#f15b5b', previewStyle: { backgroundColor: '#f15b5b' } },
  { id: 'blue-dark', value: '#2f5fd1', previewStyle: { backgroundColor: '#2f5fd1' } },
  { id: 'mint', value: '#8bd8bd', previewStyle: { backgroundColor: '#8bd8bd' } },
  { id: 'custom', value: '#7a7a7a', previewStyle: { backgroundImage: 'conic-gradient(#f66, #fa0, #6f6, #6cf, #66f, #c6f, #f66)' }, isCustom: true },
];

const url = ref('');
const name = ref('');
const iconText = ref('');
const selectedColorId = ref(colorOptions[0].id);
const customColor = ref('#8a8a8a');
const iconType = ref<'current' | 'text' | 'image'>('current');
const currentImg = ref('');
const currentText = ref('');
const fetchedImage = ref('');
const uploadedImage = ref('');
const errorMessage = ref('');
const fileInputRef = ref<HTMLInputElement | null>(null);
const colorInputRef = ref<HTMLInputElement | null>(null);
const urlInputRef = ref<HTMLInputElement | null>(null);

const canSubmit = computed(() => {
  if (!name.value.trim() || !props.icon) return false;
  if (iconType.value === 'image' && !previewImage.value) return false;
  return true;
});

const previewText = computed(() => {
  const manual = iconText.value.trim();
  if (manual) return manual.slice(0, 2);
  const fallback = name.value.trim();
  return fallback ? fallback.charAt(0) : 'A';
});

const currentPreviewText = computed(() => {
  if (currentText.value.trim()) return currentText.value.trim().slice(0, 2);
  const fallback = name.value.trim();
  return fallback ? fallback.charAt(0) : 'A';
});

const previewImage = computed(() => uploadedImage.value || fetchedImage.value);

const previewColor = computed(() => {
  const option = colorOptions.find(item => item.id === selectedColorId.value);
  if (option?.isCustom) return customColor.value;
  return option?.value ?? colorOptions[0].value;
});

const currentPreviewStyle = computed(() => ({
  backgroundColor: previewColor.value,
  backgroundImage: currentImg.value ? `url('${currentImg.value}')` : 'none',
}));

const imagePreviewStyle = computed(() => ({
  backgroundImage: previewImage.value ? `url('${previewImage.value}')` : 'none',
}));

const normalizeUrl = (value: string) => {
  if (!value.trim()) return '#';
  try {
    return new URL(value).toString();
  } catch {
    try {
      return new URL(`https://${value}`).toString();
    } catch {
      return '#';
    }
  }
};

const closeDialog = () => {
  emit('update:open', false);
};

const selectColor = (item: ColorOption) => {
  selectedColorId.value = item.id;
  if (item.isCustom && colorInputRef.value) {
    colorInputRef.value.click();
  }
};

const updateCustomColor = () => {
  selectedColorId.value = 'custom';
};

const selectType = (type: 'current' | 'text' | 'image') => {
  iconType.value = type;
};

const triggerUpload = () => {
  iconType.value = 'image';
  fileInputRef.value?.click();
};

const handleFileChange = (event: Event) => {
  const input = event.target as HTMLInputElement | null;
  const file = input?.files?.[0];
  if (!file) return;
  const reader = new FileReader();
  reader.onload = () => {
    uploadedImage.value = String(reader.result);
    iconType.value = 'image';
  };
  reader.readAsDataURL(file);
};

const fetchFavicon = () => {
  errorMessage.value = '';
  const raw = url.value.trim();
  if (!raw) return;
  const resolved = normalizeUrl(raw);
  if (resolved === '#') {
    errorMessage.value = '请输入有效的网址';
    return;
  }
  const parsed = new URL(resolved);
  const favicon = `https://www.google.com/s2/favicons?domain=${parsed.hostname}&sz=128`;
  fetchedImage.value = favicon;
  iconType.value = 'image';
};

const submit = () => {
  if (!props.icon || !canSubmit.value) return;
  const payload = {
    id: props.icon.id,
    name: name.value.trim(),
    url: normalizeUrl(url.value.trim()),
    bgColor: previewColor.value,
    img: undefined as string | undefined,
    text: undefined as string | undefined,
  };
  if (iconType.value === 'current') {
    payload.img = currentImg.value || undefined;
    payload.text = currentImg.value ? undefined : currentText.value.trim() || currentPreviewText.value;
  } else if (iconType.value === 'text') {
    payload.text = iconText.value.trim() || previewText.value;
  } else if (iconType.value === 'image') {
    payload.img = previewImage.value || undefined;
  }
  emit('submit', payload);
  closeDialog();
};

const handleKeydown = (event: KeyboardEvent) => {
  if (!props.open) return;
  if (event.key === 'Escape') {
    event.preventDefault();
    closeDialog();
  }
};

const initFromIcon = () => {
  if (!props.icon) return;
  url.value = props.icon.url ?? '';
  name.value = props.icon.name ?? '';
  iconText.value = props.icon.text ?? '';
  currentImg.value = props.icon.img ?? '';
  currentText.value = props.icon.text ?? '';
  fetchedImage.value = '';
  uploadedImage.value = '';
  errorMessage.value = '';
  iconType.value = props.icon.img ? 'current' : 'text';
  const match = colorOptions.find(item => item.value === props.icon?.bgColor);
  if (match) {
    selectedColorId.value = match.id;
  } else if (props.icon.bgColor) {
    selectedColorId.value = 'custom';
    customColor.value = props.icon.bgColor;
  } else {
    selectedColorId.value = colorOptions[0].id;
  }
};

onMounted(() => {
  window.addEventListener('keydown', handleKeydown);
});

onBeforeUnmount(() => {
  window.removeEventListener('keydown', handleKeydown);
});

watch(
  () => [props.open, props.icon?.id],
  ([open]) => {
    if (open) {
      initFromIcon();
      nextTick(() => urlInputRef.value?.focus());
    }
  }
);
</script>

<style lang="scss" scoped>
@import '@/styles/tokens.scss';

.edit-icon-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: $z-index-modal;
}

.edit-icon-dialog {
  width: min(860px, 90vw);
  padding: 28px 36px 32px;
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.96);
  color: #2a2a2a;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.18);
  position: relative;
}

.dialog-close {
  position: absolute;
  top: 16px;
  right: 18px;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: none;
  background: rgba(0, 0, 0, 0.06);
  color: #333;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.form-row {
  display: grid;
  grid-template-columns: 64px 1fr;
  gap: 12px;
  align-items: center;
  font-size: 13px;
  color: #6b6b6b;
  margin-bottom: 12px;
}

.form-row input {
  height: 34px;
  border-radius: 18px;
  border: 1px solid rgba(0, 0, 0, 0.08);
  background: rgba(0, 0, 0, 0.04);
  color: #2a2a2a;
  padding: 0 12px;
  outline: none;
}

.input-group {
  display: flex;
  gap: 12px;
}

.input-group input {
  flex: 1;
}

.ghost-button {
  padding: 6px 14px;
  border-radius: 16px;
  border: 1px solid rgba(0, 0, 0, 0.12);
  background: transparent;
  color: #2a2a2a;
  cursor: pointer;
}

.ghost-button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.color-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.color-dot {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
}

.color-dot.selected {
  border-color: #111;
}

.color-input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.icon-type-row {
  display: flex;
  gap: 14px;
  align-items: flex-end;
  margin: 10px 0 18px;
}

.icon-type-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
  background: transparent;
  border: none;
  color: #7a7a7a;
  cursor: pointer;
}

.icon-type-card.active {
  color: #2a2a2a;
}

.icon-preview {
  width: 56px;
  height: 56px;
  border-radius: 16px;
  background: rgba(0, 0, 0, 0.06);
  border: 1px solid rgba(0, 0, 0, 0.08);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 20px;
  font-weight: 600;
  background-size: cover;
  background-position: center;
}

.icon-preview.image {
  background-color: rgba(0, 0, 0, 0.04);
}

.icon-type-label {
  font-size: 12px;
}

.file-input {
  display: none;
}

.form-actions {
  margin-top: 4px;
}

.btn {
  padding: 8px 28px;
  border-radius: 16px;
  border: none;
  font-size: 13px;
  cursor: pointer;
}

.btn-primary {
  background: #1e88ff;
  color: #fff;
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.form-error {
  color: #e24b4b;
  font-size: 12px;
  margin-top: 8px;
}

.edit-icon-fade-enter-active,
.edit-icon-fade-leave-active {
  transition: opacity 220ms ease;
}

.edit-icon-fade-enter-from,
.edit-icon-fade-leave-to {
  opacity: 0;
}

.edit-icon-fade-enter-active .edit-icon-dialog,
.edit-icon-fade-leave-active .edit-icon-dialog {
  transition: transform 240ms ease, opacity 240ms ease;
}

.edit-icon-fade-enter-from .edit-icon-dialog,
.edit-icon-fade-leave-to .edit-icon-dialog {
  transform: translateY(6px) scale(0.98);
  opacity: 0;
}
</style>
