<script lang="ts">
  import { ClipboardList, Clock, Eye, FlaskConical, Info, UserPlus, X } from 'lucide-svelte';

  import { api } from '$lib/api';
  import { toast } from '$lib/toast';
  import { tooltip } from '$lib/actions/tooltip';
  import { label, money, statusTone } from '$lib/ordering/format';
  import CustomerProductVisibility from '$lib/components/ordering/CustomerProductVisibility.svelte';
  import type {
    CustomerPricing,
    CustomerVisibilityRow,
    EditorMixRow,
    OrderingCustomer,
    OrderingCustomerUser,
    Order
  } from '$lib/types';

  let {
    customer,
    onChanged,
    onClose
  }: {
    customer: OrderingCustomer;
    onChanged: (updated?: OrderingCustomer) => void;
    onClose: () => void;
  } = $props();

  type TabId = 'details' | 'access' | 'orders' | 'mixes' | 'history';
  const TABS: { id: TabId; label: string; icon: typeof Info }[] = [
    { id: 'details', label: 'Details', icon: Info },
    { id: 'access', label: 'Access', icon: Eye },
    { id: 'orders', label: 'Orders', icon: ClipboardList },
    { id: 'mixes', label: 'Mixes', icon: FlaskConical },
    { id: 'history', label: 'History', icon: Clock }
  ];

  let activeTab = $state<TabId>('details');
  let loaded = $state(new Set<TabId>());
  let loading = $state(false);

  // Per-customer data. Cleared whenever the selected customer changes.
  let users = $state<OrderingCustomerUser[]>([]);
  let pricing = $state<CustomerPricing | null>(null);
  let visibility = $state<CustomerVisibilityRow[]>([]);
  let orders = $state<Order[]>([]);
  let mixes = $state<EditorMixRow[]>([]);

  let notesDraft = $state('');
  let addingUser = $state(false);
  let newUser = $state({ full_name: '', email: '', role: 'buyer' });

  // Reset and reload only when the *id* changes. The parent re-passes a fresh
  // customer object after status/notes saves (same id); those must not reset the
  // open tab or reload everything.
  let lastId = -1;
  $effect(() => {
    const id = customer.id;
    if (id === lastId) return;
    lastId = id;
    activeTab = 'details';
    loaded = new Set();
    users = [];
    pricing = null;
    visibility = [];
    orders = [];
    mixes = [];
    notesDraft = customer.notes ?? '';
    addingUser = false;
    void loadTab('details');
  });

  async function loadTab(tab: TabId) {
    const id = customer.id;
    loading = true;
    try {
      if (tab === 'details') {
        const [u, p] = await Promise.all([
          api.orderingAdmin.customerUsers(id),
          api.orderingAdmin.pricing(id).catch(() => null)
        ]);
        if (id !== customer.id) return;
        users = u;
        pricing = p;
      } else if (tab === 'access') {
        const v = await api.orderingAdmin.visibility(id);
        if (id !== customer.id) return;
        visibility = v;
      } else if (tab === 'orders' || tab === 'history') {
        const o = await api.orderingAdmin.orders({ customer_id: id });
        if (id !== customer.id) return;
        orders = o;
        loaded = new Set(loaded).add('orders');
      } else if (tab === 'mixes') {
        const m = await api.editorMixes({ client_name: customer.name });
        if (id !== customer.id) return;
        mixes = m;
      }
      loaded = new Set(loaded).add(tab);
    } catch (e) {
      toast.error(e instanceof Error ? e.message : 'Could not load customer data.');
    } finally {
      if (id === customer.id) loading = false;
    }
  }

  async function selectTab(tab: TabId) {
    activeTab = tab;
    if (!loaded.has(tab)) await loadTab(tab);
  }

  function onTabKeydown(event: KeyboardEvent, index: number) {
    if (event.key !== 'ArrowRight' && event.key !== 'ArrowLeft') return;
    event.preventDefault();
    const next = (index + (event.key === 'ArrowRight' ? 1 : TABS.length - 1)) % TABS.length;
    void selectTab(TABS[next].id);
  }

  // ── Mutations ───────────────────────────────────────────────────────────────
  async function toggleStatus() {
    try {
      const updated = await api.orderingAdmin.updateCustomer(customer.id, {
        status: customer.status === 'active' ? 'disabled' : 'active'
      });
      toast.success(`Customer ${updated.status}.`);
      onChanged(updated);
    } catch (e) {
      toast.error(e instanceof Error ? e.message : 'Update failed.');
    }
  }

  async function saveNotes() {
    try {
      const updated = await api.orderingAdmin.updateCustomer(customer.id, { notes: notesDraft });
      toast.success('Notes saved.');
      onChanged(updated);
    } catch (e) {
      toast.error(e instanceof Error ? e.message : 'Could not save notes.');
    }
  }

  async function addUser() {
    if (!newUser.full_name || !newUser.email) return toast.error('Name and email required.');
    try {
      await api.orderingAdmin.createCustomerUser(customer.id, newUser);
      toast.success('User invited.');
      newUser = { full_name: '', email: '', role: 'buyer' };
      addingUser = false;
      users = await api.orderingAdmin.customerUsers(customer.id);
      onChanged();
    } catch (e) {
      toast.error(e instanceof Error ? e.message : 'Could not add user.');
    }
  }

  async function toggleUserStatus(u: OrderingCustomerUser) {
    try {
      const next = u.status === 'suspended' ? 'active' : 'suspended';
      await api.orderingAdmin.updateCustomerUser(customer.id, u.id, { status: next });
      users = await api.orderingAdmin.customerUsers(customer.id);
    } catch (e) {
      toast.error(e instanceof Error ? e.message : 'Update failed.');
    }
  }

  async function toggleVisibility(row: CustomerVisibilityRow) {
    try {
      await api.orderingAdmin.setVisibility(customer.id, { product_id: row.product_id, visible: !row.visible });
      visibility = await api.orderingAdmin.visibility(customer.id);
    } catch (e) {
      toast.error(e instanceof Error ? e.message : 'Update failed.');
    }
  }

  async function bulkVisibility(productIds: number[], visible: boolean) {
    if (!productIds.length) return;
    const id = customer.id;
    try {
      await Promise.all(productIds.map((pid) => api.orderingAdmin.setVisibility(id, { product_id: pid, visible })));
    } catch (e) {
      toast.error(e instanceof Error ? e.message : 'Could not update visibility.');
    } finally {
      visibility = await api.orderingAdmin.visibility(id);
    }
  }

  function initials(name: string) {
    return (
      name
        .split(/\s+/)
        .filter(Boolean)
        .slice(0, 2)
        .map((part) => part[0]?.toUpperCase() ?? '')
        .join('') || '?'
    );
  }

  function fmtDate(value?: string | null) {
    return value ? new Date(value).toLocaleDateString('en-AU', { day: 'numeric', month: 'short', year: 'numeric' }) : '—';
  }

  const customPriceCount = $derived(pricing?.product_prices.filter((p) => p.active).length ?? 0);

  // Activity timeline merged from order milestones and user invites.
  const historyEvents = $derived.by(() => {
    const events: { when: string; text: string; tone: string }[] = [];
    for (const o of orders) {
      const ref = o.order_number ?? `#${o.id}`;
      if (o.submitted_at) events.push({ when: o.submitted_at, text: `Order ${ref} submitted`, tone: 'info' });
      if (o.updated_at) events.push({ when: o.updated_at, text: `Order ${ref} is ${label(o.status)}`, tone: statusTone(o.status) });
    }
    for (const u of users) {
      if (u.created_at) events.push({ when: u.created_at, text: `Invited ${u.full_name}`, tone: '' });
    }
    return events
      .filter((e) => e.when)
      .sort((a, b) => new Date(b.when).getTime() - new Date(a.when).getTime());
  });
</script>

<section class="surface-card detail workspace">
  <div class="workspace-head">
    <div class="detail-head head-row">
      <div class="detail-title">
        <p class="eyebrow">{customer.client_code}</p>
        <h2>{customer.name}</h2>
      </div>
      <div class="detail-head-actions">
        <span class="pill {statusTone(customer.status)}">{customer.status}</span>
        <button
          class="secondary"
          onclick={toggleStatus}
          use:tooltip={customer.status === 'active'
            ? 'Disable ordering for this customer'
            : 'Re-enable ordering for this customer'}
        >
          {customer.status === 'active' ? 'Disable' : 'Enable'}
        </button>
        <button class="icon-btn" onclick={onClose} aria-label="Close customer" use:tooltip={{ label: 'Close (Esc)', placement: 'bottom' }}>
          <X size={17} strokeWidth={2} aria-hidden="true" />
        </button>
      </div>
    </div>

    <div class="workspace-tabs" role="tablist" aria-label="Customer sections">
      {#each TABS as tab, i (tab.id)}
        {@const Icon = tab.icon}
        <button
          class="workspace-tab"
          role="tab"
          id={`ws-tab-${tab.id}`}
          aria-selected={activeTab === tab.id}
          aria-controls={`ws-panel-${tab.id}`}
          tabindex={activeTab === tab.id ? 0 : -1}
          onclick={() => selectTab(tab.id)}
          onkeydown={(e) => onTabKeydown(e, i)}
        >
          <Icon size={15} strokeWidth={2} aria-hidden="true" />
          {tab.label}
          {#if tab.id === 'orders' && loaded.has('orders') && orders.length}<span class="tab-count">{orders.length}</span>{/if}
          {#if tab.id === 'mixes' && loaded.has('mixes') && mixes.length}<span class="tab-count">{mixes.length}</span>{/if}
        </button>
      {/each}
    </div>
  </div>

  <div class="workspace-body" role="tabpanel" id={`ws-panel-${activeTab}`} aria-labelledby={`ws-tab-${activeTab}`}>
    {#if loading && !loaded.has(activeTab)}
      <div class="skeleton" aria-hidden="true">
        <span class="skel skel-line"></span>
        <span class="skel skel-line short"></span>
        <span class="skel skel-block"></span>
      </div>

    <!-- ── Details ─────────────────────────────────────────────── -->
    {:else if activeTab === 'details'}
      <div class="ws-section">
        <dl class="facts">
          <div><dt>Client code</dt><dd>{customer.client_code}</dd></div>
          <div><dt>Discount</dt><dd>{customer.discount_percent ? `${customer.discount_percent}%` : 'None'}</dd></div>
          <div><dt>Price list</dt><dd>{pricing?.price_list_id ? `#${pricing.price_list_id}` : 'Default'}</dd></div>
          <div><dt>Custom prices</dt><dd>{customPriceCount || 'None'}</dd></div>
          <div><dt>Xero</dt><dd>{customer.xero_contact_id ? 'Linked' : 'Not linked'}</dd></div>
          <div><dt>People</dt><dd>{customer.user_count}</dd></div>
        </dl>
      </div>

      <div class="ws-section">
        <div class="section-head"><h3>Notes</h3></div>
        <textarea class="notes" rows="3" placeholder="Internal notes about this customer" bind:value={notesDraft}></textarea>
        <div class="actions notes-actions">
          <button class="secondary" onclick={saveNotes} disabled={(customer.notes ?? '') === notesDraft}>Save notes</button>
        </div>
      </div>

      <div class="ws-section">
        <div class="section-head">
          <h3>People <span class="count">{users.length}</span></h3>
          <button class="link" onclick={() => (addingUser = !addingUser)}>
            <UserPlus size={14} strokeWidth={2} aria-hidden="true" />
            {addingUser ? 'Cancel' : 'Add person'}
          </button>
        </div>

        {#if users.length}
          <ul class="roster">
            {#each users as u (u.id)}
              <li>
                <span class="avatar" aria-hidden="true">{initials(u.full_name)}</span>
                <div class="who">
                  <strong>{u.full_name}</strong>
                  <span class="who-sub">{u.email}</span>
                </div>
                <span class="pill role-pill">{label(u.role)}</span>
                <span class="pill {statusTone(u.status)}">{u.status}</span>
                <button class="link" onclick={() => toggleUserStatus(u)}>
                  {u.status === 'suspended' ? 'Reactivate' : 'Suspend'}
                </button>
              </li>
            {/each}
          </ul>
        {:else}
          <p class="empty">No people yet. Invite a buyer to give them portal access.</p>
        {/if}

        {#if addingUser}
          <div class="create-panel add-user">
            <div class="form-row">
              <label>Full name<input placeholder="Jordan Lee" bind:value={newUser.full_name} /></label>
              <label>Email<input type="email" placeholder="jordan@acme.com" bind:value={newUser.email} /></label>
              <label>Role
                <select bind:value={newUser.role}>
                  <option value="owner">Owner</option>
                  <option value="buyer">Buyer</option>
                  <option value="accounts">Accounts</option>
                  <option value="viewer">Viewer</option>
                </select>
              </label>
            </div>
            <div class="actions"><button class="primary" onclick={addUser}>Send invite</button></div>
          </div>
        {/if}
      </div>

    <!-- ── Access ──────────────────────────────────────────────── -->
    {:else if activeTab === 'access'}
      <p class="muted">Choose which products this customer can see and order in the portal.</p>
      <CustomerProductVisibility rows={visibility} onToggle={toggleVisibility} onBulk={bulkVisibility} />

    <!-- ── Orders ──────────────────────────────────────────────── -->
    {:else if activeTab === 'orders'}
      {#if orders.length}
        <table>
          <thead><tr><th>Order</th><th>Placed</th><th>Status</th><th class="amt">Subtotal</th></tr></thead>
          <tbody>
            {#each orders as o (o.id)}
              <tr>
                <td class="id-name">{o.order_number ?? `#${o.id}`}</td>
                <td>{fmtDate(o.submitted_at ?? o.created_at)}</td>
                <td><span class="pill {statusTone(o.status)}">{label(o.status)}</span></td>
                <td class="amt">{o.requires_quote ? 'Quote' : money(o.subtotal_ex_gst)}</td>
              </tr>
            {/each}
          </tbody>
        </table>
      {:else}
        <p class="empty">No orders from this customer yet.</p>
      {/if}

    <!-- ── Mixes ───────────────────────────────────────────────── -->
    {:else if activeTab === 'mixes'}
      <div class="section-head">
        <p class="muted" style="margin:0">Recipes linked to {customer.name}.</p>
        <a class="link" href="/editor">Open Mix Editor</a>
      </div>
      {#if mixes.length}
        <table>
          <thead><tr><th>Mix</th><th class="amt">Products</th><th>Visible</th></tr></thead>
          <tbody>
            {#each mixes as m (m.id)}
              <tr>
                <td class="id-name">{m.name}</td>
                <td class="amt">{m.product_count}</td>
                <td><span class="pill {m.visible ? 'pos' : 'muted-pill'}">{m.visible ? 'Visible' : 'Hidden'}</span></td>
              </tr>
            {/each}
          </tbody>
        </table>
      {:else}
        <p class="empty">No mixes matched “{customer.name}”. Mixes link to customers by client name.</p>
      {/if}

    <!-- ── History ─────────────────────────────────────────────── -->
    {:else if activeTab === 'history'}
      {#if historyEvents.length}
        <ol class="timeline">
          {#each historyEvents as e (e.when + e.text)}
            <li>
              <span class="dot {e.tone}" aria-hidden="true"></span>
              <div class="event">
                <span class="event-text">{e.text}</span>
                <time>{new Date(e.when).toLocaleString('en-AU', { dateStyle: 'medium', timeStyle: 'short' })}</time>
              </div>
            </li>
          {/each}
        </ol>
      {:else}
        <p class="empty">No recorded activity yet.</p>
      {/if}
    {/if}
  </div>
</section>

<style>
  .head-row {
    border-bottom: none;
    padding-bottom: 0;
    margin-bottom: 0;
  }

  /* ── Details: facts grid ──────────────────────────────────────── */
  .facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
    gap: 0.9rem 1.2rem;
    margin: 0;
  }
  .facts dt {
    font-size: 0.64rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin-bottom: 0.15rem;
  }
  .facts dd {
    margin: 0;
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--color-text-primary);
  }

  .notes {
    width: 100%;
    padding: 0.6rem 0.7rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-control);
    background: var(--color-input-bg);
    color: var(--color-text-primary);
    font: inherit;
    resize: vertical;
  }
  .notes:focus {
    outline: none;
    border-color: var(--color-brand);
    background: var(--color-bg-surface);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-brand) 20%, transparent);
  }
  .notes-actions { justify-content: flex-end; margin-top: 0.6rem; }

  .link { display: inline-flex; align-items: center; gap: 0.32rem; }
  .link :global(svg) { display: block; }

  /* ── People roster ────────────────────────────────────────────── */
  .roster { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.45rem; }
  .roster li {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.55rem 0.65rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-control);
    background: var(--color-bg-surface);
  }
  .avatar {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 2.1rem;
    height: 2.1rem;
    border-radius: 50%;
    background: var(--color-brand-tint);
    color: var(--color-brand);
    font-size: 0.74rem;
    font-weight: 700;
  }
  .who { flex: 1; min-width: 0; display: grid; gap: 0.1rem; }
  .who strong { font-size: 0.86rem; font-weight: 600; color: var(--color-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .who-sub { font-size: 0.76rem; color: var(--color-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .role-pill { background: color-mix(in srgb, var(--panel-soft) 70%, var(--color-bg-surface)); color: var(--color-text-secondary); }
  .add-user { margin-top: 0.7rem; }
  .add-user .form-row { margin-bottom: 0.7rem; }
  .add-user .form-row label { flex: 1; min-width: 9rem; }
  .add-user .actions { justify-content: flex-end; }

  .amt { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

  /* ── History timeline ─────────────────────────────────────────── */
  .timeline { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
  .timeline li { display: flex; gap: 0.75rem; padding: 0.1rem 0; }
  .timeline .dot {
    flex-shrink: 0;
    width: 0.6rem;
    height: 0.6rem;
    margin-top: 0.4rem;
    border-radius: 50%;
    background: var(--color-text-muted);
    position: relative;
  }
  /* Connecting line between events. */
  .timeline li:not(:last-child) .dot::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 0.85rem;
    transform: translateX(-50%);
    width: 1px;
    height: calc(100% + 0.2rem);
    background: var(--color-divider);
  }
  .timeline .dot.pos { background: var(--color-success); }
  .timeline .dot.info { background: var(--color-info); }
  .timeline .dot.warn { background: var(--color-warning); }
  .timeline .dot.danger { background: var(--color-error); }
  .timeline .event { display: flex; flex-direction: column; gap: 0.05rem; padding-bottom: 0.85rem; }
  .event-text { font-size: 0.85rem; color: var(--color-text-primary); }
  .event time { font-size: 0.73rem; color: var(--color-text-muted); }

  /* ── Loading skeleton ─────────────────────────────────────────── */
  .skeleton { display: grid; gap: 0.7rem; }
  .skel {
    border-radius: var(--radius-control);
    background: linear-gradient(90deg, var(--panel-soft) 25%, var(--color-surface-hover) 37%, var(--panel-soft) 63%);
    background-size: 400% 100%;
    animation: skel-shimmer 1.4s ease-in-out infinite;
  }
  .skel-line { height: 1rem; width: 60%; }
  .skel-line.short { width: 35%; }
  .skel-block { height: 9rem; width: 100%; }
  @keyframes skel-shimmer {
    0% { background-position: 100% 0; }
    100% { background-position: 0 0; }
  }
  @media (prefers-reduced-motion: reduce) {
    .skel { animation: none; }
  }
</style>
