<script setup lang="ts">
interface ProfileView {
  id: string
  display_name: string
  description: string
  avatar_url: string | null
  created_at: string
  updated_at: string
}

interface SharedCampaign {
  id: string
  title: string
  status: string
  role: string
  joined_at: string
}

const { api } = useDngApi()
const auth = useDngAuth()
const profile = ref<ProfileView | null>(null)
const campaigns = ref<SharedCampaign[]>([])
const loading = ref(true)
const saving = ref(false)
const changingPassword = ref(false)
const removingAvatar = ref(false)
const error = ref('')
const success = ref('')
const avatarFile = ref<File | null>(null)
const avatarPreview = ref('')
const displayName = ref('')
const description = ref('')
const password = ref('')
const passwordConfirmation = ref('')

const initials = computed(() => (displayName.value || 'Adventurer')
  .trim().split(/\s+/).map(part => part[0]).join('').slice(0, 2).toUpperCase())
const currentAvatar = computed(() => avatarPreview.value || profile.value?.avatar_url || '')

function messageFrom(cause: unknown, fallback: string) {
  const value = cause as { data?: { statusMessage?: string; message?: string }; message?: string }
  return value.data?.statusMessage ?? value.data?.message ?? value.message ?? fallback
}

function clearPreview() {
  if (avatarPreview.value) URL.revokeObjectURL(avatarPreview.value)
  avatarPreview.value = ''
  avatarFile.value = null
}

function chooseAvatar(event: Event) {
  error.value = ''
  const input = event.target as HTMLInputElement
  const file = input.files?.[0]
  if (!file) return
  if (!['image/jpeg', 'image/png', 'image/webp'].includes(file.type)) {
    error.value = 'Choose a JPG, PNG, or WebP image.'
    input.value = ''
    return
  }
  if (file.size > 2 * 1024 * 1024) {
    error.value = 'Profile pictures must be 2 MB or smaller.'
    input.value = ''
    return
  }
  clearPreview()
  avatarFile.value = file
  avatarPreview.value = URL.createObjectURL(file)
}

async function loadProfile() {
  await auth.restore()
  const userId = auth.session.value?.user.id
  if (!userId) {
    await navigateTo('/auth/sign-in')
    return
  }
  const result = await api<{ profile: ProfileView; sharedCampaigns: SharedCampaign[] }>(`/api/v1/profiles/${userId}`)
  profile.value = result.profile
  campaigns.value = result.sharedCampaigns
  displayName.value = result.profile.display_name
  description.value = result.profile.description
}

async function saveProfile() {
  if (saving.value) return
  saving.value = true
  error.value = ''
  success.value = ''
  try {
    let avatarPath: string | undefined
    if (avatarFile.value) avatarPath = await auth.uploadAvatar(avatarFile.value)
    const body: Record<string, unknown> = {
      displayName: displayName.value.trim(),
      description: description.value.trim(),
    }
    if (avatarPath) body.avatarPath = avatarPath
    const result = await api<{ profile: ProfileView }>('/api/v1/profile', { method: 'PATCH', body })
    profile.value = result.profile
    displayName.value = result.profile.display_name
    description.value = result.profile.description
    await auth.updateUserMetadata({
      display_name: result.profile.display_name,
      avatar_url: result.profile.avatar_url,
    })
    clearPreview()
    success.value = 'Profile changes saved.'
  } catch (cause) {
    error.value = messageFrom(cause, 'Could not save your profile.')
  } finally {
    saving.value = false
  }
}

async function removePicture() {
  if (!profile.value?.avatar_url || removingAvatar.value) return
  removingAvatar.value = true
  error.value = ''
  success.value = ''
  try {
    await auth.removeAvatar()
    const result = await api<{ profile: ProfileView }>('/api/v1/profile', {
      method: 'PATCH', body: { avatarPath: null },
    })
    profile.value = result.profile
    await auth.updateUserMetadata({ avatar_url: null })
    clearPreview()
    success.value = 'Profile picture removed.'
  } catch (cause) {
    error.value = messageFrom(cause, 'Could not remove your profile picture.')
  } finally {
    removingAvatar.value = false
  }
}

async function changePassword() {
  error.value = ''
  success.value = ''
  if (password.value.length < 8) {
    error.value = 'Your new password must contain at least 8 characters.'
    return
  }
  if (password.value !== passwordConfirmation.value) {
    error.value = 'The password confirmation does not match.'
    return
  }
  changingPassword.value = true
  try {
    await auth.updatePassword(password.value)
    password.value = ''
    passwordConfirmation.value = ''
    success.value = 'Password updated securely.'
  } catch (cause) {
    error.value = messageFrom(cause, 'Could not update your password.')
  } finally {
    changingPassword.value = false
  }
}

async function leave() {
  await auth.signOut()
  await navigateTo('/')
}

onMounted(async () => {
  try {
    await loadProfile()
  } catch (cause) {
    error.value = messageFrom(cause, 'Could not load your profile.')
  } finally {
    loading.value = false
  }
})

onBeforeUnmount(clearPreview)
</script>

<template>
  <AppShell section="ACCOUNT">
    <div v-if="loading" class="profile-state noise"><i class="profile-spinner" /><p>OPENING YOUR ACCOUNT…</p></div>
    <div v-else-if="!profile" class="profile-state noise"><p class="eyebrow">PROFILE UNAVAILABLE</p><h1>THE RECORD IS MISSING.</h1><p>{{ error }}</p><NuxtLink to="/dashboard">BACK TO DASHBOARD</NuxtLink></div>
    <div v-else class="profile-page noise">
      <header class="profile-head">
        <div><p class="eyebrow">PLAYER RECORD / PRIVATE CONTROLS</p><h1>YOUR PROFILE<span>.</span></h1><p>Shape how your party sees you and keep your account secure.</p></div>
        <NuxtLink :to="`/profile/${profile.id}`">VIEW PUBLIC PROFILE ↗</NuxtLink>
      </header>

      <div v-if="error || success" class="profile-notice" :class="{success}" role="status">{{ error || success }}</div>

      <div class="profile-grid">
        <aside class="identity-card">
          <div class="avatar-stage">
            <span>{{ initials }}</span><img v-if="currentAvatar" :src="currentAvatar" alt="Current profile picture">
            <i>PLAYER<br>PORTRAIT</i>
          </div>
          <label class="upload-button">CHOOSE PICTURE<input type="file" accept="image/jpeg,image/png,image/webp" @change="chooseAvatar"></label>
          <button v-if="profile.avatar_url" class="remove-button" :disabled="removingAvatar" @click="removePicture">{{ removingAvatar ? 'REMOVING…' : 'REMOVE PICTURE' }}</button>
          <p>JPG, PNG, or WebP · 2 MB maximum.</p>
          <dl>
            <div><dt>REGISTERED</dt><dd>{{ new Date(profile.created_at).toLocaleDateString(undefined, { dateStyle: 'long' }) }}</dd></div>
            <div><dt>UNIVERSES</dt><dd>{{ campaigns.length }}</dd></div>
            <div><dt>ACCOUNT EMAIL</dt><dd>{{ auth.session.value?.user.email }}</dd></div>
          </dl>
        </aside>

        <div class="profile-forms">
          <form class="profile-panel" @submit.prevent="saveProfile">
            <header><span>01</span><div><small>PUBLIC IDENTITY</small><h2>NAME & DESCRIPTION</h2></div></header>
            <label>DISPLAY NAME<input v-model="displayName" required minlength="1" maxlength="80" autocomplete="name" placeholder="How your party knows you"></label>
            <label>PROFILE DESCRIPTION<textarea v-model="description" maxlength="500" rows="7" placeholder="Tell your party who you are, what you enjoy playing, or what kind of stories you seek." /><span>{{ description.length }} / 500</span></label>
            <button class="acid-button" :disabled="saving || !displayName.trim()">{{ saving ? 'SAVING PROFILE…' : avatarFile ? 'SAVE PROFILE & PICTURE →' : 'SAVE PROFILE →' }}</button>
          </form>

          <form class="profile-panel" @submit.prevent="changePassword">
            <header><span>02</span><div><small>SECURITY</small><h2>PASSWORD MANAGER</h2></div></header>
            <p class="panel-copy">Choose a new password for this account. Your current signed-in session authorizes the change.</p>
            <div class="password-grid"><label>NEW PASSWORD<input v-model="password" type="password" minlength="8" autocomplete="new-password" placeholder="At least 8 characters"></label><label>CONFIRM PASSWORD<input v-model="passwordConfirmation" type="password" minlength="8" autocomplete="new-password" placeholder="Repeat new password"></label></div>
            <button class="ghost-button" :disabled="changingPassword || !password">{{ changingPassword ? 'UPDATING…' : 'UPDATE PASSWORD' }}</button>
          </form>

          <section class="signout-panel"><div><small>SESSION CONTROL</small><h2>LEAVE THE GROUND</h2><p>Sign out on this device. Your universes and profile remain safely stored.</p></div><button @click="leave">SIGN OUT →</button></section>
        </div>
      </div>
    </div>
  </AppShell>
</template>

<style scoped>
.profile-page{min-height:calc(100vh - 76px);padding:clamp(38px,6vw,84px) clamp(20px,6vw,88px) 90px}.profile-head{display:flex;align-items:end;justify-content:space-between;gap:30px;margin-bottom:42px}.eyebrow{margin:0;color:var(--acid);font:500 8px var(--mono);letter-spacing:.18em}.profile-head h1,.profile-state h1{margin:15px 0 10px;font:600 clamp(42px,7vw,82px)/1 var(--display);letter-spacing:-.07em}.profile-head h1 span{color:var(--acid)}.profile-head>div>p:last-child,.profile-state>p{color:var(--muted)}.profile-head>a,.profile-state>a{padding-bottom:6px;border-bottom:1px solid var(--acid);color:var(--ink);text-decoration:none;font:600 8px var(--mono);letter-spacing:.1em}.profile-notice{margin-bottom:22px;padding:13px 16px;border:1px solid #6b372e;background:#221310;color:#ffab98;font:500 9px var(--mono)}.profile-notice.success{border-color:#526425;background:#151a0d;color:var(--acid)}.profile-grid{display:grid;grid-template-columns:minmax(240px,320px) minmax(0,1fr);gap:22px;align-items:start}.identity-card,.profile-panel,.signout-panel{border:1px solid var(--line);background:#0e0e0d}.identity-card{position:sticky;top:98px;padding:22px}.avatar-stage{position:relative;aspect-ratio:1;overflow:hidden;display:grid;place-items:center;background:radial-gradient(circle at 50% 45%,#3a411d,#11110e 52%,#080808);border:1px solid #37372f;color:var(--acid);font:600 48px var(--display)}.avatar-stage::after{content:"";position:absolute;inset:12px;border:1px solid rgba(217,247,95,.18);pointer-events:none}.avatar-stage img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}.avatar-stage>i{position:absolute;z-index:2;left:22px;bottom:20px;color:var(--ink);font:500 7px/1.5 var(--mono);letter-spacing:.16em;font-style:normal}.upload-button,.remove-button{width:100%;min-height:42px;display:grid;place-items:center;margin-top:10px;border:0;background:var(--acid);color:#090909;font:700 8px var(--mono);letter-spacing:.11em}.upload-button input{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}.remove-button{border:1px solid var(--line);background:transparent;color:var(--muted)}.identity-card>p{margin:12px 0 25px;color:var(--muted);font-size:9px}.identity-card dl{margin:0;border-top:1px solid var(--line)}.identity-card dl div{padding:14px 0;border-bottom:1px solid var(--line)}.identity-card dt{font:500 7px var(--mono);letter-spacing:.12em;color:var(--muted)}.identity-card dd{margin:6px 0 0;overflow-wrap:anywhere;font-size:11px}.profile-forms{display:grid;gap:22px}.profile-panel{padding:clamp(24px,4vw,46px)}.profile-panel header{display:flex;gap:18px;align-items:start;margin-bottom:34px}.profile-panel header>span{color:var(--acid);font:500 9px var(--mono)}.profile-panel small,.signout-panel small{color:var(--acid);font:500 7px var(--mono);letter-spacing:.15em}.profile-panel h2,.signout-panel h2{margin:8px 0 0;font:600 clamp(21px,3vw,33px) var(--display);letter-spacing:-.04em}.profile-panel>label,.password-grid label{display:grid;gap:9px;margin-top:18px;color:#b5b5ae;font:600 8px var(--mono);letter-spacing:.11em}.profile-panel input,.profile-panel textarea{width:100%;padding:14px;border:1px solid #383833;background:#090909;color:var(--ink);outline:none;font:12px/1.6 var(--body)}.profile-panel textarea{resize:vertical;min-height:150px}.profile-panel input:focus,.profile-panel textarea:focus{border-color:var(--acid)}.profile-panel label>span{justify-self:end;margin-top:-4px;color:var(--muted);font:500 7px var(--mono)}.acid-button,.ghost-button{min-height:46px;margin-top:22px;padding:0 20px;border:0;background:var(--acid);color:#090909;font:700 8px var(--mono);letter-spacing:.1em}.ghost-button{border:1px solid var(--line);background:transparent;color:var(--ink)}button:disabled{opacity:.45}.panel-copy{max-width:650px;color:var(--muted);font-size:11px;line-height:1.7}.password-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}.signout-panel{display:flex;align-items:center;justify-content:space-between;gap:30px;padding:28px 34px}.signout-panel p{margin:9px 0 0;color:var(--muted);font-size:10px}.signout-panel button{flex:0 0 auto;min-height:42px;padding:0 18px;border:1px solid #61352e;background:transparent;color:#ff9e88;font:600 8px var(--mono);letter-spacing:.1em}.profile-state{min-height:calc(100vh - 76px);display:grid;place-content:center;justify-items:center;text-align:center;padding:30px}.profile-spinner{width:24px;height:24px;border:2px solid var(--line);border-top-color:var(--acid);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}@media(max-width:850px){.profile-head{align-items:start;flex-direction:column}.profile-grid{grid-template-columns:1fr}.identity-card{position:static;display:grid;grid-template-columns:160px 1fr;column-gap:18px}.avatar-stage{grid-row:1/5}.identity-card dl{grid-column:1/-1;margin-top:12px}.password-grid{grid-template-columns:1fr}}@media(max-width:520px){.identity-card{display:block}.avatar-stage{margin-bottom:14px}.signout-panel{align-items:flex-start;flex-direction:column}.signout-panel button{width:100%}}
</style>
