<template>
  <div class="page">
    <div class="profile-layout">
      <aside class="profile-aside">
        <div class="avatar-panel">
          <el-avatar :size="88" :src="avatarPreview" :alt="form.full_name || form.email" class="profile-avatar">
            {{ profileInitial }}
          </el-avatar>
          <div class="avatar-meta">
            <div class="avatar-name">{{ form.full_name || TEXT.modules.profile.title }}</div>
            <div class="avatar-email">{{ form.email }}</div>
          </div>
          <el-button :icon="Upload" class="upload-button avatar-uploader" @click="selectAndUploadAvatar">
            {{ TEXT.modules.profile.uploadAvatar }}
          </el-button>
        </div>
      </aside>

      <main class="profile-main">
        <header class="profile-header">
          <div>
            <h3 class="title">{{ TEXT.modules.profile.title }}</h3>
          </div>
          <el-button class="dialog-close" text :icon="Close" aria-label="关闭个人中心" @click="emit('close-request')" />
        </header>

        <el-form ref="formRef" :model="form" :rules="rules" label-width="112px" class="form" autocomplete="off">
          <section class="form-section">
            <div class="section-heading">
              <span class="section-kicker">Account</span>
              <h4>基本信息</h4>
            </div>
            <el-form-item :label="TEXT.common.fields.email">
              <el-input v-model="form.email" disabled />
            </el-form-item>
            <el-form-item :label="TEXT.common.fields.name" prop="full_name">
              <el-input v-model="form.full_name" autocomplete="off" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.name" />
            </el-form-item>
            <el-form-item :label="TEXT.common.fields.clinicalDepartment" prop="clinical_department">
              <el-input v-model="form.clinical_department" autocomplete="off" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.clinicalDepartment" />
            </el-form-item>
          </section>

          <section class="form-section form-section--password">
            <div class="section-heading">
              <span class="section-kicker">Security</span>
              <h4>修改密码</h4>
            </div>
            <el-form-item :label="TEXT.modules.profile.currentPassword" prop="current_password">
              <el-input
                v-model="form.current_password"
                type="password"
                show-password
                autocomplete="new-password"
                name="ctms-profile-current-password"
                :placeholder="TEXT.modules.profile.currentPasswordHint"
              />
            </el-form-item>
            <el-form-item :label="TEXT.modules.profile.newPassword" prop="password">
              <el-input v-model="form.password" type="password" show-password :placeholder="TEXT.modules.profile.newPasswordHint" />
            </el-form-item>
            <el-form-item :label="TEXT.modules.profile.confirmPassword" prop="confirmPassword">
              <el-input v-model="form.confirmPassword" type="password" show-password :placeholder="TEXT.modules.profile.confirmPasswordHint" />
            </el-form-item>
          </section>

          <section class="form-section form-section--desktop">
            <div class="section-heading">
              <span class="section-kicker">Desktop</span>
              <h4>客户端与通知</h4>
            </div>
            <el-form-item v-if="isDesktop" label="系统通知">
              <el-switch
                v-model="desktopNotificationsEnabled"
                :loading="desktopNotificationLoading"
                @change="onDesktopNotificationChange"
              />
              <span class="desktop-setting-hint">仅推送不含项目详情的文件更新提示</span>
            </el-form-item>
            <el-form-item label="客户端信息">
              <div class="client-metadata">
                <code>{{ clientMetadataText }}</code>
                <el-button size="small" @click="copyClientMetadata">复制</el-button>
              </div>
            </el-form-item>
          </section>

          <div class="actions">
            <el-button type="primary" :loading="submitting" @click="onSubmit">{{ TEXT.common.actions.save }}</el-button>
          </div>
        </el-form>
      </main>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, reactive, ref, watch } from "vue";
import type { FormInstance, FormRules } from "element-plus";
import { ElMessage } from "element-plus";
import { Close, Upload } from "@element-plus/icons-vue";
import { updateProfile, fetchMe, uploadAvatar } from "../api/auth";
import {
  getDesktopNotificationSubscription,
  setDesktopNotificationSubscription,
} from "../api/desktopNotifications";
import { useAuthStore } from "../store/auth";
import {
  getAppMetadata,
  isTauriRuntime,
  pickFiles,
  requestNotificationPermission,
} from "../runtime";
import { triggerDesktopNotificationPoll } from "../session/desktopNotificationManager";
import { TEXT, requiredMessage } from "../locales";

const emit = defineEmits<{
  "close-request": [];
  "dirty-change": [dirty: boolean];
  saved: [];
}>();
const auth = useAuthStore();
const isDesktop = isTauriRuntime();
const clientMetadata = getAppMetadata();
const clientMetadataText = [
  `${clientMetadata.clientType} ${clientMetadata.version}`,
  clientMetadata.platform,
  clientMetadata.channel,
  clientMetadata.commit,
].join(" · ");
const desktopNotificationsEnabled = ref(false);
const desktopNotificationLoading = ref(false);
const formRef = ref<FormInstance>();
const submitting = ref(false);
const form = reactive({
  email: "",
  full_name: "",
  clinical_department: "",
  current_password: "",
  password: "",
  confirmPassword: "",
});
const savedProfile = ref({
  full_name: "",
  clinical_department: "",
});
const avatarPreview = ref<string | undefined>();
const profileInitial = computed(() => (form.full_name?.charAt(0) || form.email?.charAt(0) || "?").toUpperCase());
const avatarAcceptedTypes = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
const hasUnsavedChanges = computed(
  () =>
    form.full_name !== savedProfile.value.full_name ||
    form.clinical_department !== savedProfile.value.clinical_department ||
    Boolean(form.current_password || form.password || form.confirmPassword)
);

const rules: FormRules<typeof form> = {
  full_name: [{ required: true, message: requiredMessage(TEXT.common.fields.name), trigger: "blur" }],
  clinical_department: [{ required: true, message: requiredMessage(TEXT.common.fields.clinicalDepartment), trigger: "blur" }],
  current_password: [
    {
      validator: (_r, value, cb) => {
        if (form.password && !value) {
          cb(new Error(TEXT.modules.profile.passwordRequired));
          return;
        }
        cb();
      },
      trigger: "blur",
    },
  ],
  password: [
    {
      validator: (_r, value, cb) => {
        if (!value) return cb();
        if (value.length < 8 || !/[A-Za-z]/.test(value) || !/[0-9]/.test(value)) {
          cb(new Error(TEXT.modules.profile.passwordRule));
          return;
        }
        cb();
      },
      trigger: "blur",
    },
  ],
  confirmPassword: [
    {
      validator: (_r, value, cb) => {
        if (form.password && value !== form.password) {
          cb(new Error(TEXT.modules.profile.passwordMismatch));
          return;
        }
        cb();
      },
      trigger: "blur",
    },
  ],
};

const loadProfile = async () => {
  const { data } = await fetchMe();
  form.email = data.email;
  form.full_name = data.full_name;
  form.clinical_department = data.clinical_department;
  savedProfile.value = {
    full_name: data.full_name,
    clinical_department: data.clinical_department,
  };
  avatarPreview.value = data.avatar_url || undefined;
};

const loadDesktopNotificationSubscription = async () => {
  if (!isDesktop) return;
  const { data } = await getDesktopNotificationSubscription();
  desktopNotificationsEnabled.value = data.enabled;
};

const onDesktopNotificationChange = async (value: string | number | boolean) => {
  if (!isDesktop || desktopNotificationLoading.value) return;
  desktopNotificationLoading.value = true;
  try {
    const enable = Boolean(value);
    if (enable) {
      const permission = await requestNotificationPermission();
      if (permission !== "granted") {
        desktopNotificationsEnabled.value = false;
        ElMessage.warning("系统通知权限未开启，请在系统设置中允许 CTMS 通知");
        return;
      }
    }
    const { data } = await setDesktopNotificationSubscription(enable);
    desktopNotificationsEnabled.value = data.enabled;
    if (data.enabled) triggerDesktopNotificationPoll();
  } catch (error: any) {
    desktopNotificationsEnabled.value = !Boolean(value);
    ElMessage.error(error?.response?.data?.detail || "通知设置保存失败");
  } finally {
    desktopNotificationLoading.value = false;
  }
};

const copyClientMetadata = async () => {
  await navigator.clipboard.writeText(clientMetadataText);
  ElMessage.success("客户端信息已复制");
};

const onSubmit = async () => {
  if (!formRef.value) return;
  await formRef.value.validate(async (valid) => {
    if (!valid) return;
    submitting.value = true;
    try {
      await updateProfile({
        full_name: form.full_name,
        clinical_department: form.clinical_department,
        current_password: form.password ? form.current_password : undefined,
        password: form.password || undefined,
      });
      await auth.fetchMe();
      ElMessage.success(TEXT.modules.profile.updateSuccess);
      form.current_password = "";
      form.password = "";
      form.confirmPassword = "";
      savedProfile.value = {
        full_name: form.full_name,
        clinical_department: form.clinical_department,
      };
      avatarPreview.value = auth.user?.avatar_url || avatarPreview.value;
      emit("saved");
    } catch (error: any) {
      ElMessage.error(error?.response?.data?.message || TEXT.modules.profile.updateFailed);
    } finally {
      submitting.value = false;
    }
  });
};

const selectAndUploadAvatar = async () => {
  const [file] = await pickFiles({
    multiple: false,
    accept: ["png", "jpg", "jpeg", "gif", "webp"],
    title: TEXT.modules.profile.uploadAvatar,
  });
  if (!file) return;
  const extensionAllowed = /\.(png|jpe?g|gif|webp)$/i.test(file.name);
  if (!avatarAcceptedTypes.has(file.type) && !extensionAllowed) {
    ElMessage.error(TEXT.modules.profile.avatarTypeInvalid);
    return;
  }
  try {
    await uploadAvatar(file);
    await auth.fetchMe();
    avatarPreview.value = auth.user?.avatar_url || undefined;
    ElMessage.success(TEXT.modules.profile.avatarUpdated);
  } catch (err: any) {
    ElMessage.error(err?.response?.data?.message || TEXT.modules.profile.avatarUploadFailed);
  }
};

onMounted(() => {
  loadProfile();
  loadDesktopNotificationSubscription().catch(() => {});
});

watch(hasUnsavedChanges, (dirty) => emit("dirty-change", dirty), { immediate: true });
</script>

<style scoped>
.page {
  background: #fff;
}

.profile-layout {
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  min-height: 620px;
}

.profile-aside {
  padding: 48px 32px;
  border-right: 1px solid #e5ebf2;
  background: linear-gradient(180deg, #f8fbfd 0%, #f1f5f9 100%);
}

.avatar-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.profile-avatar {
  --el-avatar-bg-color: #64748b;
  color: #fff;
  font-size: 30px;
  font-weight: 700;
  border: 4px solid #fff;
  box-shadow: 0 12px 30px rgba(51, 65, 85, 0.16);
}

.avatar-meta {
  width: 100%;
  margin-top: 18px;
}

.avatar-name {
  overflow: hidden;
  color: #172033;
  font-size: 17px;
  font-weight: 700;
  line-height: 1.35;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.avatar-email {
  overflow: hidden;
  margin-top: 6px;
  color: #718096;
  font-size: 12px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.avatar-uploader {
  margin-top: 22px;
}

.upload-button {
  border-color: #cbd5e1;
  color: #40566f;
  font-weight: 600;
}

.profile-main {
  padding: 42px 48px 36px;
  background: #fff;
}

.profile-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
  margin-bottom: 26px;
}

.dialog-close {
  width: 32px;
  height: 32px;
  margin-top: -4px;
  color: #718096;
}

.title {
  margin: 0;
  color: #172033;
  font-size: 24px;
  font-weight: 800;
  line-height: 1.25;
}

.form {
  max-width: 620px;
}

.form-section {
  padding-top: 2px;
}

.form-section--password {
  margin-top: 26px;
  padding-top: 28px;
  border-top: 1px solid #e5ebf2;
}

.form-section--desktop {
  margin-top: 26px;
  padding-top: 28px;
  border-top: 1px solid #e5ebf2;
}

.desktop-setting-hint {
  margin-left: 12px;
  color: #7f92ad;
  font-size: 12px;
}

.client-metadata {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.client-metadata code {
  overflow-wrap: anywhere;
  color: #40566f;
  font-size: 12px;
}

.section-heading {
  margin-bottom: 18px;
  padding-left: 112px;
}

.section-kicker {
  display: block;
  color: #7f92ad;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0;
  line-height: 1;
  text-transform: uppercase;
}

.section-heading h4 {
  margin: 6px 0 0;
  color: #1f2a3d;
  font-size: 15px;
  font-weight: 800;
  line-height: 1.4;
}

.form :deep(.el-form-item) {
  margin-bottom: 20px;
}

.form :deep(.el-form-item__label) {
  color: #6f83a3;
  font-weight: 700;
}

.form :deep(.el-input__wrapper) {
  min-height: 44px;
  border-radius: 10px;
  box-shadow: 0 0 0 1px #dfe6ee inset;
}

.form :deep(.el-input__wrapper.is-focus) {
  box-shadow: 0 0 0 1px #3f8f6b inset, 0 0 0 3px rgba(63, 143, 107, 0.12);
}

.actions {
  margin-top: 28px;
  padding-left: 112px;
  text-align: right;
}

.actions :deep(.el-button) {
  min-width: 96px;
  min-height: 40px;
  border-radius: 10px;
  font-weight: 700;
}

@media (max-width: 900px) {
  .profile-layout {
    grid-template-columns: 1fr;
  }

  .profile-aside {
    border-right: 0;
    border-bottom: 1px solid #e5ebf2;
  }

  .profile-main {
    padding: 32px 24px;
  }
}

@media (max-width: 640px) {
  .profile-main {
    padding: 24px 18px;
  }

  .form {
    max-width: none;
  }

  .section-heading,
  .actions {
    padding-left: 0;
  }

  .actions {
    text-align: left;
  }
}
</style>
