<script setup lang="ts">
// 5-step wizard for provisioning a tenant from the operator — the operator
// counterpart of the partner portal's CustomerCreateWizard, sharing its step
// flow and result UX. Submit is two calls composed client-side: POST
// /api/tenants creates + provisions the tenant, then (when admin fields are
// filled) POST /api/users/invite-tenant-admin runs the same invite flow the
// partner wizard gets server-side. An invite failure never rolls back the
// tenant — the result view says exactly what happened.

const props = defineProps<{ open: boolean }>()
const emit = defineEmits<{ close: []; done: [] }>()

const STEPS = [
  { n: 1, label: 'Organization' },
  { n: 2, label: 'Domain' },
  { n: 3, label: 'First admin' },
  { n: 4, label: 'Plan' },
  { n: 5, label: 'Review' },
] as const
const LAST_STEP = STEPS.length

const step = ref(1)

const form = reactive({
  legalName: '',
  displayName: '',
  slug: '',
  slugTouched: false,
  cvr: '',
  country: '',
  domain: '',
  adminFirst: '',
  adminLast: '',
  adminEmail: '',
  plan: 'Business' as PlanLabel,
  seats: 5,
  cycle: 'Monthly' as 'Monthly' | 'Quarterly' | 'Yearly',
  currency: 'DKK' as 'DKK' | 'EUR' | 'USD',
})

const plans = [
  { code: 'mvp',        name: 'Starter',    features: '10 GB mail · 100 GB drive · 5 video rooms' },
  { code: 'pro',        name: 'Business',   features: '50 GB mail · 1 TB drive · unlimited video · MFA', best: true },
  { code: 'enterprise', name: 'Enterprise', features: 'Custom quotas · SSO · audit log · 24/7 support' },
] as const

type PlanLabel = (typeof plans)[number]['name']
type PlanCode = (typeof plans)[number]['code']

interface CatalogRow {
  plan: PlanCode
  cycle: 'monthly' | 'quarterly' | 'yearly'
  amounts: { DKK?: number; EUR?: number; USD?: number }
  active: boolean
}
const { data: catalog } = useLazyFetch<CatalogRow[]>('/api/prices', {
  key: 'tenant-wizard-catalog',
  default: () => [],
  server: false,
})

const CYCLE_SUFFIX: Record<'monthly' | 'quarterly' | 'yearly', string> = {
  monthly: 'mo',
  quarterly: 'quarter',
  yearly: 'yr',
}

// Slug auto-derives from the display name until the operator edits it.
function slugFromName(name: string): string {
  return name
    .toLowerCase()
    .normalize('NFKD')
    .replace(/[̀-ͯ]/g, '')
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
    .slice(0, 40)
}
watch(
  () => form.displayName,
  (v) => {
    if (!form.slugTouched) form.slug = slugFromName(v)
  },
)

function reset() {
  step.value = 1
  result.value = null
  submitError.value = null
  Object.assign(form, {
    legalName: '', displayName: '', slug: '', slugTouched: false, cvr: '',
    country: '', domain: '', adminFirst: '', adminLast: '', adminEmail: '',
    plan: 'Business', seats: 5, cycle: 'Monthly', currency: 'DKK',
  })
}

function close() {
  emit('close')
  setTimeout(reset, 200)
}

function next() {
  if (step.value < LAST_STEP) step.value++
}
function back() {
  if (step.value > 1) step.value--
}

const submitting = ref(false)
const submitError = ref<string | null>(null)

function planCode(label: PlanLabel): PlanCode {
  return plans.find((p) => p.name === label)!.code
}
function cycleCode(label: 'Monthly' | 'Quarterly' | 'Yearly'): 'monthly' | 'quarterly' | 'yearly' {
  return label.toLowerCase() as 'monthly' | 'quarterly' | 'yearly'
}

const visiblePlans = computed(() =>
  plans.map((p) => {
    const row = catalog.value.find((r) => r.plan === p.code && r.cycle === cycleCode(form.cycle))
    const minor = row?.amounts[form.currency]
    const cycleSuffix = CYCLE_SUFFIX[cycleCode(form.cycle)]
    let priceLabel: string
    let available = true
    if (p.code === 'enterprise' && minor === undefined) {
      priceLabel = 'Custom'
    } else if (minor === undefined) {
      priceLabel = `Not sold in ${form.currency}`
      available = false
    } else {
      priceLabel = `${(minor / 100).toLocaleString('da-DK')} ${form.currency} / seat / ${cycleSuffix}`
    }
    return { ...p, priceLabel, available }
  }),
)

const totalPerCycle = computed(() => {
  const p = visiblePlans.value.find((x) => x.name === form.plan)
  if (!p || !p.available) return null
  const row = catalog.value.find((r) => r.plan === p.code && r.cycle === cycleCode(form.cycle))
  const minor = row?.amounts[form.currency]
  if (minor === undefined || !form.seats) return null
  const total = (minor * form.seats) / 100
  return `${total.toLocaleString('da-DK')} ${form.currency} / ${CYCLE_SUFFIX[cycleCode(form.cycle)]}`
})

interface AdminCredentials {
  link?: string
  tempPassword?: string
  attached?: boolean
  error?: string
}
const result = ref<{
  tenantName: string
  tenantSlug: string
  adminEmail: string
  admin?: AdminCredentials
} | null>(null)
const copied = ref(false)

async function copyToClipboard(value: string) {
  try {
    await navigator.clipboard.writeText(value)
    copied.value = true
    setTimeout(() => (copied.value = false), 2000)
  } catch {
    // Non-secure context — user selects the readonly input instead.
  }
}

async function submit() {
  submitError.value = null
  const displayName = form.displayName.trim()
  const slug = form.slug.trim()
  if (!displayName || !slug) {
    submitError.value = 'Display name and slug are required'
    step.value = 1
    return
  }
  submitting.value = true
  try {
    const adminName = `${form.adminFirst.trim()} ${form.adminLast.trim()}`.trim()
    const adminEmail = form.adminEmail.trim()
    const domain = form.domain.trim().toLowerCase()
    const tenant = await $fetch<{ name: string; slug: string }>('/api/tenants', {
      method: 'POST',
      body: {
        slug,
        name: displayName,
        plan: planCode(form.plan),
        cycle: cycleCode(form.cycle),
        currency: form.currency,
        seats: form.seats,
        ...(domain && { domains: [domain] }),
        billingInfo: {
          ...(form.legalName.trim() && { companyName: form.legalName.trim() }),
          ...(form.cvr.trim() && { vatId: form.cvr.trim() }),
          ...(form.country.trim() && { country: form.country.trim().toUpperCase().slice(0, 2) }),
          ...(adminEmail && { contactEmail: adminEmail }),
        },
      },
    })

    // The tenant exists from here on — an invite failure is reported, never
    // rolled back (same contract as the partner wizard).
    let admin: AdminCredentials | undefined
    if (adminName && adminEmail) {
      try {
        admin = await $fetch<AdminCredentials>('/api/users/invite-tenant-admin', {
          method: 'POST',
          body: { tenantSlug: tenant.slug, name: adminName, email: adminEmail },
        })
      } catch (err: unknown) {
        const e = err as { data?: { data?: { message?: string }; message?: string } }
        admin = { error: e.data?.data?.message || e.data?.message || String(err) }
      }
    }

    result.value = { tenantName: tenant.name, tenantSlug: tenant.slug, adminEmail, admin }
    emit('done')
  } catch (err: unknown) {
    const e = err as { data?: { data?: { message?: string | string[] }; message?: string; statusMessage?: string } }
    const msg = e.data?.data?.message || e.data?.message || e.data?.statusMessage || 'Provisioning failed'
    submitError.value = Array.isArray(msg) ? msg.join(' · ') : msg
  } finally {
    submitting.value = false
  }
}

async function finish() {
  const slug = result.value?.tenantSlug
  result.value = null
  emit('close')
  if (slug) await navigateTo(`/tenants/${slug}`)
}

onMounted(() => {
  const onKey = (e: KeyboardEvent) => {
    if (e.key === 'Escape' && props.open && !submitting.value) close()
  }
  document.addEventListener('keydown', onKey)
  onBeforeUnmount(() => document.removeEventListener('keydown', onKey))
})
</script>

<template>
  <Teleport to="body">
    <div v-if="open" class="backdrop" @click="!submitting && close()">
      <div class="modal" role="dialog" aria-label="New tenant" @click.stop>
        <header>
          <div>
            <Eyebrow>{{ result ? 'Provisioned' : `Step ${step} of ${STEPS.length}` }}</Eyebrow>
            <h2>Provision new tenant</h2>
          </div>
          <button class="x" type="button" aria-label="Close" :disabled="submitting" @click="close">
            <UiIcon name="x" :size="12" />
          </button>
        </header>

        <div class="body">
          <!-- Step rail -->
          <div v-if="!result" class="rail">
            <template v-for="(s, idx) in STEPS" :key="s.n">
              <div class="rail-step" :class="{ done: s.n < step, active: s.n === step }">
                <div class="bubble">
                  <UiIcon v-if="s.n < step" name="check" :size="11" :stroke-width="2.6" />
                  <template v-else>{{ s.n }}</template>
                </div>
                <span class="lab">{{ s.label }}</span>
              </div>
              <div v-if="idx < STEPS.length - 1" class="rail-line" />
            </template>
          </div>

          <!-- 1. Organization -->
          <div v-if="step === 1 && !result" class="form">
            <label class="field">
              <span class="label">Display name · shown to users</span>
              <input v-model="form.displayName" placeholder="e.g. Dezky ApS" />
            </label>
            <label class="field">
              <span class="label">Slug · URL-safe id, also the Authentik group</span>
              <input v-model="form.slug" placeholder="e.g. dezky" @input="form.slugTouched = true" />
            </label>
            <label class="field">
              <span class="label">Legal name · billing</span>
              <input v-model="form.legalName" placeholder="e.g. Dezky ApS" />
            </label>
            <div class="row-2">
              <label class="field">
                <span class="label">CVR / VAT id</span>
                <input v-model="form.cvr" />
              </label>
              <label class="field">
                <span class="label">Country · ISO-2</span>
                <input v-model="form.country" placeholder="DK" maxlength="2" />
              </label>
            </div>
          </div>

          <!-- 2. Domain -->
          <div v-if="step === 2 && !result" class="form">
            <label class="field">
              <span class="label">Primary mail domain · optional</span>
              <input v-model="form.domain" placeholder="e.g. dezky.eu" />
            </label>
            <div class="info-box">
              <span class="label">DNS verification</span>
              <p>
                This only records the intent on the tenant. The customer admin adds and
                verifies the domain on the portal's Domains page, which hands them the
                exact MX/SPF/DKIM/DMARC records to publish.
              </p>
            </div>
          </div>

          <!-- 3. First admin -->
          <div v-if="step === 3 && !result" class="form">
            <p class="hint">
              Optional — creates (or attaches) this person as the tenant's first admin and
              returns a single-use credential to share. Skip it to invite someone later.
            </p>
            <div class="row-2">
              <label class="field">
                <span class="label">First name</span>
                <input v-model="form.adminFirst" />
              </label>
              <label class="field">
                <span class="label">Last name</span>
                <input v-model="form.adminLast" />
              </label>
            </div>
            <label class="field">
              <span class="label">Email</span>
              <input v-model="form.adminEmail" type="email" />
            </label>
          </div>

          <!-- 4. Plan -->
          <div v-if="step === 4 && !result" class="form">
            <div class="row-3">
              <label class="field">
                <span class="label">Initial seats</span>
                <input v-model.number="form.seats" type="number" min="0" max="10000" />
              </label>
              <label class="field">
                <span class="label">Billing cycle</span>
                <select v-model="form.cycle">
                  <option value="Monthly">Monthly</option>
                  <option value="Quarterly">Quarterly</option>
                  <option value="Yearly">Yearly</option>
                </select>
              </label>
              <label class="field">
                <span class="label">Currency</span>
                <select v-model="form.currency">
                  <option value="DKK">DKK</option>
                  <option value="EUR">EUR</option>
                  <option value="USD">USD</option>
                </select>
              </label>
            </div>

            <label
              v-for="p in visiblePlans"
              :key="p.name"
              class="plan"
              :class="{ selected: form.plan === p.name, disabled: !p.available }"
              @click="p.available && (form.plan = p.name as PlanLabel)"
            >
              <span v-if="'best' in p && p.best" class="rec">RECOMMENDED</span>
              <span class="radio" :class="{ on: form.plan === p.name }">
                <span v-if="form.plan === p.name" class="radio-inner" />
              </span>
              <div class="plan-body">
                <div class="plan-head">
                  <span class="plan-name">{{ p.name }}</span>
                  <Mono dim>{{ p.priceLabel }}</Mono>
                </div>
                <Mono dim>{{ p.features }}</Mono>
              </div>
            </label>

            <p v-if="totalPerCycle" class="total-line">
              <Mono dim>Total · {{ form.seats }} {{ form.seats === 1 ? 'seat' : 'seats' }}</Mono>
              <strong>{{ totalPerCycle }}</strong>
            </p>
          </div>

          <!-- 5. Review -->
          <div v-if="step === 5 && !result" class="form">
            <div class="review-hero">
              <span class="label">You're provisioning</span>
              <div class="review-name">{{ form.displayName || '—' }}</div>
              <Mono dim>{{ form.slug }} · {{ form.domain || 'no domain yet' }} · {{ form.plan }} · {{ form.seats }} seats</Mono>
            </div>
            <dl class="def">
              <div class="def-row">
                <dt>Admin</dt>
                <dd>{{ form.adminEmail ? `${form.adminFirst} ${form.adminLast} · ${form.adminEmail}` : 'none — invite later' }}</dd>
              </div>
              <div class="def-row"><dt>Plan</dt><dd>{{ form.plan }} · {{ form.seats }} seats · {{ form.cycle.toLowerCase() }} · {{ form.currency }}</dd></div>
              <div class="def-row"><dt>Billing</dt><dd>{{ form.legalName || form.displayName }}{{ form.cvr ? ` · ${form.cvr}` : '' }}{{ form.country ? ` · ${form.country.toUpperCase()}` : '' }}</dd></div>
            </dl>
            <div class="info-box">
              <Mono dim>// provisioning</Mono>
              <p>
                On confirm the tenant is created and provisioned (Authentik group, Stalwart
                service domain; OCIS once the files tier is live), the subscription is
                spun up from the price catalog, and the first admin is invited.
              </p>
            </div>
          </div>

          <p v-if="submitError" class="err">{{ submitError }}</p>

          <!-- Provisioned: credentials handoff -->
          <div v-if="result" class="provisioned">
            <Badge tone="ok" dot>provisioned</Badge>
            <h3>{{ result.tenantName }} is live</h3>

            <template v-if="result.admin && !result.admin.error">
              <template v-if="result.admin.attached">
                <p class="ok-msg">
                  <Mono>{{ result.adminEmail }}</Mono> already existed in Authentik and was
                  attached as an admin on this tenant. They sign in with their existing
                  credentials.
                </p>
              </template>
              <template v-else-if="result.admin.link">
                <p class="ok-msg">
                  Share this single-use link with the admin — they'll set their own
                  password and enroll MFA.
                </p>
                <div class="cred-row">
                  <input :value="result.admin.link" readonly @focus="($event.target as HTMLInputElement).select()" />
                  <UiButton variant="secondary" @click="copyToClipboard(result.admin!.link!)">
                    {{ copied ? 'Copied' : 'Copy' }}
                  </UiButton>
                </div>
              </template>
              <template v-else-if="result.admin.tempPassword">
                <p class="ok-msg">
                  Authentik has no recovery flow configured, so a temporary password was
                  set — share it with the admin; they'll change it on first login.
                </p>
                <div class="cred-row">
                  <input :value="result.adminEmail" readonly @focus="($event.target as HTMLInputElement).select()" />
                  <UiButton variant="secondary" @click="copyToClipboard(result.adminEmail)">Copy</UiButton>
                </div>
                <div class="cred-row">
                  <input :value="result.admin.tempPassword" readonly @focus="($event.target as HTMLInputElement).select()" />
                  <UiButton variant="secondary" @click="copyToClipboard(result.admin!.tempPassword!)">
                    {{ copied ? 'Copied' : 'Copy' }}
                  </UiButton>
                </div>
              </template>
            </template>

            <template v-else-if="result.admin?.error">
              <p class="warn-msg">
                Tenant was created, but the admin invite failed:
                <Mono>{{ result.admin.error }}</Mono>. Retry from the tenant page's
                "Invite admin" action.
              </p>
            </template>

            <template v-else>
              <p class="ok-msg">
                No first-admin info was provided. Invite an admin from the tenant page
                whenever you're ready.
              </p>
            </template>
          </div>
        </div>

        <footer>
          <template v-if="!result">
            <UiButton variant="ghost" :disabled="submitting" @click="close">Cancel</UiButton>
            <div style="flex: 1" />
            <UiButton v-if="step > 1" variant="secondary" :disabled="submitting" @click="back">Back</UiButton>
            <UiButton v-if="step < LAST_STEP" variant="primary" @click="next">Continue</UiButton>
            <UiButton v-else variant="primary" :disabled="submitting" @click="submit">
              <template #leading><UiIcon name="check" :size="14" /></template>
              {{ submitting ? 'Provisioning…' : 'Provision tenant' }}
            </UiButton>
          </template>
          <template v-else>
            <div style="flex: 1" />
            <UiButton variant="primary" @click="finish">Open tenant</UiButton>
          </template>
        </footer>
      </div>
    </div>
  </Teleport>
</template>

<style scoped>
.backdrop {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.55);
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  z-index: 180;
}
.modal {
  width: 100%; max-width: 640px;
  max-height: calc(100vh - 48px);
  background: var(--elevated);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.5);
  display: flex; flex-direction: column;
  overflow: hidden;
}
header {
  padding: 16px 20px;
  display: flex; justify-content: space-between; align-items: flex-start;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
h2 { margin: 4px 0 0 0; font-family: var(--font-display); font-weight: 600; font-size: 18px; }
.x {
  width: 26px; height: 26px;
  border: 0; border-radius: 6px; background: transparent;
  color: var(--text-mute); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.x:hover:not(:disabled) { background: var(--surface); color: var(--text); }
.x:disabled { opacity: 0.4; cursor: not-allowed; }

.body { padding: 18px 20px; overflow-y: auto; }
footer {
  padding: 14px 20px;
  display: flex; gap: 8px; align-items: center;
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}

.rail { display: flex; align-items: center; gap: 6px; margin-bottom: 20px; }
.rail-step { display: flex; align-items: center; gap: 8px; opacity: 0.45; }
.rail-step.active, .rail-step.done { opacity: 1; }
.bubble {
  width: 22px; height: 22px; border-radius: 999px;
  background: var(--surface); color: var(--text-mute);
  border: 1px solid var(--border);
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-size: 11px; font-weight: 600;
}
.rail-step.done .bubble { background: var(--text); color: var(--bg); border-color: var(--text); }
.rail-step.active .bubble { background: var(--accent); color: var(--accent-fg, var(--bg)); border-color: var(--accent); }
.lab { font-size: 12px; font-weight: 500; color: var(--text-mute); white-space: nowrap; }
.rail-step.active .lab, .rail-step.done .lab { color: var(--text); }
.rail-step.active .lab { font-weight: 600; }
.rail-line { flex: 1; height: 1px; background: var(--border); }

.form { display: flex; flex-direction: column; gap: 14px; }
.row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }

.field { display: flex; flex-direction: column; gap: 6px; }
.label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
  font-weight: 500;
}
.field input,
.field select {
  height: 34px;
  padding: 0 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  outline: none;
}
.field input:focus,
.field select:focus { border-color: var(--accent); }

.hint { font-size: 13px; color: var(--text-dim); margin: 0; line-height: 1.5; }

.info-box {
  padding: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.info-box p { font-size: 13px; color: var(--text-dim); margin: 8px 0 0 0; line-height: 1.55; }

.plan {
  position: relative;
  display: flex; align-items: center; gap: 14px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  cursor: pointer;
}
.plan.selected { border-color: var(--text); background: var(--bg); }
.plan.disabled { opacity: 0.45; cursor: not-allowed; }
.rec {
  position: absolute; top: -8px; right: 12px;
  background: var(--accent); color: var(--accent-fg, var(--bg));
  font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  padding: 2px 8px; border-radius: 3px; letter-spacing: 0.06em;
}
.radio {
  width: 18px; height: 18px; border-radius: 999px;
  border: 2px solid var(--border);
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.radio.on { border-color: var(--text); }
.radio-inner { width: 8px; height: 8px; border-radius: 999px; background: var(--text); }
.plan-body { flex: 1; display: flex; flex-direction: column; gap: 2px; }
.plan-head { display: flex; align-items: baseline; gap: 10px; }
.plan-name { font-family: var(--font-display); font-size: 16px; font-weight: 600; }

.total-line {
  display: flex; justify-content: space-between; align-items: baseline;
  margin: 0;
  padding: 12px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.total-line strong { font-family: var(--font-display); font-size: 16px; font-weight: 600; }

.review-hero {
  padding: 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.review-name {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 6px 0 4px 0;
}

.def { display: flex; flex-direction: column; gap: 8px; margin: 0; }
.def-row { display: grid; grid-template-columns: 120px 1fr; gap: 12px; font-size: 13px; }
.def-row dt { color: var(--text-mute); font-family: var(--font-mono); font-size: 11px; padding-top: 1px; }
.def-row dd { margin: 0; color: var(--text); }

.err {
  margin: 12px 0 0;
  padding: 10px 12px;
  font-size: 12px;
  color: var(--bad);
  background: rgba(240, 88, 88, 0.08);
  border: 1px solid rgba(240, 88, 88, 0.24);
  border-radius: 6px;
}

.provisioned { display: flex; flex-direction: column; gap: 14px; padding: 4px 0; }
.provisioned h3 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 22px;
  letter-spacing: -0.02em;
  margin: 0;
}
.ok-msg, .warn-msg { margin: 0; font-size: 13px; color: var(--text-dim); line-height: 1.55; }
.warn-msg {
  padding: 10px 12px;
  background: rgba(232, 154, 31, 0.08);
  border: 1px solid rgba(232, 154, 31, 0.24);
  border-radius: 6px;
  color: var(--warn);
}
.cred-row { display: flex; align-items: center; gap: 8px; }
.cred-row input {
  flex: 1;
  height: 34px;
  padding: 0 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text);
  outline: none;
}
</style>
