<template>
  <div>
    <div class="title-container">
      <span class="main-title">Agent Leadboard</span>
    </div>
    <AssetsFilter v-model="filters.assets" :assetOptions="assetOptions" />
    <div v-if="loading" class="loading-overlay">
      <div class="loading-box">
        <ProgressSpinner />
        <div class="mt-3 text-600">Loading Paper Trading Agents...</div>
      </div>
    </div>
    <div class="page-wrapper">
      <div class="p-3 flex flex-column gap-2">
        <!-- Mobile filter button -->
        <div class="mobile-filter-btn-container">
          <Button icon="pi pi-filter" label="Filters" @click="drawerVisible = true" size="small" rounded class="mobile-filter-btn" />
        </div>

        <!-- Drawer for mobile -->
        <Drawer v-model:visible="drawerVisible" header="Filter Matrix" position="left" class="filter-drawer">
          <AgentFilters v-model="filters" :nameOptions="nameOptions" :assetOptions="assetOptions" :modelOptions="modelOptions" :strategyOptions="strategyOptions" :dateBounds="dateBounds" />
        </Drawer>

        <div class="flex gap-2 align-items-stretch">
          <!-- Desktop filter panel -->
          <div class="col-panel desktop-filter-panel" style="flex: 1; min-width: 280px;">
            <Card class="mb-2 card-full compact-card content-card">
              <template #title>
                <div class="mb-4 text-900" style="font-size: 20px; font-weight: 600">Filter Matrix</div>
                <Divider />
              </template>
              <template #content>
                <div>
                  <AgentFilters v-model="filters" :nameOptions="nameOptions" :assetOptions="assetOptions" :modelOptions="modelOptions" :strategyOptions="strategyOptions" :dateBounds="dateBounds" />
                </div>
              </template>
            </Card>
          </div>

          <div class="col-panel" style="flex: 4; min-width: 0;">
        <Card class="mb-2 card-full compact-card content-card">
          <template #content>
            <div v-if="filteredRows.length === 0" class="empty-offset">
              <span class="text-600">No data found</span>
            </div>
            <div v-else>
              <div class="flex align-items-center justify-content-between mb-2">
                <div class="flex align-items-center gap-2">
                  <span class="p-overlay-badge">
                    <Button label="Refresh" icon="pi pi-refresh" size="small" rounded @click="forceRefresh()" v-tooltip.right="'Force Refresh, clear all caches and reload from remote'" />
                  </span>
                  <div class="text-500 ml-2" @click="onUpdatedClick">Last updated: {{ lastUpdatedDisplay }}</div>
                  <div class="flex align-items-center gap-2 ml-2" v-if="requestButtonVisible">
                    <Button label="Request Assets" class="p-button-outlined" size="small" rounded @click="requestAssetsVisible = true" />
                  </div>
                </div>
                <div class="flex gap-2">
                  <Button v-if="!selectMode" label="Compare" class="p-button-outlined" size="small" rounded @click="enterSelectMode()" />
                  <template v-else>
                    <Button label="Cancel" class="p-button-text" size="small" rounded severity="secondary" @click="exitSelectMode()" />
                    <Button :disabled="selectedRows.length===0" label="Compare Selected" icon="pi pi-chart-line" size="small" rounded @click="openCompareDialog()" />
                  </template>
                </div>
              </div>
              <AgentTable :rows="filteredRows" :loading="loading" :selectable="selectMode" v-model:selection="selectedRows" />
            </div>
            
          </template>
        </Card>
        </div>
      </div>
    </div>
  </div>
  </div>
  <Dialog v-model:visible="compareVisible" modal header="Equity Curve Comparison" style="width: 90vw; max-width: 1200px">
    <div>
      <CompareChart :selected="selectedRows.map(r => ({ agent_name: r.agent_name, asset: r.asset, model: r.model, strategy: r.strategy, decision_ids: r.decision_ids || [] }))" :visible="compareVisible" />
    </div>
  </Dialog>
  <Dialog v-model:visible="requestAssetsVisible" modal header="Request Asset" style="width: 90vw; max-width: 400px">
    <div class="flex justify-content-between gap-2">
      <InputText v-model="asset" placeholder="Asset need adding..." />
      <Button label="Request" size="small" rounded @click="requestAsset()" :disabled="!asset" />
    </div>
  </Dialog>
</template>

<script>
import { dataService } from '../lib/dataService.js'
import AgentTable from '../components/AgentTable.vue'
import AgentFilters from '../components/AgentFilters.vue'
import AssetsFilter from '../components/AssetsFilter.vue'
import CompareChart from '../components/CompareChart.vue'
import InputText from 'primevue/inputtext'
import Dialog from 'primevue/dialog'
import { countNonTradingDaysBetweenForAsset, countTradingDaysBetweenForAsset } from '../lib/marketCalendar.js'
import { computeBuyHoldEquity, computeStrategyEquity, calculateMetricsFromSeries, computeWinRate } from '../lib/perf.js'
import { STRATEGIES } from '../lib/strategies.js'
import emailjs from 'emailjs-com'

export default {
  name: 'LeadboardView',
  components: { AgentTable, AgentFilters, AssetsFilter, CompareChart, Dialog, InputText },
  data() {
    return {
      loading: true,
      agents: [],
      tableRows: [],
      lastUpdated: null,
      filters: { names: [], assets: [], models: [], strategies: [], dates: [] },
      dateBounds: { min: null, max: null },
      dateFilteredRows: [],
      nameOptions: [],
      assetOptions: [],
      modelOptions: [],
      strategyOptions: [],
      selectMode: false,
      selectedRows: [],
      compareVisible: false,
      requestAssetsVisible: false,
      requestButtonVisible: false,
      updatedClickCount: 0,
      asset: '',
      drawerVisible: false,
      unsubscribe: null
    }
  },
  watch: {
    // persist filters to sessionStorage for cross-page initialization
    filters: {
      deep: true,
      handler(val){
        try { sessionStorage.setItem('homeFilters', JSON.stringify(val || {})) } catch(_) {}
        // if filters change while comparing, reset to initial state
        if (this.selectMode || this.compareVisible) {
          this.exitSelectMode()
        }
        // recompute on date range change
        try {
          const ds = (val && Array.isArray(val.dates)) ? val.dates : []
          if (ds.length === 2 && ds[0] && ds[1]) {
            const start = new Date(Math.min(new Date(ds[0]).getTime(), new Date(ds[1]).getTime()))
            const end = new Date(Math.max(new Date(ds[0]).getTime(), new Date(ds[1]).getTime()))
            this.recomputeAllForRange(start, end)
          } else {
            this.dateFilteredRows = []
          }
        } catch(_) {}
      }
    }
  },
  computed: {
    lastUpdatedDisplay() {
      return this.lastUpdated ? new Date(this.lastUpdated).toLocaleString() : '-'
    },
    filteredRows() {
      // base rows come from date-filtered recomputation when a range is active
      const useDateRange = Array.isArray(this.filters.dates) && this.filters.dates.length === 2 && this.filters.dates[0] && this.filters.dates[1]
      let rows = useDateRange ? (this.dateFilteredRows || []) : this.tableRows
      const cols = ['names','assets','models','strategies']
      for (const c of cols) { if (!this.filters[c] || this.filters[c].length === 0) return [] }
      rows = rows.filter(r => this.filters.names.includes(r.agent_name))
      rows = rows.filter(r => this.filters.assets.includes(r.asset))
      rows = rows.filter(r => this.filters.models.includes(r.model))
      rows = rows.filter(r => this.filters.strategies.includes(r.strategy))
      return rows
    }
  },
  methods: {
    /**
     * 从 dataService 同步状态
     */
    syncFromDataService(state) {
      this.loading = state.loading
      this.agents = state.agents
      this.tableRows = state.tableRows
      this.lastUpdated = state.lastUpdated
      this.dateBounds = state.dateBounds
      
      // 只在选项为空时才从服务同步
      if (!this.nameOptions.length) this.nameOptions = state.nameOptions
      if (!this.assetOptions.length) this.assetOptions = state.assetOptions
      if (!this.modelOptions.length) this.modelOptions = state.modelOptions
      if (!this.strategyOptions.length) this.strategyOptions = state.strategyOptions
      
      // 初始化 filters（全选）
      if (!this.filters.names.length && state.nameOptions.length) {
        this.filters.names = state.nameOptions.map(o => o.value)
      }
      if (!this.filters.assets.length && state.assetOptions.length) {
        this.filters.assets = state.assetOptions.map(o => o.value)
      }
      if (!this.filters.models.length && state.modelOptions.length) {
        this.filters.models = state.modelOptions.map(o => o.value)
      }
      if (!this.filters.strategies.length && state.strategyOptions.length) {
        this.filters.strategies = ['long_only']  // 默认只显示 Aggressive Long Only
      }
    },
    onUpdatedClick(){
      try {
        this.updatedClickCount = (this.updatedClickCount || 0) + 1
        if (this.updatedClickCount >= 5) {
          this.requestButtonVisible = true
          this.updatedClickCount = 0
        }
      } catch(_) {}
    },
    async recomputeRowForRange(row, start, end){
      try {
        const isCrypto = row.asset === 'BTC' || row.asset === 'ETH'
        const seriesAll = Array.isArray(row.series) ? row.series : []
        const inRange = seriesAll.filter(p => {
          const d = new Date(p.date)
          return d >= start && d <= end
        })
        if (!inRange.length) return null
        // Get correct strategy config - row.strategy is the ID, we need the strategy type
        const strategyCfg = (STRATEGIES || []).find(s => s.id === row.strategy) || { strategy: 'long_short', tradingMode: 'normal', fee: 0.0005 }
        const st = computeStrategyEquity(inRange, 100000, strategyCfg.fee, strategyCfg.strategy, strategyCfg.tradingMode)
        const stNoFee = computeStrategyEquity(inRange, 100000, 0, strategyCfg.strategy, strategyCfg.tradingMode)
        const metrics = calculateMetricsFromSeries(st, isCrypto ? 'crypto' : 'stock')
        const metricsNoFee = calculateMetricsFromSeries(stNoFee, isCrypto ? 'crypto' : 'stock')
        const { winRate, trades } = computeWinRate(inRange, strategyCfg.strategy, strategyCfg.tradingMode)
        const bhSeries = computeBuyHoldEquity(inRange, 100000)
        const buy_hold = bhSeries.length ? (bhSeries[bhSeries.length - 1] - bhSeries[0]) / bhSeries[0] : 0
        const dates = inRange.map(s => s.date).filter(Boolean).sort()
        const start_date = dates[0] || row.start_date
        const end_date = dates[dates.length - 1] || row.end_date
        let trading_days = 0
        let closed_days = 0
        if (isCrypto) {
          trading_days = Math.max(0, Math.floor((new Date(end_date) - new Date(start_date)) / 86400000) + 1)
          closed_days = 0
        } else {
          // accurate counts via calendar utils
          trading_days = await countTradingDaysBetweenForAsset(row.asset, start_date, end_date)
          closed_days = await countNonTradingDaysBetweenForAsset(row.asset, start_date, end_date)
        }
        return {
          ...row,
          decision_ids: inRange.map(r => r.id).filter(Boolean),
          balance: st.length ? st[st.length - 1] : 100000,
          ret_with_fees: metrics.total_return / 100,
          ret_no_fees: metricsNoFee.total_return / 100,
          buy_hold,
          vs_bh_with_fees: (metrics.total_return / 100) - buy_hold,
          sharpe: metrics.sharpe_ratio,
          win_rate: winRate,
          trades,
          start_date,
          end_date,
          trading_days,
          closed_days,
          closed_date: closed_days
        }
      } catch(_) { return row }
    },
    async recomputeAllForRange(start, end){
      try {
        const tasks = (this.tableRows || []).map(r => this.recomputeRowForRange(r, start, end))
        const res = await Promise.all(tasks)
        this.dateFilteredRows = (res || []).filter(Boolean)
      } catch(_) {
        this.dateFilteredRows = []
      }
    },
    enterSelectMode(){ this.selectMode = true; this.selectedRows = [] },
    exitSelectMode(){ this.selectMode = false; this.selectedRows = []; this.compareVisible = false },
    openCompareDialog(){ this.compareVisible = true },
    goCompare(){
      try {
        const payload = (this.selectedRows || []).map(r => ({
          agent_name: r.agent_name,
          asset: r.asset,
          model: r.model,
          strategy: r.strategy,
          decision_ids: Array.isArray(r.decision_ids) ? r.decision_ids : []
        }))
        console.log('[Compare] selectedRows:', this.selectedRows)
        console.log('[Compare] payload size:', payload.length, payload)
        sessionStorage.setItem('compareRows', JSON.stringify(payload))
      } catch(_) {}
      // keep old navigation path available, but we now prefer dialog view
      // this.$router.push('/equity-comparison')
    },
    requestAsset(){
      console.log('[Request Asset] asset:', this.asset)
      const svc = import.meta.env.VITE_EMAILJS_SERVICE_ID
      const tpl = import.meta.env.VITE_EMAILJS_TEMPLATE_ID
      if (!this.asset || !svc || !tpl) { this.requestAssetsVisible = false; this.asset=''; return }
      try {
        emailjs.send(svc, tpl, { title: String(this.asset), message: String(this.asset) })
      } catch(_) {}
      this.asset = ''
      this.requestAssetsVisible = false
    },
    async forceRefresh(){
      // reset compare state on refresh
      try { this.exitSelectMode() } catch(_) {}
      // 使用 dataService 强制刷新
      await dataService.forceRefresh()
    }
  },
  mounted() {
    // 订阅 dataService 状态变化
    this.unsubscribe = dataService.subscribe((state) => {
      this.syncFromDataService(state)
    })
    
    // 立即同步当前状态
    this.syncFromDataService(dataService.getState())
    
    // 如果数据还没加载，触发加载
    if (!dataService.loaded && !dataService.loading) {
      dataService.load()
    }
  },
  beforeUnmount() {
    // 取消订阅
    if (this.unsubscribe) {
      this.unsubscribe()
      this.unsubscribe = null
    }
  }
}
</script>

<style scoped>
.page-wrapper {
    max-width: 1600px;
    margin: 0 auto;
    padding: 0 1rem 1rem 1rem;
}

.title-container {
    text-align: center;
}

.main-title {
    font-size: 2rem;
    letter-spacing: -0.02em;
    font-weight: 800;
    color: #1f1f33;
}
.loading-overlay {
  position: fixed;
  inset: 0;
  background: rgba(255, 255, 255, 0.85);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.loading-box { text-align: center; }

.card--with-divider :deep(.p-card-title) {
  border-bottom: 1px solid var(--surface-200);
  padding-bottom: 0.75rem;
  margin-bottom: 0.75rem;
}

/* equal-height for side-by-side cards */
.col-panel { display: flex; }
.card-full { width: 100%; display: flex; flex-direction: column; }
.card-full :deep(.p-card-body) { display: flex; flex-direction: column; height: 100%; }
.card-full :deep(.p-card-content) { flex: 1; display: flex; flex-direction: column; }

/* compact spacing for higher information density */
.compact-card :deep(.p-card-body) { padding: 0.75rem; }
.compact-card :deep(.p-card-content) { padding-top: 0; padding-bottom: 0; overflow-y: auto; }
.compact-card :deep(.p-card-title) { margin-bottom: 0.5rem; }

/* datatable compact paddings */
:deep(.p-datatable .p-datatable-header) { padding: 0.5rem 0.75rem; }
:deep(.p-datatable .p-datatable-footer) { padding: 0.5rem 0.75rem; }
:deep(.p-datatable .p-datatable-thead > tr > th) { padding: 0.5rem 0.5rem; font-size: 0.9rem; }
:deep(.p-datatable .p-datatable-tbody > tr > td) { padding: 0.4rem 0.5rem; font-size: 0.9rem; line-height: 1.2; }

/* multiselect and inputs in filter card */
.compact-card :deep(.p-inputtext),
.compact-card :deep(.p-multiselect) { font-size: 0.9rem; }
.compact-card :deep(.p-inputtext) { padding: 0.35rem 0.5rem; }
.compact-card :deep(.p-multiselect .p-multiselect-label) { padding: 0.35rem 0.5rem; }
.compact-card :deep(.p-multiselect .p-multiselect-trigger) { width: 1.5rem; }

/* dialog compact header/content on this page */
:deep(.p-dialog .p-dialog-header) { padding: 0.6rem 0.9rem; }
:deep(.p-dialog .p-dialog-content) { padding: 0.6rem 0.9rem; }

/* empty state position: lower center (about 65% height) */
.empty-offset { flex: 1; min-height: 280px; position: relative; }
.empty-offset > span {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
}

/* Content cards with adaptive height */
.content-card {
  height: calc(100vh - 280px);
  min-height: 500px;
  max-height: 850px;
}

/* Responsive design for mobile */
.mobile-filter-btn-container {
  display: none;
}

@media (max-width: 768px) {
  /* Show mobile filter button */
  .mobile-filter-btn-container {
    display: flex;
    justify-content: flex-start;
    margin-bottom: 1rem;
  }
  
  /* Hide desktop filter panel */
  .desktop-filter-panel {
    display: none !important;
  }
  
  /* Adjust main content to full width on mobile */
  .col-panel {
    flex: 1 !important;
    min-width: 0 !important;
  }
  
  /* Adjust card height for mobile */
  .content-card {
    height: calc(100vh - 350px);
    min-height: 400px;
    max-height: none;
  }
}

/* Landscape mode optimization */
@media (max-height: 600px) and (orientation: landscape) {
  .content-card {
    height: calc(100vh - 180px);
    min-height: 300px;
    max-height: none;
  }
  
  .page-wrapper {
    padding: 0 1rem 0.5rem 1rem;
  }
  
  .title-container {
    margin-bottom: 0.5rem;
  }
  
  .main-title {
    font-size: 1.5rem;
  }
}

/* Drawer custom styles */
:deep(.filter-drawer) {
  width: 320px;
  max-width: 85vw;
}

:deep(.filter-drawer .p-drawer-content) {
  padding: 1rem;
}
</style>

