<script setup lang="ts">
/**
 * WorkspaceSwitcher — ported from crewli-starter WorkspaceSwitcher.vue.
 *
 * Data: read-only from useAuthStore (organisations, currentOrganisation,
 * setActiveOrganisation). This component owns NO org state — RFC AD-G4.
 *
 * Popover: PrimeVue <Popover> replaces the manual document.mousedown
 * click-outside listener from crewli-starter. Toggle via popoverRef.toggle($event).
 *
 * Plan 2.5 P5 Fix 5 (RFC §5.5): dropdown panel restructured per
 * crewli-starter reference, with AD-2.5-W1 option A applied — dropdown
 * rows render WITHOUT a sub line (organisation type / metrics deferred
 * to a future RFC with backend scope — tracked as
 * WORKSPACE-DROPDOWN-SUB-CONTENT). Known visual divergence: rows are
 * ~16px shorter than crewli-starter; accepted.
 *
 * Semantic class names (.popover-head, .title, .link, .list, .opt,
 * .is-current, .ws-logo, .name, .check-mark, .foot) are kept alongside
 * the Tailwind utility classes as structural markers — they carry no
 * scoped CSS rules (only the .ws-logo / .ws-logo-square-lg box-shadow
 * exceptions below) and exist so specs can assert structure with
 * stable selectors.
 *
 * Icons: Crewli Icon.vue convention — name="tabler-x" :size="N".
 *
 * Styling: crewli-starter CSS selectors translated to Tailwind utilities inline.
 * One <style scoped> block covers the two box-shadow exceptions documented below.
 */

import Popover from 'primevue/popover'
import { computed, ref } from 'vue'
import Icon from '@/components/Icon.vue'
import { useAuthStore } from '@/stores/useAuthStore'
import { computeOrgGradient } from '@/utils/v2/gradient'
import type { Organisation } from '@/types/auth'

defineProps<{
  /**
   * When true (collapsed sidebar), hide the name meta text and
   * show only the logo square — mirrors crewli-starter's collapsed prop.
   */
  collapsed?: boolean
}>()

// ---------------------------------------------------------------------------
// Store
// ---------------------------------------------------------------------------

const authStore = useAuthStore()

// ---------------------------------------------------------------------------
// Derived current-workspace display object
// ---------------------------------------------------------------------------

interface WorkspaceDisplay {
  id: string
  initials: string
  name: string
  /**
   * Light-grey second line under the name. Plan 2.5 P6-styling-switcher-sub
   * re-adds the two-line layout (reverses AD-2.5-W1 option A) after visual
   * review against the crewli-starter SoT. The value is a neutral
   * PLACEHOLDER — the real org type + metrics (e.g. "Festival · 12 days ·
   * 14 stages") require the organisations.type enum + a metrics endpoint,
   * which stay deferred under WORKSPACE-DROPDOWN-SUB-CONTENT. The org
   * object exposes no field that reads well as a subtitle today (only
   * id/name/slug/role; role is an access identifier, not a description),
   * so a static neutral string is used rather than fabricated metrics.
   */
  sub: string
  gradient: [string, string]
}

// TODO: replace with org type + metrics when WORKSPACE-DROPDOWN-SUB-CONTENT backend lands
const SUB_PLACEHOLDER = 'Organisatie'

function buildDisplay(org: Organisation): WorkspaceDisplay {
  const words = org.name.trim().split(/\s+/)

  const initials
    = (words.length >= 2
      ? (words[0][0] + words[1][0]).toUpperCase()
      : org.name.slice(0, 2).toUpperCase()) || '?'

  return {
    id: org.id,
    initials,
    name: org.name,
    sub: SUB_PLACEHOLDER,
    gradient: computeOrgGradient(org.id),
  }
}

const current = computed<WorkspaceDisplay | null>(() => {
  const org = authStore.currentOrganisation

  return org ? buildDisplay(org) : null
})

// Sorted list: active org first, then the rest alphabetically
const allOrgs = computed<WorkspaceDisplay[]>(() => {
  const currentId = authStore.currentOrganisation?.id

  return [...authStore.organisations]
    .sort((a, b) => {
      if (a.id === currentId)
        return -1
      if (b.id === currentId)
        return 1

      return a.name.localeCompare(b.name)
    })
    .map(buildDisplay)
})

// ---------------------------------------------------------------------------
// Popover plumbing — PrimeVue Popover replaces the manual mousedown listener
// ---------------------------------------------------------------------------

const popoverRef = ref<InstanceType<typeof Popover> | null>(null)

/**
 * Tracks the popover open state so the trigger keeps the grey "active"
 * background + visible border while the workspace dropdown is open
 * (crewli-starter `.ws-switcher.is-open .trigger`). Synced from the
 * PrimeVue Popover's @show/@hide events rather than mirroring the manual
 * toggle, so programmatic hides (selectOrg) and outside-click dismissal
 * both keep it accurate.
 */
const isOpen = ref(false)

function toggle(event: MouseEvent): void {
  popoverRef.value?.toggle(event)
}

function selectOrg(ws: WorkspaceDisplay): void {
  if (ws.id === authStore.currentOrganisation?.id) {
    popoverRef.value?.hide()

    return
  }
  authStore.setActiveOrganisation(ws.id)
  popoverRef.value?.hide()
}

// ---------------------------------------------------------------------------
// Footer button placeholders — neither flow is wired in Plan 2.5 scope.
// ---------------------------------------------------------------------------

// TODO: wire to /v2/workspaces/new route or create-workspace dialog when ready
function createWorkspace(): void {
  console.warn('WorkspaceSwitcher: createWorkspace not yet implemented')
}

// TODO: wire to invite-user dialog when ready
function inviteUser(): void {
  console.warn('WorkspaceSwitcher: inviteUser not yet implemented')
}
</script>

<template>
  <!--
    Unified borderless structure (P6-styling-switcher-hover, crewli-starter
    SoT). ONE `.trigger` button renders in BOTH states; collapsing only
    hides `.meta` + `.chev` (v-if). The button keeps the SAME padding in
    both states — this is what kills the avatar jump:

    avatar offset = wrapper px-2 (8px) + trigger p-2 (8px) = 16px

    16px from the rail's left edge in BOTH collapsed and expanded —
    identical to the SidebarHeader brand logo (px-4). Because the
    horizontal padding is symmetric (8 + 8 each side) and the rail
    collapses to 64px = 16 + 32 (avatar) + 16, the left-aligned avatar
    is ALSO visually centred when collapsed — no justify-center swap, no
    horizontal shift. Vertical padding is likewise constant, so no
    vertical shift either. The earlier 24px-expanded / 16px-collapsed
    split (and the later px-[10px]/justify-center variant) both left a
    residual shift; this symmetric structure removes it entirely.

    Borderless: the trigger has NO border in any state (the prior
    is-open border is dropped per the starter screenshots). The only
    divider is the wrapper's `border-t` separating the switcher from the
    nav above. The grey background is the sole fill, on hover OR while
    the popover is open.
  -->
  <div class="ws-switcher relative flex-shrink-0 border-t border-[var(--p-content-border-color)] p-2">
    <!--
      Trigger — single button for both states. Transparent at rest; grey
      on hover; grey KEPT while the popover is open (isOpen, wired to the
      Popover @show/@hide). The `p-2` gives the grey background generous
      padding around the avatar+text (crewli-starter hover treatment),
      and because the background is the button's own bg (not a separate
      shifting layer) it never moves the avatar.
    -->
    <button
      class="trigger flex w-full items-center gap-[10px] rounded-lg bg-transparent p-2 text-[var(--p-text-color)] transition-colors duration-150 hover:bg-surface-100 dark:hover:bg-surface-800 focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--p-primary-color)] focus-visible:outline-offset-2"
      :class="{ 'bg-surface-100 dark:bg-surface-800': isOpen }"
      aria-haspopup="true"
      :aria-expanded="isOpen"
      :aria-label="collapsed && current ? `Workspace: ${current.name}` : undefined"
      @click="toggle"
    >
      <!-- Avatar square — shared treatment with SidebarHeader logo (h-8 w-8 rounded-lg). Gradient background is bespoke per organisation (dynamic hex pair, RFC §7.4 inline-style). -->
      <span
        v-if="current"
        class="ws-logo ws-logo-square w-8 h-8 flex-shrink-0 rounded-lg inline-flex items-center justify-center text-white font-bold text-[12px]"
        :style="{ background: `linear-gradient(135deg, ${current.gradient[0]}, ${current.gradient[1]})` }"
      >
        {{ current.initials }}
      </span>

      <!-- Meta: name + placeholder sub line (hidden when collapsed). -->
      <span
        v-if="!collapsed && current"
        class="meta flex flex-1 min-w-0 flex-col text-left leading-[1.2]"
      >
        <span class="name truncate text-[13.5px] font-semibold text-[var(--p-text-color)]">
          {{ current.name }}
        </span>
        <span class="sub truncate text-[11.5px] text-[var(--p-text-muted-color)]">
          {{ current.sub }}
        </span>
      </span>

      <!-- Chevron: hidden when collapsed. -->
      <Icon
        v-if="!collapsed"
        name="tabler-chevron-down"
        :size="14"
        class="chev flex-shrink-0 text-[var(--p-text-muted-color)]"
      />
    </button>

    <!-- PrimeVue Popover — replaces crewli-starter's manual document.mousedown click-outside -->
    <Popover
      ref="popoverRef"
      @show="isOpen = true"
      @hide="isOpen = false"
    >
      <!-- Header -->
      <div class="popover-head flex items-center justify-between border-b border-[var(--p-content-border-color)] px-[16px] py-[14px]">
        <span class="title text-[15px] font-bold tracking-[-0.01em]">Workspaces</span>
        <!--
          TODO TECH-WS-GUI-REDESIGN: no v2-workspaces-manage route yet —
          rendered as a non-navigating label per the routeName-undefined
          convention (registry-style). Becomes a RouterLink when the
          manage-workspaces page lands.
        -->
        <span class="link cursor-pointer text-[13px] font-medium text-[var(--p-primary-color)]">Manage</span>
      </div>

      <!-- List of organisations -->
      <div class="list min-w-[280px] p-[6px]">
        <!-- TODO TECH-WS-GUI-REDESIGN: full listbox/menu ARIA (role, roving tabindex, arrow-key nav) — tracked -->
        <button
          v-for="ws in allOrgs"
          :key="ws.id"
          type="button"
          class="opt grid w-full grid-cols-[36px_1fr_auto] cursor-pointer items-center gap-[12px] rounded-[var(--p-border-radius)] border-0 bg-transparent p-[10px] text-start hover:bg-[var(--p-content-hover-background)]"
          :class="[
            ws.id === current?.id ? 'is-current bg-[var(--p-primary-50)]' : '',
          ]"
          :aria-current="current?.id === ws.id ? 'true' : undefined"
          @click="selectOrg(ws)"
        >
          <!-- Org logo (large variant) — dynamic gradient via inline :style -->
          <span
            class="ws-logo lg ws-logo-square-lg w-9 h-9 flex-shrink-0 rounded-[var(--p-border-radius)] inline-flex items-center justify-center text-white font-bold text-[13px]"
            :style="{ background: `linear-gradient(135deg, ${ws.gradient[0]}, ${ws.gradient[1]})` }"
          >{{ ws.initials }}</span>

          <!--
            Name + placeholder sub line (P6-styling-switcher-sub reverses
            AD-2.5-W1 option A). Real org type + metrics still deferred
            under WORKSPACE-DROPDOWN-SUB-CONTENT.
          -->
          <span class="min-w-0">
            <div class="name truncate text-[14px] font-semibold text-[var(--p-text-color)]">{{ ws.name }}</div>
            <div class="sub mt-[2px] truncate text-[12.5px] text-[var(--p-text-muted-color)]">{{ ws.sub }}</div>
          </span>

          <!-- Check mark for active org -->
          <Icon
            v-if="ws.id === current?.id"
            name="tabler-check"
            :size="18"
            class="check-mark text-[var(--p-primary-color)]"
          />
          <!-- Spacer when not current (keeps grid alignment) -->
          <span
            v-else
            class="w-[18px]"
          />
        </button>
      </div>

      <!-- Footer -->
      <div class="foot flex gap-[4px] border-t border-[var(--p-content-border-color)] p-[8px]">
        <button
          type="button"
          class="inline-flex flex-1 h-[36px] items-center justify-center gap-[6px] rounded-[var(--p-border-radius)] border-0 bg-transparent text-[13px] font-medium text-[var(--p-text-color)] hover:bg-[var(--p-content-hover-background)]"
          @click="createWorkspace"
        >
          <Icon
            name="tabler-plus"
            :size="14"
          />
          New workspace
        </button>
        <button
          type="button"
          class="inline-flex flex-1 h-[36px] items-center justify-center gap-[6px] rounded-[var(--p-border-radius)] border-0 bg-transparent text-[13px] font-medium text-[var(--p-text-color)] hover:bg-[var(--p-content-hover-background)]"
          @click="inviteUser"
        >
          <Icon
            name="tabler-user-plus"
            :size="14"
          />
          Invite
        </button>
      </div>
    </Popover>
  </div>
</template>

<style scoped>
/**
 * ws-logo-square — width/height moved to Tailwind (w-8 h-8 on the element).
 * Tailwind has no inset directional box-shadow utility at this granularity →
 * scoped CSS last resort per RFC §7.4.
 */
.ws-logo-square {
  box-shadow: inset 0 -2px 0 rgb(0 0 0 / 10%);
}

/**
 * ws-logo-square-lg — width/height moved to Tailwind (w-9 h-9 on the element).
 * Same box-shadow exception as above (inset shadow has no Tailwind equivalent).
 * Background gradient set via inline :style on the element (dynamic hex pair).
 */
.ws-logo-square-lg {
  box-shadow: inset 0 -2px 0 rgb(0 0 0 / 10%);
}
</style>
