// SPDX-License-Identifier: LicenseRef-Commercial
<script lang="ts">
  import { onMount } from 'svelte';
  import {
    proMarketplaceFetchCatalog, proMarketplaceInstalled, proMarketplaceInstall,
    proMarketplaceUninstall, proMarketplaceCheckUpdates, proMarketplaceUpdate,
    type CatalogPlugin, type InstalledPlugin,
  } from './pro-bridge';

  type Tab = 'browse' | 'installed';
  let tab = $state<Tab>('browse');
  let search = $state('');
  let toast = $state<string | null>(null);
  let toastTimer: ReturnType<typeof setTimeout> | undefined;

  let catalog = $state<CatalogPlugin[]>([]);
  let catalogLoading = $state(true);
  let catalogError = $state<string | null>(null);
  let selectedPlugin = $state<CatalogPlugin | null>(null);
  let installing = $state<Set<string>>(new Set());

  let installed = $state<InstalledPlugin[]>([]);
  let installedLoading = $state(true);
  let installedError = $state<string | null>(null);
  let checking = $state(false);
  let uninstalling = $state<Set<string>>(new Set());
  let updating = $state<Set<string>>(new Set());
  let confirmUninstall = $state<string | null>(null);

  let installedIds = $derived(new Set(installed.map((p) => p.id)));
  let filtered = $derived(
    catalog.filter((p) => {
      if (!search) return true;
      const q = search.toLowerCase();
      return p.name.toLowerCase().includes(q) || p.description.toLowerCase().includes(q)
        || (p.tags ?? []).some((t) => t.toLowerCase().includes(q));
    })
  );

  function showToast(msg: string) {
    toast = msg;
    clearTimeout(toastTimer);
    toastTimer = setTimeout(() => (toast = null), 3000);
  }

  function mutSet(s: Set<string>, id: string, add: boolean): Set<string> {
    const n = new Set(s);
    add ? n.add(id) : n.delete(id);
    return n;
  }

  async function loadCatalog() {
    catalogLoading = true; catalogError = null;
    try { catalog = await proMarketplaceFetchCatalog(); }
    catch (e) { catalogError = e instanceof Error ? e.message : String(e); }
    finally { catalogLoading = false; }
  }

  async function loadInstalled() {
    installedLoading = true; installedError = null;
    try { installed = await proMarketplaceInstalled(); }
    catch (e) { installedError = e instanceof Error ? e.message : String(e); }
    finally { installedLoading = false; }
  }

  async function handleInstall(id: string) {
    installing = mutSet(installing, id, true);
    try {
      const r = await proMarketplaceInstall(id);
      installed = [...installed, r];
      showToast(`Installed ${r.name} v${r.version}`);
    } catch (e) { showToast(`Install failed: ${e instanceof Error ? e.message : String(e)}`); }
    finally { installing = mutSet(installing, id, false); }
  }

  async function handleUninstall(id: string) {
    confirmUninstall = null;
    uninstalling = mutSet(uninstalling, id, true);
    const name = installed.find((p) => p.id === id)?.name ?? id;
    try {
      await proMarketplaceUninstall(id);
      installed = installed.filter((p) => p.id !== id);
      showToast(`Uninstalled ${name}`);
    } catch (e) { showToast(`Uninstall failed: ${e instanceof Error ? e.message : String(e)}`); }
    finally { uninstalling = mutSet(uninstalling, id, false); }
  }

  async function handleCheckUpdates() {
    checking = true;
    try {
      const u = await proMarketplaceCheckUpdates();
      installed = u;
      const c = u.filter((p) => p.hasUpdate).length;
      showToast(c > 0 ? `${c} update(s) available` : 'All plugins up to date');
    } catch (e) { showToast(`Check failed: ${e instanceof Error ? e.message : String(e)}`); }
    finally { checking = false; }
  }

  async function handleUpdate(id: string) {
    updating = mutSet(updating, id, true);
    try {
      const r = await proMarketplaceUpdate(id);
      installed = installed.map((p) => (p.id === id ? r : p));
      showToast(`Updated ${r.name} to v${r.version}`);
    } catch (e) { showToast(`Update failed: ${e instanceof Error ? e.message : String(e)}`); }
    finally { updating = mutSet(updating, id, false); }
  }

  function fmtDl(n: number | null): string {
    if (n == null) return '';
    return n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);
  }

  function stars(r: number | null): string {
    if (r == null) return '';
    const f = Math.round(r);
    return '\u2605'.repeat(f) + '\u2606'.repeat(5 - f);
  }

  function safeUrl(url: string | null): string | null {
    if (!url) return null;
    return /^https?:\/\//.test(url) ? url : null;
  }

  onMount(() => { loadCatalog(); loadInstalled(); });
</script>

<div class="mp-root">
  {#if toast}<div class="mp-toast">{toast}</div>{/if}

  <div class="mp-tabs">
    <button class="mp-tab" class:active={tab === 'browse'} onclick={() => { tab = 'browse'; confirmUninstall = null; }}>Browse</button>
    <button class="mp-tab" class:active={tab === 'installed'} onclick={() => { tab = 'installed'; confirmUninstall = null; }}>
      Installed{installed.length > 0 ? ` (${installed.length})` : ''}
    </button>
  </div>

  {#if tab === 'browse'}
    <div class="mp-search">
      <input type="text" placeholder="Search plugins..." bind:value={search} class="mp-input" />
    </div>

    {#if catalogLoading}
      <div class="mp-state">Loading catalog...</div>
    {:else if catalogError}
      <div class="mp-state mp-err">{catalogError}</div>
    {:else if filtered.length === 0}
      <div class="mp-state">{search ? 'No plugins match your search' : 'No plugins available'}</div>
    {:else}
      <div class="mp-grid">
        {#each filtered as p (p.id)}
          <div class="mp-card" onclick={() => (selectedPlugin = selectedPlugin?.id === p.id ? null : p)}>
            <div class="mp-row">
              <span class="mp-name">{p.name}</span>
              <span class="mp-ver">v{p.version}</span>
            </div>
            <div class="mp-author">by {p.author}</div>
            <div class="mp-desc">{p.description}</div>
            <div class="mp-meta">
              {#if p.tags}<div class="mp-tags">{#each p.tags.slice(0, 3) as t}<span class="mp-tag">{t}</span>{/each}</div>{/if}
              <div class="mp-stats">
                {#if p.downloads != null}<span>{fmtDl(p.downloads)} dl</span>{/if}
                {#if p.rating != null}<span class="mp-stars">{stars(p.rating)}</span>{/if}
              </div>
            </div>
            {#if p.permissions.length > 0}
              <div class="mp-perms">
                {#each p.permissions.slice(0, 3) as pm}<span class="mp-perm">{pm}</span>{/each}
                {#if p.permissions.length > 3}<span class="mp-perm">+{p.permissions.length - 3}</span>{/if}
              </div>
            {/if}
            <div class="mp-actions">
              <button class="mp-btn primary" disabled={installedIds.has(p.id) || installing.has(p.id)}
                onclick={(e) => { e.stopPropagation(); handleInstall(p.id); }}>
                {installing.has(p.id) ? 'Installing...' : installedIds.has(p.id) ? 'Installed' : 'Install'}
              </button>
            </div>
          </div>
        {/each}
      </div>
    {/if}

    {#if selectedPlugin}
      {@const sp = selectedPlugin}
      <div class="mp-detail">
        <div class="mp-detail-hdr">
          <h3 class="mp-detail-name">{sp.name}</h3>
          <button class="mp-x" aria-label="Close plugin details" onclick={() => (selectedPlugin = null)}>x</button>
        </div>
        <p class="mp-detail-desc">{sp.description}</p>
        <div class="mp-fields">
          <span><b>Version</b> {sp.version}</span>
          <span><b>Author</b> {sp.author}</span>
          {#if sp.license}<span><b>License</b> {sp.license}</span>{/if}
          {#if sp.sizeBytes != null}<span><b>Size</b> {(sp.sizeBytes / 1024).toFixed(0)} KB</span>{/if}
          {#if sp.minAgorVersion}<span><b>Min Ver</b> {sp.minAgorVersion}</span>{/if}
        </div>
        {#if sp.permissions.length > 0}
          <div class="mp-section-hd">Permissions</div>
          <div class="mp-perms">{#each sp.permissions as pm}<span class="mp-perm">{pm}</span>{/each}</div>
        {/if}
        <div class="mp-links">
          {#if safeUrl(sp.homepage)}<a href={safeUrl(sp.homepage)} target="_blank" rel="noopener" class="mp-link">Homepage</a>{/if}
          {#if safeUrl(sp.repository)}<a href={safeUrl(sp.repository)} target="_blank" rel="noopener" class="mp-link">Repository</a>{/if}
        </div>
      </div>
    {/if}

  {:else}
    <div class="mp-bar">
      <button class="mp-btn" onclick={handleCheckUpdates} disabled={checking}>
        {checking ? 'Checking...' : 'Check for Updates'}
      </button>
    </div>

    {#if installedLoading}
      <div class="mp-state">Loading installed plugins...</div>
    {:else if installedError}
      <div class="mp-state mp-err">{installedError}</div>
    {:else if installed.length === 0}
      <div class="mp-state">No plugins installed. Browse the catalog to get started.</div>
    {:else}
      <div class="mp-list">
        {#each installed as p (p.id)}
          <div class="mp-item">
            <div class="mp-item-info">
              <div class="mp-row">
                <span class="mp-name">{p.name}</span>
                <span class="mp-ver">v{p.version}</span>
                {#if p.hasUpdate && p.latestVersion}<span class="mp-upd-badge">v{p.latestVersion} available</span>{/if}
              </div>
              <div class="mp-author">by {p.author}</div>
              <div class="mp-desc">{p.description}</div>
              {#if p.permissions.length > 0}
                <div class="mp-perms">{#each p.permissions as pm}<span class="mp-perm">{pm}</span>{/each}</div>
              {/if}
            </div>
            <div class="mp-item-acts">
              {#if p.hasUpdate}
                <button class="mp-btn primary" disabled={updating.has(p.id)} onclick={() => handleUpdate(p.id)}>
                  {updating.has(p.id) ? 'Updating...' : 'Update'}
                </button>
              {/if}
              {#if confirmUninstall === p.id}
                <button class="mp-btn danger" onclick={() => handleUninstall(p.id)}>Confirm</button>
                <button class="mp-btn" onclick={() => (confirmUninstall = null)}>Cancel</button>
              {:else}
                <button class="mp-btn danger" disabled={uninstalling.has(p.id)}
                  onclick={() => (confirmUninstall = p.id)}>
                  {uninstalling.has(p.id) ? 'Removing...' : 'Uninstall'}
                </button>
              {/if}
            </div>
          </div>
        {/each}
      </div>
    {/if}
  {/if}
</div>

<style>
  .mp-root { position: relative; display: flex; flex-direction: column; gap: 0.75rem; padding: 0.75rem; height: 100%; overflow-y: auto; color: var(--ctp-text); font-family: var(--ui-font-family, system-ui, sans-serif); font-size: var(--ui-font-size, 0.8125rem); }
  .mp-toast { position: fixed; top: 0.75rem; right: 0.75rem; padding: 0.5rem 1rem; background: var(--ctp-surface1); color: var(--ctp-text); border: 1px solid var(--ctp-surface2); border-radius: 0.375rem; font-size: 0.75rem; z-index: 999; animation: mp-fi 0.15s ease-out; }
  @keyframes mp-fi { from { opacity: 0; transform: translateY(-0.5rem); } }
  .mp-tabs { display: flex; gap: 0.25rem; border-bottom: 1px solid var(--ctp-surface0); padding-bottom: 0.25rem; }
  .mp-tab { padding: 0.375rem 0.75rem; background: none; border: none; border-bottom: 2px solid transparent; color: var(--ctp-subtext0); cursor: pointer; font-size: 0.8125rem; }
  .mp-tab:hover { color: var(--ctp-text); }
  .mp-tab.active { color: var(--ctp-blue); border-bottom-color: var(--ctp-blue); }
  .mp-search { display: flex; }
  .mp-input { flex: 1; padding: 0.375rem 0.625rem; background: var(--ctp-surface0); border: 1px solid var(--ctp-surface1); border-radius: 0.375rem; color: var(--ctp-text); font-size: 0.8125rem; outline: none; }
  .mp-input::placeholder { color: var(--ctp-overlay0); }
  .mp-input:focus { border-color: var(--ctp-blue); }
  .mp-state { padding: 2rem; text-align: center; color: var(--ctp-subtext0); }
  .mp-err { color: var(--ctp-red); }
  .mp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: 0.5rem; }
  .mp-card { display: flex; flex-direction: column; gap: 0.25rem; padding: 0.625rem; background: var(--ctp-surface0); border: 1px solid var(--ctp-surface1); border-radius: 0.375rem; cursor: pointer; transition: border-color 0.1s; }
  .mp-card:hover { border-color: var(--ctp-blue); }
  .mp-row { display: flex; align-items: baseline; gap: 0.375rem; }
  .mp-name { font-weight: 600; color: var(--ctp-text); }
  .mp-ver { font-size: 0.6875rem; color: var(--ctp-overlay0); font-family: monospace; }
  .mp-author { font-size: 0.6875rem; color: var(--ctp-subtext0); }
  .mp-desc { font-size: 0.75rem; color: var(--ctp-subtext1); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .mp-meta { display: flex; justify-content: space-between; align-items: center; gap: 0.25rem; }
  .mp-tags { display: flex; gap: 0.25rem; flex-wrap: wrap; }
  .mp-tag { font-size: 0.625rem; padding: 0.0625rem 0.375rem; background: color-mix(in srgb, var(--ctp-blue) 15%, transparent); color: var(--ctp-blue); border-radius: 0.25rem; }
  .mp-stats { display: flex; gap: 0.5rem; font-size: 0.6875rem; color: var(--ctp-overlay1); }
  .mp-stars { color: var(--ctp-yellow); letter-spacing: 0.05em; }
  .mp-perms { display: flex; gap: 0.25rem; flex-wrap: wrap; }
  .mp-perm { font-size: 0.625rem; padding: 0.0625rem 0.375rem; background: color-mix(in srgb, var(--ctp-peach) 12%, transparent); color: var(--ctp-peach); border-radius: 0.25rem; }
  .mp-actions { margin-top: 0.25rem; display: flex; justify-content: flex-end; }
  .mp-btn { padding: 0.25rem 0.75rem; border: 1px solid var(--ctp-surface1); border-radius: 0.25rem; background: var(--ctp-surface0); color: var(--ctp-subtext0); cursor: pointer; font-size: 0.75rem; }
  .mp-btn:hover:not(:disabled) { background: var(--ctp-surface1); color: var(--ctp-text); }
  .mp-btn:disabled { opacity: 0.4; cursor: default; }
  .mp-btn.primary { background: var(--ctp-blue); color: var(--ctp-base); border-color: var(--ctp-blue); }
  .mp-btn.primary:hover:not(:disabled) { opacity: 0.9; background: var(--ctp-blue); color: var(--ctp-base); }
  .mp-btn.danger { color: var(--ctp-red); border-color: var(--ctp-red); background: none; }
  .mp-btn.danger:hover:not(:disabled) { background: color-mix(in srgb, var(--ctp-red) 12%, transparent); color: var(--ctp-red); }
  .mp-x { background: none; border: none; color: var(--ctp-overlay0); cursor: pointer; font-size: 1rem; padding: 0.125rem 0.375rem; }
  .mp-x:hover { color: var(--ctp-text); }
  .mp-detail { background: var(--ctp-mantle); border: 1px solid var(--ctp-surface1); border-radius: 0.375rem; padding: 0.75rem; display: flex; flex-direction: column; gap: 0.5rem; }
  .mp-detail-hdr { display: flex; justify-content: space-between; align-items: center; }
  .mp-detail-name { margin: 0; font-size: 1rem; font-weight: 600; color: var(--ctp-text); }
  .mp-detail-desc { font-size: 0.8125rem; color: var(--ctp-subtext1); margin: 0; line-height: 1.5; }
  .mp-fields { display: flex; flex-wrap: wrap; gap: 0.375rem 1rem; font-size: 0.75rem; color: var(--ctp-subtext1); }
  .mp-fields b { color: var(--ctp-subtext0); font-weight: 500; margin-right: 0.25rem; }
  .mp-section-hd { font-size: 0.6875rem; color: var(--ctp-subtext0); text-transform: uppercase; letter-spacing: 0.04em; }
  .mp-links { display: flex; gap: 0.75rem; }
  .mp-link { font-size: 0.75rem; color: var(--ctp-blue); text-decoration: none; }
  .mp-link:hover { text-decoration: underline; }
  .mp-bar { display: flex; justify-content: flex-end; }
  .mp-list { display: flex; flex-direction: column; gap: 0.375rem; }
  .mp-item { display: flex; justify-content: space-between; align-items: flex-start; gap: 0.75rem; padding: 0.625rem; background: var(--ctp-surface0); border: 1px solid var(--ctp-surface1); border-radius: 0.375rem; }
  .mp-item-info { flex: 1; display: flex; flex-direction: column; gap: 0.25rem; }
  .mp-upd-badge { font-size: 0.625rem; padding: 0.0625rem 0.375rem; background: color-mix(in srgb, var(--ctp-green) 15%, transparent); color: var(--ctp-green); border-radius: 0.25rem; }
  .mp-item-acts { display: flex; gap: 0.375rem; flex-shrink: 0; align-items: center; }
</style>
