<script setup lang="ts">
import type { Partner, PartnerStatus } from '~/types/partner'
import type { Tenant } from '~/types/tenant'

const route = useRoute()
const slug = computed(() => route.params.slug as string)

const { data: partner, refresh: refreshPartner } = await useFetch<Partner>(
  () => `/api/partners/${slug.value}`,
  { watch: [slug] },
)

const { data: customers, refresh: refreshCustomers } = await useFetch<Tenant[]>(
  () => `/api/partners/${slug.value}/tenants`,
  { default: () => [], watch: [slug] },
)

const STATUS_TONE: Record<PartnerStatus, 'ok' | 'warn' | 'neutral' | 'bad'> = {
  active: 'ok',
  'in-negotiation': 'warn',
  paused: 'neutral',
  terminated: 'bad',
}
const STATUSES: PartnerStatus[] = ['in-negotiation', 'active', 'paused', 'terminated']

const TENANT_STATUS_TONE = {
  active: 'ok', pending: 'warn', suspended: 'bad', deleted: 'neutral',
} as const

// ── Inline edit mode ──────────────────────────────────────────────────────
// Cards flip from read-only display to inputs when `editing` is true. The
// draft object shadows the live partner data; save() PATCHes only the diff
// so backend audit metadata stays accurate (the partner.updated event lists
// only fields the operator actually changed). Cancel discards the draft.

interface PartnerDraft {
  name: string
  domain: string
  status: PartnerStatus
  marginPct: number
  contactInfo: {
    primaryName: string
    primaryEmail: string
    billingEmail: string
  }
  billingInfo: {
    companyName: string
    vatId: string
    country: string
    contactEmail: string
  }
}

const editing = ref(false)
const draft = ref<PartnerDraft | null>(null)
const saving = ref(false)
const saveError = ref<string | null>(null)

function cloneEditable(p: Partner): PartnerDraft {
  return {
    name: p.name,
    domain: p.domain,
    status: p.status,
    marginPct: p.marginPct,
    contactInfo: {
      primaryName: p.contactInfo?.primaryName ?? '',
      primaryEmail: p.contactInfo?.primaryEmail ?? '',
      billingEmail: p.contactInfo?.billingEmail ?? '',
    },
    billingInfo: {
      companyName: p.billingInfo?.companyName ?? '',
      vatId: p.billingInfo?.vatId ?? '',
      country: p.billingInfo?.country ?? '',
      contactEmail: p.billingInfo?.contactEmail ?? '',
    },
  }
}

// Build a PATCH body containing only fields that differ from the server
// state. Returns null when nothing changed. Nested objects (contactInfo /
// billingInfo) are included whole when any child key changed — Authentik's
// backend update path replaces nested objects rather than merging keys, so
// sending the full subobject is the safe contract.
function partnerDiff(orig: Partner, next: PartnerDraft): Record<string, unknown> | null {
  const out: Record<string, unknown> = {}
  if (orig.name !== next.name) out.name = next.name
  if (orig.domain !== next.domain) out.domain = next.domain
  if (orig.status !== next.status) out.status = next.status
  if (orig.marginPct !== next.marginPct) out.marginPct = next.marginPct
  const ci = orig.contactInfo ?? {}
  if (
    (ci.primaryName ?? '') !== next.contactInfo.primaryName ||
    (ci.primaryEmail ?? '') !== next.contactInfo.primaryEmail ||
    (ci.billingEmail ?? '') !== next.contactInfo.billingEmail
  ) {
    out.contactInfo = next.contactInfo
  }
  const bi = orig.billingInfo ?? {}
  if (
    (bi.companyName ?? '') !== next.billingInfo.companyName ||
    (bi.vatId ?? '') !== next.billingInfo.vatId ||
    (bi.country ?? '') !== next.billingInfo.country ||
    (bi.contactEmail ?? '') !== next.billingInfo.contactEmail
  ) {
    out.billingInfo = next.billingInfo
  }
  return Object.keys(out).length ? out : null
}

const dirty = computed(() => {
  if (!editing.value || !draft.value || !partner.value) return false
  return partnerDiff(partner.value, draft.value) !== null
})

// Client-side validation. Backend re-validates via DTO decorators — this is
// just to keep the Save button honest about whether a submit would succeed.
const valid = computed(() => {
  if (!draft.value) return false
  const d = draft.value
  if (d.name.length < 2 || d.name.length > 120) return false
  if (d.domain.length < 3 || d.domain.length > 120) return false
  if (d.marginPct < 0 || d.marginPct > 100 || !Number.isInteger(d.marginPct)) return false
  if (d.billingInfo.vatId.length > 40) return false
  if (d.billingInfo.country.length !== 0 && d.billingInfo.country.length !== 2) return false
  return true
})

function startEdit() {
  if (!partner.value) return
  draft.value = cloneEditable(partner.value)
  editing.value = true
  saveError.value = null
}

function cancelEdit() {
  if (dirty.value) {
    const ok = window.confirm('Discard unsaved changes?')
    if (!ok) return
  }
  editing.value = false
  draft.value = null
  saveError.value = null
}

async function save() {
  if (!partner.value || !draft.value) return
  const patch = partnerDiff(partner.value, draft.value)
  if (!patch) return
  saving.value = true
  saveError.value = null
  try {
    await $fetch(`/api/partners/${slug.value}`, { method: 'PATCH', body: patch })
    await refreshPartner()
    editing.value = false
    draft.value = null
  } catch (err: unknown) {
    const e = err as { data?: { data?: { message?: string | string[] }; message?: string } }
    const m = e.data?.data?.message ?? e.data?.message
    saveError.value = Array.isArray(m) ? m.join('; ') : (m ?? 'Save failed')
  } finally {
    saving.value = false
  }
}

// ── Attach tenant modal ───────────────────────────────────────────────────
const attachOpen = ref(false)
const attachBusy = ref(false)
const attachError = ref<string | null>(null)
const selectedSlug = ref('')

const { data: allTenants } = await useFetch<Tenant[]>('/api/tenants', { default: () => [] })

const attachable = computed(() => {
  return (allTenants.value ?? []).filter(
    (t) => !t.partnerId && t.status !== 'deleted',
  )
})

function openAttach() {
  selectedSlug.value = attachable.value[0]?.slug ?? ''
  attachError.value = null
  attachOpen.value = true
}

async function submitAttach() {
  if (!selectedSlug.value || !partner.value) return
  attachBusy.value = true
  attachError.value = null
  try {
    await $fetch(`/api/tenants/${selectedSlug.value}`, {
      method: 'PATCH',
      body: { partnerId: partner.value._id },
    })
    attachOpen.value = false
    await Promise.all([refreshPartner(), refreshCustomers()])
  } catch (err: unknown) {
    const e = err as { data?: { data?: { message?: string }; message?: string } }
    attachError.value = e.data?.data?.message ?? e.data?.message ?? String(err)
  } finally {
    attachBusy.value = false
  }
}

// ── Detach confirm ────────────────────────────────────────────────────────
const detachTarget = ref<Tenant | null>(null)
const detachBusy = ref(false)
const detachError = ref<string | null>(null)

async function confirmDetach() {
  if (!detachTarget.value) return
  detachBusy.value = true
  detachError.value = null
  try {
    await $fetch(`/api/tenants/${detachTarget.value.slug}`, {
      method: 'PATCH',
      body: { partnerId: null },
    })
    detachTarget.value = null
    await Promise.all([refreshPartner(), refreshCustomers()])
  } catch (err: unknown) {
    const e = err as { data?: { data?: { message?: string }; message?: string } }
    detachError.value = e.data?.data?.message ?? e.data?.message ?? String(err)
  } finally {
    detachBusy.value = false
  }
}

// ── Soft-terminate partner ────────────────────────────────────────────────
const terminateOpen = ref(false)
const terminateBusy = ref(false)
const terminateError = ref<string | null>(null)

async function confirmTerminate() {
  terminateBusy.value = true
  terminateError.value = null
  try {
    await $fetch(`/api/partners/${slug.value}`, { method: 'DELETE' })
    await navigateTo('/partners')
  } catch (err: unknown) {
    const e = err as { data?: { data?: { message?: string }; message?: string } }
    terminateError.value = e.data?.data?.message ?? e.data?.message ?? String(err)
    terminateBusy.value = false
  }
}
</script>

<template>
  <div v-if="partner">
    <PageHeader
      :eyebrow="`Partner · ${partner.slug}`"
      :title="partner.name"
      :subtitle="`${partner.customers} customer(s) · ${partner.marginPct}% revenue share · partner since ${partner.partnershipStartedAt ? new Date(partner.partnershipStartedAt).toISOString().slice(0, 10) : new Date(partner.createdAt).toISOString().slice(0, 10)}`"
    >
      <template #actions>
        <Badge :tone="STATUS_TONE[partner.status]" dot>{{ partner.status }}</Badge>
        <template v-if="!editing">
          <UiButton variant="secondary">
            <template #leading><UiIcon name="external" :size="13" /></template>
            Open {{ partner.domain }}
          </UiButton>
          <UiButton variant="primary" @click="startEdit">
            <template #leading><UiIcon name="more" :size="13" /></template>
            Edit
          </UiButton>
        </template>
        <template v-else>
          <UiButton variant="ghost" :disabled="saving" @click="cancelEdit">Cancel</UiButton>
          <UiButton
            variant="primary"
            :disabled="!dirty || !valid || saving"
            @click="save"
          >
            {{ saving ? 'Saving…' : 'Save changes' }}
          </UiButton>
        </template>
      </template>
    </PageHeader>

    <div class="stage">
      <div class="grid">
        <Card>
          <h2>Contract</h2>
          <!-- Read-only view -->
          <dl v-if="!editing || !draft">
            <div class="dl-row"><dt>Slug</dt><dd><Mono>{{ partner.slug }}</Mono></dd></div>
            <div class="dl-row"><dt>Name</dt><dd>{{ partner.name }}</dd></div>
            <div class="dl-row"><dt>Domain</dt><dd><Mono>{{ partner.domain }}</Mono></dd></div>
            <div class="dl-row"><dt>Status</dt><dd><Badge :tone="STATUS_TONE[partner.status]" dot>{{ partner.status }}</Badge></dd></div>
            <div class="dl-row"><dt>Margin</dt><dd><Mono>{{ partner.marginPct }}%</Mono></dd></div>
            <div class="dl-row"><dt>Customers</dt><dd>
              <span class="num">{{ partner.customers }}</span>
              <Mono dim>(MRR aggregation ships when Subscription gains pricing)</Mono>
            </dd></div>
          </dl>
          <!-- Edit view -->
          <dl v-else>
            <div class="dl-row"><dt>Slug</dt><dd><Mono>{{ partner.slug }}</Mono><Mono dim>(immutable)</Mono></dd></div>
            <div class="dl-row">
              <dt>Name</dt>
              <dd><input v-model="draft.name" class="field" type="text" maxlength="120" :disabled="saving" /></dd>
            </div>
            <div class="dl-row">
              <dt>Domain</dt>
              <dd><input v-model="draft.domain" class="field" type="text" maxlength="120" :disabled="saving" /></dd>
            </div>
            <div class="dl-row">
              <dt>Status</dt>
              <dd>
                <div class="seg four">
                  <button
                    v-for="s in STATUSES"
                    :key="s"
                    type="button"
                    :class="{ on: draft.status === s }"
                    :disabled="saving"
                    @click="draft.status = s"
                  >{{ s }}</button>
                </div>
              </dd>
            </div>
            <div class="dl-row">
              <dt>Margin</dt>
              <dd class="slider-cell">
                <input
                  v-model.number="draft.marginPct"
                  type="range"
                  min="0"
                  max="100"
                  step="1"
                  class="slider"
                  :disabled="saving"
                />
                <Mono class="pct">{{ draft.marginPct }}%</Mono>
              </dd>
            </div>
          </dl>
        </Card>

        <Card>
          <h2>Contact</h2>
          <!-- Read-only view -->
          <dl v-if="!editing || !draft">
            <div class="dl-row"><dt>Primary</dt><dd>{{ partner.contactInfo.primaryName || '—' }}</dd></div>
            <div class="dl-row"><dt>Email</dt><dd><Mono :dim="!partner.contactInfo.primaryEmail">{{ partner.contactInfo.primaryEmail || '—' }}</Mono></dd></div>
            <div class="dl-row"><dt>Billing</dt><dd><Mono :dim="!partner.contactInfo.billingEmail">{{ partner.contactInfo.billingEmail || '—' }}</Mono></dd></div>
            <div class="dl-row"><dt>Company</dt><dd>{{ partner.billingInfo.companyName || '—' }}</dd></div>
            <div class="dl-row"><dt>VAT</dt><dd><Mono :dim="!partner.billingInfo.vatId">{{ partner.billingInfo.vatId || '—' }}</Mono></dd></div>
            <div class="dl-row"><dt>Country</dt><dd><Mono :dim="!partner.billingInfo.country">{{ partner.billingInfo.country || '—' }}</Mono></dd></div>
          </dl>
          <!-- Edit view -->
          <dl v-else>
            <div class="dl-row">
              <dt>Primary</dt>
              <dd><input v-model="draft.contactInfo.primaryName" class="field" type="text" maxlength="200" :disabled="saving" /></dd>
            </div>
            <div class="dl-row">
              <dt>Email</dt>
              <dd><input v-model="draft.contactInfo.primaryEmail" class="field mono" type="email" :disabled="saving" /></dd>
            </div>
            <div class="dl-row">
              <dt>Billing</dt>
              <dd><input v-model="draft.contactInfo.billingEmail" class="field mono" type="email" :disabled="saving" /></dd>
            </div>
            <div class="dl-row">
              <dt>Company</dt>
              <dd><input v-model="draft.billingInfo.companyName" class="field" type="text" maxlength="200" :disabled="saving" /></dd>
            </div>
            <div class="dl-row">
              <dt>VAT</dt>
              <dd><input v-model="draft.billingInfo.vatId" class="field mono" type="text" maxlength="40" :disabled="saving" /></dd>
            </div>
            <div class="dl-row">
              <dt>Country</dt>
              <dd><input v-model="draft.billingInfo.country" class="field mono country" type="text" maxlength="2" placeholder="DK" :disabled="saving" /></dd>
            </div>
          </dl>
        </Card>
      </div>

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

      <Card :pad="0">
        <div class="card-head padded">
          <div>
            <h2>Customers</h2>
            <p class="hint">Tenants whose <code>partnerId</code> points at this partner.</p>
          </div>
          <UiButton variant="primary" :disabled="attachable.length === 0" @click="openAttach">
            <template #leading><UiIcon name="plus" :size="13" /></template>
            Attach tenant
          </UiButton>
        </div>
        <table>
          <thead>
            <tr>
              <th>Tenant</th><th>Status</th><th>Plan</th><th>Domains</th><th class="th-right">Actions</th>
            </tr>
          </thead>
          <tbody>
            <tr v-if="(customers ?? []).length === 0" class="empty">
              <td colspan="5">
                <span class="empty-inner">No customers under this partner yet.</span>
              </td>
            </tr>
            <tr v-for="t in (customers ?? [])" :key="t._id">
              <td>
                <NuxtLink class="link" :to="`/tenants/${t.slug}`">
                  <div class="cell-name">{{ t.name }}</div>
                  <Mono dim>{{ t.slug }}</Mono>
                </NuxtLink>
              </td>
              <td><Badge :tone="TENANT_STATUS_TONE[t.status]" dot>{{ t.status }}</Badge></td>
              <td><Badge tone="neutral">{{ t.plan }}</Badge></td>
              <td><Mono :dim="!t.domains.length">{{ t.domains[0] || '—' }}</Mono></td>
              <td class="td-right">
                <UiButton variant="danger" size="sm" @click="detachTarget = t; detachError = null">Detach</UiButton>
              </td>
            </tr>
          </tbody>
        </table>
      </Card>

      <Card>
        <h2 class="danger">Soft-terminate partner</h2>
        <p>
          Marks the partner as <Mono>terminated</Mono>. Customer tenants keep
          their <Mono>partnerId</Mono> reference — we never hard-delete since
          historical billing depends on it. To re-activate, edit the status
          back to active via the admin API (UI editor lands later).
        </p>
        <UiButton
          variant="danger"
          :disabled="partner.status === 'terminated'"
          @click="terminateOpen = true; terminateError = null"
        >Terminate partner</UiButton>
      </Card>
    </div>

    <!-- Attach tenant modal -->
    <ConfirmDialog
      :open="attachOpen"
      :eyebrow="`Partner · ${partner.slug}`"
      title="Attach a tenant"
      confirm-label="Attach"
      :busy="attachBusy"
      @close="attachOpen = false"
      @confirm="submitAttach"
    >
      <p>Pick an unattached tenant. Direct customers (those without a partner) show below.</p>
      <select v-if="attachable.length" v-model="selectedSlug" class="select">
        <option v-for="t in attachable" :key="t._id" :value="t.slug">
          {{ t.name }} — {{ t.slug }} · {{ t.plan }}
        </option>
      </select>
      <p v-else class="hint">No unattached tenants. Detach one from its current partner first.</p>
      <p v-if="attachError" class="err">{{ attachError }}</p>
    </ConfirmDialog>

    <!-- Detach confirm -->
    <ConfirmDialog
      :open="detachTarget !== null"
      :eyebrow="`Partner · ${partner.slug}`"
      title="Detach this tenant?"
      confirm-label="Detach"
      tone="danger"
      :busy="detachBusy"
      @close="detachTarget = null"
      @confirm="confirmDetach"
    >
      <p>
        Remove <strong>{{ detachTarget?.name }}</strong> from <strong>{{ partner.name }}</strong>.
        The tenant becomes a direct customer (no partner). Reversible — re-attach any time.
      </p>
      <p v-if="detachError" class="err">{{ detachError }}</p>
    </ConfirmDialog>

    <!-- Terminate partner confirm -->
    <ConfirmDialog
      :open="terminateOpen"
      :eyebrow="`Partner · ${partner.slug}`"
      title="Terminate this partner?"
      confirm-label="Terminate"
      tone="danger"
      :busy="terminateBusy"
      @close="terminateOpen = false"
      @confirm="confirmTerminate"
    >
      <p>
        Set <strong>{{ partner.name }}</strong> status to <Mono>terminated</Mono>. Existing
        customer tenants keep their reference. To re-activate later, set status back via the
        admin API.
      </p>
      <p v-if="terminateError" class="err">{{ terminateError }}</p>
    </ConfirmDialog>
  </div>
</template>

<style scoped>
.stage {
  padding: 24px 40px 64px 40px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
  gap: 16px;
}

h2 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 16px;
  letter-spacing: -0.01em;
  margin: 0 0 16px 0;
}
h2.danger { color: var(--bad); }

p {
  margin: 0 0 14px 0;
  color: var(--text-dim);
  font-size: 13px;
  line-height: 1.55;
}
p.hint { font-size: 12px; color: var(--text-mute); margin: 4px 0 0 0; }
p.err { color: var(--bad); font-family: var(--font-mono); font-size: 12px; margin-top: 12px; }

code {
  font-family: var(--font-mono);
  font-size: 12px;
  background: rgba(244, 243, 238, 0.06);
  padding: 1px 6px;
  border-radius: 3px;
}

dl { display: flex; flex-direction: column; gap: 10px; }
.dl-row {
  display: grid;
  grid-template-columns: 120px 1fr;
  gap: 12px;
  font-size: 13px;
}
dt {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
  align-self: center;
}
dd { margin: 0; color: var(--text); display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }

.num {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 18px;
  letter-spacing: -0.01em;
}

.card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
.card-head.padded { padding: 20px 24px 12px 24px; }
.card-head h2 { margin: 0; }

table { width: 100%; border-collapse: collapse; font-size: 13px; }
thead tr { border-bottom: 1px solid var(--border); }
th {
  padding: 12px 16px;
  text-align: left;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
}
th.th-right { text-align: right; }

tbody tr { border-bottom: 1px solid var(--border); }
tbody tr:last-child { border-bottom: none; }
td { padding: 14px 16px; color: var(--text); }
td.td-right { text-align: right; }

.empty td { padding: 36px 16px; text-align: center; }
.empty-inner { color: var(--text-mute); font-size: 13px; }

.link {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-decoration: none;
  color: var(--text);
}
.link:hover .cell-name { text-decoration: underline; }
.cell-name { font-weight: 500; }

.select {
  width: 100%;
  height: 36px;
  padding: 0 10px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  outline: none;
}
.select:focus { border-color: var(--accent); }

/* Inline edit-mode form fields */
.field {
  flex: 1;
  min-width: 0;
  height: 32px;
  padding: 0 10px;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  outline: 0;
}
.field.mono { font-family: var(--font-mono); }
.field.country { max-width: 80px; text-transform: uppercase; }
.field:focus { border-color: var(--border-hi); }
.field:disabled { opacity: 0.6; }

/* Status segmented control (mirrors FlagDetail.vue's seg pattern) */
.seg {
  display: grid;
  width: 100%;
  gap: 2px;
  padding: 2px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 7px;
  box-sizing: border-box;
}
.seg.four { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.seg button {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--text-dim);
  font-family: inherit;
  font-size: 11px;
  font-weight: 500;
  padding: 6px 8px;
  border-radius: 5px;
  cursor: pointer;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.seg button:hover:not(:disabled) { color: var(--text); }
.seg button.on { background: var(--text); color: var(--bg); }
.seg button:disabled { opacity: 0.5; cursor: not-allowed; }

/* marginPct range slider (mirrors FlagDetail.vue rollout slider) */
.slider-cell { display: flex; align-items: center; gap: 14px; width: 100%; }
.slider { flex: 1; accent-color: var(--accent); }
.slider:disabled { opacity: 0.6; }
.pct { font-size: 14px; font-weight: 600; min-width: 50px; text-align: right; }

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