<template>
  <div class="marketplace-container">
    <!-- Header Section -->
    <div>
      <!-- Desktop: tabs + categories + search -->
      <div class="hidden md:flex mb-4 items-center gap-4">
        <div class="mode-switcher flex-shrink-0">
          <RouterLink to="/dashboard/apps" class="mode-switcher-btn">My Apps</RouterLink>
          <RouterLink to="/dashboard/discover" class="mode-switcher-btn mode-switcher-btn-active">App Store</RouterLink>
          <RouterLink to="/dashboard/apps?tab=services" class="mode-switcher-btn">Services</RouterLink>
        </div>
        <div class="mode-switcher flex-shrink-0">
          <RouterLink
            to="/dashboard/discover"
            class="mode-switcher-btn"
            :class="{ 'mode-switcher-btn-active': $route.path === '/dashboard/discover' }"
          >Discover</RouterLink>
          <button
            v-for="category in categoriesWithApps"
            :key="category.id"
            @click="selectCategory(category.id)"
            class="mode-switcher-btn"
            :class="{ 'mode-switcher-btn-active': selectedCategory === category.id }"
          >
            {{ category.name }}
            <span v-if="category.id === 'nostr' && nostrApps.length > 0" class="ml-1 text-xs px-1.5 py-0.5 rounded-full bg-white/10">+{{ nostrApps.length }}</span>
          </button>
        </div>
        <input
          v-model="searchQuery"
          type="text"
          :placeholder="t('marketplace.searchPlaceholder')"
          :aria-label="t('marketplace.searchApps')"
          class="flex-1 px-4 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/50 focus:outline-none focus:border-white/40 transition-colors"
        />
      </div>

      <!-- Mobile: search (tabs handled by Dashboard.vue header) -->
      <div class="md:hidden mb-4">
        <input
          v-model="searchQuery"
          type="text"
          :placeholder="t('marketplace.searchPlaceholder')"
          :aria-label="t('marketplace.searchApps')"
          class="w-full px-4 py-3 md:py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/50 focus:outline-none focus:border-white/40 transition-colors"
        />
      </div>
    </div>

    <!-- Scrollable Apps Section -->
    <div class="pb-8">
      <!-- Community Load Error -->
      <div v-if="communityError" class="alert-error mb-4">
        {{ communityError }}
        <button @click="loadCommunityMarketplace()" class="ml-2 underline hover:no-underline">Retry</button>
      </div>

      <!-- Apps Grid -->
      <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
        <MarketplaceAppCard
          v-for="(app, index) in filteredApps"
          :key="app.id"
          :app="app"
          :index="index"
          :stagger="showStagger"
          :installed="isInstalled(app.id)"
          :installing="installingApps.has(app.id)"
          :install-progress="installingApps.get(app.id)"
          :installed-state="getInstalledState(app.id)"
          :starting-up="isStartingUp(app.id)"
          :containers-scanned="containersScanned"
          :tier-label="getAppTier(app.id)"
          @view="viewAppDetails"
          @install="app.source === 'local' ? installApp(app) : installCommunityApp(app)"
          @launch="launchInstalledApp"
        />
      </div>

      <!-- Empty State -->
      <div v-if="filteredApps.length === 0" class="text-center py-12">
        <div v-if="loadingCommunity || nostrLoading" class="flex flex-col items-center gap-4">
          <svg class="animate-spin h-12 w-12 text-blue-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
            <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
            <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
          </svg>
          <p class="text-white/70">{{ nostrLoading ? t('marketplace.queryingRelays') : t('common.loading') }}</p>
        </div>
        <div v-else-if="nostrError && selectedCategory === 'nostr'" class="flex flex-col items-center gap-4">
          <p class="text-white/70">{{ t('marketplace.noCommunityApps') }}</p>
          <p class="text-white/40 text-sm">{{ nostrError }}</p>
          <button @click="nostrApps = []; loadNostrMarketplace()" class="px-4 py-2 glass-button rounded-lg text-sm">{{ t('common.retry') }}</button>
        </div>
        <p v-else class="text-white/70">{{ searchQuery && selectedCategory !== 'all' ? t('marketplace.noResults', { category: categories.find(c => c.id === selectedCategory)?.name, query: searchQuery }) : searchQuery ? t('marketplace.noResultsSearch', { query: searchQuery }) : t('marketplace.noResultsCategory', { category: categories.find(c => c.id === selectedCategory)?.name }) }}</p>
      </div>
    </div>
    <!-- End Scrollable Apps Section -->

    <MarketplaceFilterModal
      :categories="categoriesWithApps"
      :selected-category="selectedCategory"
      @select="selectCategory"
    />
  </div>
</template>

<script lang="ts">
let marketplaceAnimationDone = false
</script>

<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { useRouter, useRoute, RouterLink } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app'
import { useServerStore } from '@/stores/server'
import { rpcClient } from '@/api/rpc-client'
import { useMarketplaceApp } from '@/composables/useMarketplaceApp'
import { useAppLauncherStore } from '@/stores/appLauncher'
import { useToast } from '@/composables/useToast'
import MarketplaceAppCard from './marketplace/MarketplaceAppCard.vue'
import MarketplaceFilterModal from './marketplace/MarketplaceFilterModal.vue'
import {
  type MarketplaceApp,
  INSTALLED_ALIASES,
  getAppTier,
  categorizeCommunityApp,
  getCuratedAppList,
} from './marketplace/marketplaceData'

const router = useRouter()
const route = useRoute()
const store = useAppStore()
const server = useServerStore()
const { t } = useI18n()

const showStagger = !marketplaceAnimationDone
const { setCurrentApp } = useMarketplaceApp()
const appLauncher = useAppLauncherStore()
const toast = useToast()

// Category state — read initial value from query param (set by Discover page navigation)
const selectedCategory = ref((route.query.category as string) || 'all')

const categories = computed(() => [
  { id: 'all', name: t('marketplace.all') },
  { id: 'community', name: t('marketplace.community') },
  { id: 'nostr', name: 'Nostr' },
  { id: 'commerce', name: t('marketplace.commerce') },
  { id: 'money', name: t('marketplace.money') },
  { id: 'data', name: t('marketplace.data') },
  { id: 'home', name: t('marketplace.homeCategory') },
  { id: 'car', name: t('marketplace.auto') },
  { id: 'networking', name: t('marketplace.networking') },
  { id: 'l484', name: 'L484' },
  { id: 'other', name: t('marketplace.other') }
])

// Installation state — uses global store so it persists across navigation
const installingApps = server.installingApps

// Install progress tracking is now in serverStore (global watcher on WebSocket data)
// so it works regardless of which page is active

// Select category and trigger Nostr relay discovery when 'nostr' is chosen
function selectCategory(id: string) {
  selectedCategory.value = id
  if (id === 'nostr' && nostrApps.value.length === 0 && !nostrLoading.value) {
    loadNostrMarketplace()
  }
}

// Community marketplace state
const loadingCommunity = ref(false)
const communityError = ref('')
const communityApps = ref<MarketplaceApp[]>([])
const searchQuery = ref('')

// Nostr community marketplace state
const nostrApps = ref<MarketplaceApp[]>([])
const nostrLoading = ref(false)
const nostrError = ref('')

async function loadNostrMarketplace() {
  if (nostrApps.value.length > 0 || nostrLoading.value) return
  nostrLoading.value = true
  nostrError.value = ''
  try {
    const res = await rpcClient.marketplaceDiscover()
    nostrApps.value = res.apps.map(app => ({
      id: app.manifest.app_id,
      title: app.manifest.name,
      version: app.manifest.version,
      description: typeof app.manifest.description === 'string'
        ? app.manifest.description
        : app.manifest.description,
      icon: app.manifest.icon_url || '',
      author: app.manifest.author.name,
      dockerImage: app.manifest.container.image,
      repoUrl: app.manifest.repo_url,
      category: app.manifest.category,
      source: 'nostr',
      trustScore: app.trust_score,
      trustTier: app.trust_tier,
      relayCount: app.relay_count,
    }))
  } catch (e) {
    nostrError.value = e instanceof Error ? e.message : 'Discovery failed'
    if (import.meta.env.DEV) console.warn('Nostr marketplace discovery failed:', e)
  } finally {
    nostrLoading.value = false
  }
}

const installedPackages = computed(() => {
  return store.data?.['package-data'] || {}
})

const containersScanned = computed(() => {
  return store.data?.['server-info']?.['status-info']?.['containers-scanned'] ?? false
})

// Combine curated apps with Nostr relay-discovered apps
const allApps = computed(() => {
  const local: (MarketplaceApp & { category: string; source: string })[] = []

  const community = communityApps.value.map(app => {
    const category = categorizeCommunityApp(app)
    return { ...app, category, source: 'community' }
  })

  const base = [...local, ...community]

  if (nostrApps.value.length > 0) {
    const existingIds = new Set(base.map(a => a.id))
    const nostrMerged = nostrApps.value
      .filter(app => !existingIds.has(app.id))
      .map(app => {
        const category = app.category || categorizeCommunityApp(app)
        return { ...app, category, source: 'nostr' }
      })
    return [...base, ...nostrMerged]
  }

  return base
})

const categoriesWithApps = computed(() => {
  const apps = allApps.value
  return categories.value.filter(cat => {
    if (cat.id === 'all') return apps.length > 0
    return apps.some(app => app.category === cat.id)
  })
})

const filteredApps = computed(() => {
  let apps = allApps.value

  if (selectedCategory.value && selectedCategory.value !== 'all') {
    apps = apps.filter(app => app.category === selectedCategory.value)
  }

  if (searchQuery.value) {
    const query = searchQuery.value.toLowerCase()
    apps = apps.filter(app =>
      app.title?.toLowerCase().includes(query) ||
      (typeof app.description === 'string' && app.description.toLowerCase().includes(query)) ||
      (typeof app.description === 'object' && app.description?.short?.toLowerCase().includes(query)) ||
      app.id?.toLowerCase().includes(query) ||
      app.author?.toLowerCase().includes(query)
    )
  }

  // Hide installed, installing, and web-only apps (no dockerImage = not installable)
  apps = apps.filter(app => !isInstalled(app.id) && !installingApps.has(app.id) && app.dockerImage)

  return apps
})

function isInstalled(appId: string): boolean {
  if (appId in installedPackages.value) return true
  const aliases = INSTALLED_ALIASES[appId]
  return aliases ? aliases.some((a) => a in installedPackages.value) : false
}

function getInstalledState(appId: string): string | null {
  const pkg = installedPackages.value[appId]
  if (pkg) return pkg.state
  const aliases = INSTALLED_ALIASES[appId]
  if (aliases) {
    for (const a of aliases) {
      const aliasPkg = installedPackages.value[a]
      if (aliasPkg) return aliasPkg.state
    }
  }
  return null
}

function isStartingUp(appId: string): boolean {
  const state = getInstalledState(appId)
  return state !== null && state !== 'running' && state !== 'stopped' && state !== 'exited'
}

function launchInstalledApp(app: MarketplaceApp) {
  appLauncher.openSession(app.id)
}

onMounted(() => {
  marketplaceAnimationDone = true
  if (communityApps.value.length === 0 && !loadingCommunity.value) {
    loadCommunityMarketplace()
  }
})

async function loadCommunityMarketplace() {
  loadingCommunity.value = true
  communityError.value = ''
  if (import.meta.env.DEV) console.log('Loading Docker-based app marketplace')
  communityApps.value = getCuratedAppList()
  loadingCommunity.value = false
}

function viewAppDetails(app: MarketplaceApp) {
  if (import.meta.env.DEV) console.log('[Marketplace] Navigating to app detail:', app)

  try {
    if (isInstalled(app.id)) {
      if (import.meta.env.DEV) console.log('[Marketplace] App is installed, navigating to app details page')
      router.push({ name: 'app-details', params: { id: app.id } })
    } else {
      setCurrentApp(app)
      if (import.meta.env.DEV) console.log('[Marketplace] App data stored in composable')
      router.push({ name: 'marketplace-app-detail', params: { id: app.id } })
    }
  } catch (e) {
    if (import.meta.env.DEV) console.error('[Marketplace] Navigation error:', e)
  }
}

const activeTimers: ReturnType<typeof setTimeout>[] = []

function trackTimeout(fn: () => void, ms: number) {
  const id = setTimeout(() => {
    const idx = activeTimers.indexOf(id)
    if (idx !== -1) activeTimers.splice(idx, 1)
    fn()
  }, ms)
  activeTimers.push(id)
  return id
}

onBeforeUnmount(() => {
  for (const t of activeTimers) clearTimeout(t)
  activeTimers.length = 0
})

function queueInstall(app: MarketplaceApp) {
  server.setInstallProgress(app.id, {
    id: app.id,
    title: app.title ?? app.id,
    status: 'downloading',
    progress: 2,
    message: 'Queued…',
    attempt: 0,
  })
}

function failInstall(app: MarketplaceApp, err: unknown) {
  const message = "Failed: " + (err instanceof Error ? err.message : String(err))
  server.setInstallProgress(app.id, {
    id: app.id,
    title: app.title ?? app.id,
    status: 'error',
    progress: 0,
    message,
    attempt: 0,
  })
  trackTimeout(() => { server.clearInstallProgress(app.id) }, 5000)
}

async function installApp(app: MarketplaceApp) {
  if (installingApps.has(app.id) || isInstalled(app.id)) return

  queueInstall(app)
  toast.info("Installing " + (app.title ?? app.id) + " - check My Apps")
  router.push('/dashboard/apps').catch(() => {})

  try {
    const installUrl = app.url || app.manifestUrl || app.s9pkUrl
    await rpcClient.call({
      method: 'package.install',
      params: { id: app.id, url: installUrl, version: app.version },
      timeout: 15000,
    })
  } catch (err) {
    if (import.meta.env.DEV) console.error('Installation failed:', err)
    failInstall(app, err)
  }
}

async function installCommunityApp(app: MarketplaceApp) {
  if (installingApps.has(app.id) || isInstalled(app.id) || !app.dockerImage) return

  queueInstall(app)
  toast.info("Installing " + (app.title ?? app.id) + " - check My Apps")
  router.push('/dashboard/apps').catch(() => {})

  try {
    await rpcClient.call({
      method: 'package.install',
      params: { id: app.id, dockerImage: app.dockerImage, version: app.version },
      timeout: 15000,
    })
  } catch (err) {
    if (import.meta.env.DEV) console.error('[Marketplace] Installation failed:', err)
    failInstall(app, err)
  }
}

</script>

<style scoped>
/* Custom scrollbar styling for apps section */
.marketplace-container ::-webkit-scrollbar {
  width: 8px;
}

.marketplace-container ::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.05);
  border-radius: 4px;
}

.marketplace-container ::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.2);
  border-radius: 4px;
  transition: background 0.3s ease;
}

.marketplace-container ::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.3);
}

/* Firefox scrollbar */
.marketplace-container {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.2) rgba(255, 255, 255, 0.05);
}
</style>
