<template>
  <div class="workspace-chart-widget">
    <div class="widget-head">
      <div class="widget-title-area">
        <span class="widget-title">{{ displayLabel }}</span>
        <span v-if="chartPg?.last_synced_on" class="widget-subtitle">
          {{ t('Last synced') }} {{ chartPg.last_synced_on }}
        </span>
      </div>
      <div v-if="!editMode" class="widget-actions">
        <template v-if="isTimeseries && !isHeatmap">
          <n-dropdown trigger="click" :options="timeIntervalOptions" @select="handleTimeIntervalChange">
            <n-button size="tiny" quaternary>
              {{ currentTimeInterval || t('Monthly') }}
              <template #icon><Icon icon="tabler:chevron-down" :style="{ fontSize: '12px' }" /></template>
            </n-button>
          </n-dropdown>
          <n-dropdown trigger="click" :options="timespanOptions" @select="handleTimespanChange">
            <n-button size="tiny" quaternary>
              {{ currentTimespan || t('Last Year') }}
              <template #icon><Icon icon="tabler:chevron-down" :style="{ fontSize: '12px' }" /></template>
            </n-button>
          </n-dropdown>
        </template>
        <template v-if="isHeatmap">
          <n-dropdown trigger="click" :options="heatmapYearOptions" @select="handleHeatmapYearChange">
            <n-button size="tiny" quaternary>
              {{ String(currentHeatmapYear || new Date().getFullYear()) }}
              <template #icon><Icon icon="tabler:chevron-down" :style="{ fontSize: '12px' }" /></template>
            </n-button>
          </n-dropdown>
        </template>
        <n-button v-if="showFilterButton" size="tiny" quaternary @click="openFilterDialog">
          <template #icon><Icon icon="tabler:filter" :style="{ fontSize: '14px' }" /></template>
        </n-button>
        <n-dropdown trigger="click" :options="chartMenuOptions" @select="handleMenuAction">
          <n-button size="tiny" quaternary>
            <template #icon><Icon icon="tabler:dots" :style="{ fontSize: '14px' }" /></template>
          </n-button>
        </n-dropdown>
      </div>
    </div>

    <div v-if="showDateRange" class="date-range-picker">
      <n-date-picker
        v-model:value="dateRange"
        type="daterange"
        size="small"
        clearable
        @update:value="handleDateRangeChange"
      />
    </div>

    <n-modal
      v-model:show="filterDialogVisible"
      preset="dialog"
      :title="filterDialogTitle"
      style="width: 500px;"
      :positive-text="t('Set')"
      :negative-text="t('Cancel')"
      @positive-click="applyFilters"
    >
      <div v-if="filterFields.length === 0" class="no-filters">
        {{ t('No Filters Set') }}
      </div>
      <div v-else class="filter-form">
        <div v-for="field in visibleFilterFields" :key="field.fieldname" class="filter-field">
          <label v-if="field.fieldtype !== 'Check'" class="filter-label">{{ t(field.label) }}</label>
          <n-input
            v-if="field.fieldtype === 'Data'"
            v-model:value="filterValues[field.fieldname]"
            :placeholder="field.placeholder || ''"
          />
          <n-select
            v-else-if="field.fieldtype === 'Link'"
            v-model:value="filterValues[field.fieldname]"
            :options="getLinkOptions(field.fieldname)"
            :loading="linkOptionsLoading[field.fieldname]"
            :render-label="renderLinkOption"
            filterable
            clearable
            remote
            :placeholder="`${t('Select')} ${t(field.label)}`"
            @search="(query) => handleLinkSearch(field, query)"
            @focus="() => handleLinkFocus(field)"
            @update:value="(value) => handleLinkSelect(field, value)"
          />
          <n-select
            v-else-if="field.fieldtype === 'Select'"
            v-model:value="filterValues[field.fieldname]"
            :options="parseSelectOptions(field.options)"
            clearable
          />
          <n-date-picker
            v-else-if="field.fieldtype === 'Date'"
            v-model:value="filterValues[field.fieldname]"
            type="date"
            clearable
          />
          <n-checkbox
            v-else-if="field.fieldtype === 'Check'"
            v-model:checked="filterValues[field.fieldname]"
          >
            {{ t(field.label) }}
          </n-checkbox>
          <n-input
            v-else
            v-model:value="filterValues[field.fieldname]"
            :placeholder="field.placeholder || ''"
          />
        </div>
      </div>
    </n-modal>

    <div class="widget-body">
      <div v-if="loading" class="chart-state loading-state">
        <n-spin size="small" />
        <span>{{ t('Loading...') }}</span>
      </div>
      <div v-else-if="error" class="chart-state error-state">
        <Icon icon="tabler:alert-circle" :style="{ fontSize: '24px', color: '#ef4444' }" />
        <span>{{ error }}</span>
      </div>
      <div v-else-if="!hasData" class="chart-state empty-state">
        <Icon icon="tabler:chart-bar-off" :style="{ fontSize: '32px', color: '#94a3b8' }" />
        <span>{{ t('No Data') }}</span>
      </div>
      <ChartRenderer
        v-else
        :data="chartData"
        :config="chartPg"
        :colors="chartColors"
        :height="chartHeight"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, h } from 'vue'
import { useRouter } from 'vue-router'
import { NButton, NDropdown, NSpin, NDatePicker, NModal, NInput, NSelect, NCheckbox } from 'naive-ui'
import { Icon } from '@iconify/vue'
import { t } from '@/shared/i18n'
import { api, searchWidget } from '@/shared/api/common'
import { getReportFilters as getRegisteredFilters } from '@/shared/extensions'
import { pageTypeToSlug } from '@/shared/utils/slug'
import { useChartData } from '@/shared/composables/useChartData'
import ChartRenderer from './ChartRenderer.vue'

const props = withDefaults(defineProps<{
  chartName: string
  label?: string
  chartSettings?: Record<string, any>
  editMode?: boolean
  height?: number
}>(), {
  label: '',
  editMode: false,
  height: 240
})

const router = useRouter()

const {
  chartPg,
  chartData,
  loading,
  error,
  hasData,
  isTimeseries,
  isHeatmap,
  chartColors,
  selectedTimespan,
  selectedTimeInterval,
  selectedHeatmapYear,
  refresh,
  reset,
  setTimespan,
  setTimeInterval,
  setHeatmapYear,
  setDateRange,
  setFilters,
  filters: currentFilters
} = useChartData({
  chartName: props.chartName,
  chartSettings: props.chartSettings
})

const showDateRange = ref(false)
const dateRange = ref<[number, number] | null>(null)

const filterDialogVisible = ref(false)
const filterFields = ref<Array<any>>([])
const filterValues = ref<Record<string, any>>({})
const linkOptionsMap = ref<Record<string, Array<{ label: string; value: string }>>>({})
const linkOptionsLoading = ref<Record<string, boolean>>({})

function getLinkOptions(fieldname: string) {
  return linkOptionsMap.value[fieldname] || []
}

async function handleLinkSearch(field: any, query: string) {
  if (!field.options) return
  
  const fieldname = field.fieldname
  linkOptionsLoading.value[fieldname] = true
  
  try {
    const result = await searchWidget(field.options, query || '', {
      page_length: 20
    })
    
    const normalOptions = result.success && result.data 
      ? result.data.map((item: any) => ({
          label: item.value || item.name,
          value: item.name || item.value
        }))
      : []
    
    // Add "Create new..." option at the end
    linkOptionsMap.value[fieldname] = [
      ...normalOptions,
      {
        label: t('Create a new {0}', [t(field.options)]),
        value: '__create_new__',
        type: 'create'
      }
    ]
  } catch (e) {
    console.error(`[ChartFilter] Failed to search ${field.options}:`, e)
    linkOptionsMap.value[fieldname] = [{
      label: t('Create a new {0}', [t(field.options)]),
      value: '__create_new__',
      type: 'create'
    }]
  } finally {
    linkOptionsLoading.value[fieldname] = false
  }
}

function renderLinkOption(option: any) {
  if (option.type === 'create') {
    return h('div', { style: 'display: flex; align-items: center; color: #18a058;' }, [
      h(Icon, { icon: 'mdi:plus', style: 'margin-right: 6px;' }),
      h('span', option.label)
    ])
  }
  return option.label
}

function handleLinkSelect(field: any, value: string) {
  if (value === '__create_new__') {
    // Open new page to create record
    if (field.options) {
      const slug = pageTypeToSlug(field.options)
      const randomStr = Math.random().toString(36).substring(2, 12)
      const tempName = `new-${slug}-${randomStr}`
      window.open(`/app/${slug}/${tempName}`, '_blank')
    }
    // Clear the selection
    filterValues.value[field.fieldname] = ''
  }
}

async function handleLinkFocus(field: any) {
  const fieldname = field.fieldname
  if (!linkOptionsMap.value[fieldname] || linkOptionsMap.value[fieldname].length === 0) {
    await handleLinkSearch(field, '')
  }
}

function parseSelectOptions(options: any): Array<{ label: string; value: string }> {
  if (!options) return []
  if (typeof options === 'string') {
    return options.split('\n').filter((o: string) => o.trim()).map((o: string) => ({ label: t(o.trim()), value: o.trim() }))
  }
  if (Array.isArray(options)) {
    return options.map((o: any) => {
      if (typeof o === 'string') return { label: t(o), value: o }
      return { label: t(o.label || o.value), value: o.value || o.label }
    })
  }
  return []
}

async function loadLinkFieldDefaults(reportName: string) {
  const config = getRegisteredFilters(reportName)
  if (config?.resolveDefaults) {
    try {
      const defaults = await config.resolveDefaults()
      if (defaults && typeof defaults === 'object') {
        Object.keys(defaults).forEach(key => {
          const field = filterFields.value.find((f: any) => f.fieldname === key)
          if (field && !filterValues.value[key]) {
            filterValues.value[key] = defaults[key]
          }
        })
      }
    } catch {
      // ignore
    }
  }
}

function evalDependsOn(dependsOn: string | undefined): boolean {
  if (!dependsOn) return true
  try {
    const match = dependsOn.match(/^(\w+)\s*(==|!=)\s*['"]([^'"]+)['"]$/)
    if (match) {
      const [, field, op, value] = match
      const fieldValue = filterValues.value[field]
      if (op === '==') return fieldValue === value
      if (op === '!=') return fieldValue !== value
    }
    return true
  } catch {
    return true
  }
}

const visibleFilterFields = computed(() => {
  return filterFields.value.filter(field => evalDependsOn(field.depends_on))
})

const displayLabel = computed(() => {
  return props.label || props.chartName
})

const chartHeight = computed(() => {
  if (isHeatmap.value) return 180
  return props.height
})

const showFilterButton = computed(() => {
  return chartPg.value?.chart_type === 'Report' || chartPg.value?.chart_type === 'Custom'
})

const filterDialogTitle = computed(() => {
  const chartName = t(chartPg.value?.chart_name || props.chartName)
  return t('Set Filters for {0}', [chartName])
})

const currentTimespan = computed(() => {
  return selectedTimespan.value || props.chartSettings?.timespan || chartPg.value?.timespan || ''
})

const currentTimeInterval = computed(() => {
  return selectedTimeInterval.value || props.chartSettings?.time_interval || chartPg.value?.time_interval || ''
})

const currentHeatmapYear = computed(() => {
  return selectedHeatmapYear.value || props.chartSettings?.heatmap_year || chartPg.value?.heatmap_year || new Date().getFullYear()
})

const timeIntervalOptions = computed(() => {
  return ['Yearly', 'Quarterly', 'Monthly', 'Weekly', 'Daily'].map(v => ({
    label: t(v),
    key: v
  }))
})

const timespanOptions = computed(() => {
  return ['Last Year', 'Last Quarter', 'Last Month', 'Last Week', 'Select Date Range'].map(v => ({
    label: t(v),
    key: v
  }))
})

const heatmapYearOptions = computed(() => {
  const currentYear = new Date().getFullYear()
  const years: Array<{ label: string; key: string }> = []
  for (let y = currentYear; y >= currentYear - 5; y--) {
    years.push({ label: String(y), key: String(y) })
  }
  return years
})

const chartMenuOptions = computed(() => {
  const items: Array<{ label: string; key: string; icon: () => any }> = [
    { label: t('Refresh'), key: 'refresh', icon: () => h(Icon, { icon: 'tabler:refresh' }) },
    { label: t('Reset Chart'), key: 'reset', icon: () => h(Icon, { icon: 'tabler:arrow-back-up' }) }
  ]

  if (chartPg.value?.document_type) {
    items.push({
      label: `${chartPg.value.document_type} ${t('List')}`,
      key: 'list',
      icon: () => h(Icon, { icon: 'tabler:list' })
    })
  } else if (chartPg.value?.chart_type === 'Report' && chartPg.value?.report_name) {
    items.push({
      label: `${chartPg.value.report_name} ${t('Report')}`,
      key: 'report',
      icon: () => h(Icon, { icon: 'tabler:file-text' })
    })
  }

  return items
})

function handleTimeIntervalChange(key: string) {
  setTimeInterval(key)
}

function handleTimespanChange(key: string) {
  if (key === 'Select Date Range') {
    showDateRange.value = true
    selectedTimespan.value = key
  } else {
    showDateRange.value = false
    dateRange.value = null
    setTimespan(key)
  }
}

function handleHeatmapYearChange(key: string) {
  setHeatmapYear(key)
}

function handleDateRangeChange(val: [number, number] | null) {
  if (val && val.length === 2) {
    const fromDate = new Date(val[0]).toISOString().split('T')[0]
    const toDate = new Date(val[1]).toISOString().split('T')[0]
    setDateRange(fromDate, toDate)
  }
}

function getReportFilters(reportName: string): any[] {
  const config = getRegisteredFilters(reportName)
  if (config?.filters?.length) {
    return config.filters
  }
  return []
}

async function openFilterDialog() {
  if (!chartPg.value) return
  
  filterFields.value = []
  filterValues.value = {}
  linkOptionsMap.value = {}
  linkOptionsLoading.value = {}
  
  // Report type: get filters from report script
  if (chartPg.value.chart_type === 'Report' && chartPg.value.report_name) {
    try {
      let filters: any[] = getReportFilters(chartPg.value.report_name)
      
      if (!filters.length) {
        const result = await api.call('jingrow.desk.query_report.get_script', {
          report_name: chartPg.value.report_name
        })
        const rawFilters = result.message?.filters || result.filters
        if (Array.isArray(rawFilters) && rawFilters.length > 0) {
          filters = rawFilters
        }
      }
      
      filterFields.value = filters.filter((f: any) => f.fieldname)
      
      filterFields.value.forEach((f: any) => {
        let defaultValue = f.default
        
        if (defaultValue === undefined || defaultValue === null || defaultValue === '') {
          if (f.fieldtype === 'Check') {
            defaultValue = false
          } else if (f.fieldtype === 'Date') {
            defaultValue = null
          } else {
            defaultValue = ''
          }
        }
        if (f.fieldtype === 'Date' && defaultValue && typeof defaultValue === 'string') {
          defaultValue = new Date(defaultValue).getTime()
        }
        filterValues.value[f.fieldname] = defaultValue
      })
      
      await loadLinkFieldDefaults(chartPg.value.report_name)
      
      // Apply saved filters from currentFilters (loaded from Dashboard Settings)
      if (currentFilters.value && typeof currentFilters.value === 'object') {
        Object.keys(currentFilters.value).forEach((key) => {
          const field = filterFields.value.find((f: any) => f.fieldname === key)
          let value = currentFilters.value[key]
          if (value !== undefined && value !== null && value !== '') {
            if (field && field.fieldtype === 'Date' && typeof value === 'string') {
              value = new Date(value).getTime()
            }
            filterValues.value[key] = value
          }
        })
      }
    } catch (e) {
      console.error('[ChartFilter] Failed to load report filters:', e)
      filterFields.value = []
    }
  }
  
  // Custom type: get filters from chart source JS
  if (chartPg.value.chart_type === 'Custom' && chartPg.value.source) {
    try {
      // Get the chart source JS config
      const result = await api.call('jingrow.desk.pagetype.dashboard_chart_source.dashboard_chart_source.get_config', {
        name: chartPg.value.source
      })
      
      // Parse filters from JS code
      const jsCode = result.message || result
      const filters = parseFiltersFromJs(jsCode, chartPg.value.source)
      
      filterFields.value = filters
      
      // Set default values - try user defaults from boot first
      const userDefaults = (window as any).jingrow?.boot?.user?.defaults || {}
      
      filterFields.value.forEach((f: any) => {
        let defaultValue = f.default
        
        // If default is empty, try to get from user defaults
        if ((!defaultValue || defaultValue === '') && f.fieldname) {
          const userDefault = userDefaults[f.fieldname] || userDefaults[f.options]
          if (userDefault) {
            defaultValue = Array.isArray(userDefault) ? userDefault[0] : userDefault
          }
        }
        
        if (defaultValue === undefined || defaultValue === null || defaultValue === '') {
          defaultValue = f.fieldtype === 'Check' ? false : ''
        }
        if (f.fieldtype === 'Date' && defaultValue) {
          defaultValue = new Date(defaultValue).getTime()
        }
        filterValues.value[f.fieldname] = defaultValue
      })
      
      // Apply saved filters
      if (currentFilters.value && typeof currentFilters.value === 'object') {
        Object.keys(currentFilters.value).forEach((key) => {
          const field = filterFields.value.find((f: any) => f.fieldname === key)
          let value = currentFilters.value[key]
          if (value !== undefined && value !== null && value !== '') {
            if (field && field.fieldtype === 'Date' && typeof value === 'string') {
              value = new Date(value).getTime()
            }
            filterValues.value[key] = value
          }
        })
      }
    } catch (e) {
      console.error('[ChartFilter] Failed to load custom chart filters:', e)
      filterFields.value = []
    }
  }
  
  filterDialogVisible.value = true
}

function parseFiltersFromJs(jsCode: string, sourceName: string): any[] {
  // Parse filters array from JS code
  try {
    // Find filters: [ ... ] using bracket counting
    const filtersStartMatch = jsCode.match(/filters\s*:\s*\[/)
    if (!filtersStartMatch) {
      return []
    }
    
    const startIndex = filtersStartMatch.index! + filtersStartMatch[0].length
    let depth = 1
    let endIndex = startIndex
    
    // Count brackets to find the matching closing bracket
    while (endIndex < jsCode.length && depth > 0) {
      const char = jsCode[endIndex]
      if (char === '[') depth++
      else if (char === ']') depth--
      endIndex++
    }
    
    // Extract the content between [ and ]
    const filtersContent = jsCode.slice(startIndex, endIndex - 1)
    
    // Clean up JS-specific syntax for JSON parsing
    let jsonStr = '[' + filtersContent + ']'
    
    // Step 1: Replace function calls
    jsonStr = jsonStr
      .replace(/__\("([^"]+)"\)/g, '"$1"')
      .replace(/__\('([^']+)'\)/g, '"$1"')
      .replace(/jingrow\.defaults\.get_user_default\([^)]*\)/g, '""')
    
    // Step 2: Remove all whitespace around brackets and commas
    jsonStr = jsonStr
      .replace(/\s+/g, ' ')
      .replace(/\s*\{/g, '{')
      .replace(/\s*\}/g, '}')
      .replace(/\s*\[/g, '[')
      .replace(/\s*\]/g, ']')
      .replace(/\s*,\s*/g, ',')
      .replace(/\s*:\s*/g, ':')
    
    // Step 3: Quote unquoted keys (fieldname: -> "fieldname":)
    jsonStr = jsonStr.replace(/([{,]\s*)([a-zA-Z_][a-zA-Z0-9_]*)\s*:/g, '$1"$2":')
    
    // Step 4: Remove trailing commas (,} or ,])
    jsonStr = jsonStr.replace(/,}/g, '}').replace(/,]/g, ']')
    
    const parsed = JSON.parse(jsonStr)
    return parsed
  } catch (e) {
    console.error('[ChartFilter] Failed to parse filters from JS:', e)
    return []
  }
}

async function applyFilters() {
  const filters: Record<string, any> = {}
  filterFields.value.forEach((field: any) => {
    const value = filterValues.value[field.fieldname]
    if (value !== undefined && value !== null && value !== '') {
      if (field.fieldtype === 'Date' && typeof value === 'number') {
        filters[field.fieldname] = new Date(value).toISOString().split('T')[0]
      } else {
        filters[field.fieldname] = value
      }
    }
  })
  
  await setFilters(filters)
  filterDialogVisible.value = false
}

function handleMenuAction(key: string) {
  switch (key) {
    case 'refresh':
      refresh()
      break
    case 'reset':
      showDateRange.value = false
      dateRange.value = null
      reset()
      break
    case 'list':
      if (chartPg.value?.document_type) {
        const slug = pageTypeToSlug(chartPg.value.document_type)
        router.push(`/app/${slug}`)
      }
      break
    case 'report':
      if (chartPg.value?.report_name) {
        const slug = pageTypeToSlug(chartPg.value.report_name)
        router.push(`/app/query-report/${slug}`)
      }
      break
  }
}
</script>

<style scoped>
.workspace-chart-widget {
  width: 100%;
}

.widget-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: 8px;
  border-bottom: 1px solid #f1f5f9;
  min-height: 32px;
}

.widget-title-area {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}

.widget-title {
  font-size: 14px;
  font-weight: 600;
  color: #0f172a;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.widget-subtitle {
  font-size: 11px;
  color: #94a3b8;
}

.widget-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

.date-range-picker {
  padding: 8px 0 4px 0;
}

.widget-body {
  padding-top: 8px;
}

.chart-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 200px;
  color: #94a3b8;
  font-size: 13px;
}

.error-state {
  color: #ef4444;
}

.no-filters {
  padding: 20px;
  text-align: center;
  color: #94a3b8;
}

.filter-form {
  padding: 16px 0;
}

.filter-field {
  margin-bottom: 16px;
}

.filter-field:last-child {
  margin-bottom: 0;
}

.filter-label {
  display: block;
  margin-bottom: 8px;
  font-size: 13px;
  font-weight: 500;
  color: #374151;
}
</style>
