import { ref, computed, onMounted } from 'vue'
import { api } from '@/shared/api/common'
import { getReportFilters as getRegisteredFilters } from '@/shared/extensions'
import { t } from '@/shared/i18n'

export interface ChartDataset {
  name: string
  values: number[]
}

export interface ChartData {
  labels: string[]
  datasets: ChartDataset[]
  dataPoints?: Record<number, number>
}

export interface DashboardChartPg {
  name: string
  chart_name: string
  chart_type: string
  type: string
  document_type: string
  timeseries: number | boolean
  time_interval: string
  timespan: string
  filters_json: string
  color: string
  y_axis: Array<{ y_field: string; color: string }>
  number_of_groups: number
  custom_options: string
  value_based_on: string
  currency: string
  source: string
  report_name: string
  use_report_chart: number | boolean
  heatmap_year: number
  x_field: string
  last_synced_on: string
  parent_document_type: string
}

export interface ChartSettings {
  timespan?: string
  time_interval?: string
  from_date?: string | null
  to_date?: string | null
  heatmap_year?: number | string
  filters?: any
}

interface UseChartDataOptions {
  chartName: string
  chartSettings?: ChartSettings
}

export function useChartData(options: UseChartDataOptions) {
  const chartPg = ref<DashboardChartPg | null>(null)
  const chartData = ref<ChartData | null>(null)
  const loading = ref(true)
  const error = ref<string | null>(null)
  const settings = ref<ChartSettings>(options.chartSettings || {})

  const selectedTimespan = ref<string>('')
  const selectedTimeInterval = ref<string>('')
  const selectedHeatmapYear = ref<string | number>('')
  const selectedFromDate = ref<string | null>(null)
  const selectedToDate = ref<string | null>(null)
  const filters = ref<any>(null)

  const hasData = computed(() => {
    if (!chartData.value) return false
    if (chartData.value.dataPoints && Object.keys(chartData.value.dataPoints).length > 0) return true
    if (chartData.value.labels && chartData.value.labels.length > 0) return true
    return false
  })

  const isTimeseries = computed(() => {
    return chartPg.value?.timeseries ? true : false
  })

  const isHeatmap = computed(() => {
    return chartPg.value?.type === 'Heatmap'
  })

  const chartColors = computed(() => {
    if (!chartPg.value) return []
    const colors: string[] = []
    if (chartPg.value.y_axis?.length) {
      chartPg.value.y_axis.forEach((field) => {
        if (field.color) colors.push(field.color)
      })
    } else if (['Line', 'Bar'].includes(chartPg.value.type)) {
      if (chartPg.value.color) colors.push(chartPg.value.color)
    }
    return colors
  })

  function getApiMethod(): string {
    if (!chartPg.value) return ''
    if (chartPg.value.chart_type === 'Report') {
      return 'jingrow.desk.query_report.run'
    }
    // Custom type uses method from chart source JS
    if (chartPg.value.chart_type === 'Custom' && chartPg.value.source) {
      // Return the method from chart source (will be set by loadChartSourceMethod)
      return customChartMethod.value
    }
    return 'jingrow.desk.pagetype.dashboard_chart.dashboard_chart.get'
  }

  const customChartMethod = ref('')

  async function loadChartSourceMethod(): Promise<boolean> {
    if (!chartPg.value?.source || chartPg.value.chart_type !== 'Custom') return false
    
    try {
      const result = await api.call('jingrow.desk.pagetype.dashboard_chart_source.dashboard_chart_source.get_config', {
        name: chartPg.value.source
      })
      
      const jsCode = result.message || result
      // Parse method from JS: method: "jerp.stock.dashboard_chart_source..."
      const methodMatch = jsCode.match(/method\s*:\s*["']([^"']+)["']/)
      if (methodMatch) {
        customChartMethod.value = methodMatch[1]
        return true
      }
    } catch (e) {
      console.error('[Chart] Failed to load chart source method:', e)
    }
    return false
  }

  async function loadChartConfig(): Promise<boolean> {
    try {
      const result = await api.call('jingrow.client.get_list', {
        pagetype: 'Dashboard Chart',
        filters: { name: options.chartName },
        fields: ['*'],
        limit_page_length: 1
      })
      const records = result.message || result
      if (Array.isArray(records) && records.length > 0) {
        chartPg.value = records[0] as DashboardChartPg

        if (chartPg.value.y_axis && typeof chartPg.value.y_axis === 'string') {
          try {
            chartPg.value.y_axis = JSON.parse(chartPg.value.y_axis as any)
          } catch {
            chartPg.value.y_axis = []
          }
        }
        if (!Array.isArray(chartPg.value.y_axis)) {
          chartPg.value.y_axis = []
        }

        // Load user's saved chart config from Dashboard Settings
        try {
          const settingsResult = await api.call('jingrow.client.get_value', {
            pagetype: 'Dashboard Settings',
            filters: { name: (window as any).jingrow?.boot?.user?.name || 'Guest' },
            fieldname: 'chart_config'
          })
          const chartConfigStr = settingsResult.message?.chart_config || settingsResult.chart_config
          if (chartConfigStr) {
            const chartConfig = typeof chartConfigStr === 'string' ? JSON.parse(chartConfigStr) : chartConfigStr
            if (chartConfig && chartConfig[options.chartName]) {
              const savedConfig = chartConfig[options.chartName]
              // Merge saved config into chartPg
              Object.assign(settings.value, savedConfig)
              if (savedConfig.filters) {
                filters.value = savedConfig.filters
              }
            }
          }
        } catch {
          // ignore - Dashboard Settings may not exist
        }

        // For Custom chart type, load the method from chart source
        if (chartPg.value.chart_type === 'Custom' && chartPg.value.source) {
          await loadChartSourceMethod()
        }

        return true
      }
      return false
    } catch {
      return false
    }
  }

  async function fetchData(refresh = true) {
    if (!chartPg.value) return

    const method = getApiMethod()
    if (!method) return

    loading.value = true
    error.value = null

    try {
      const args: Record<string, any> = {}

      if (chartPg.value.chart_type === 'Report') {
        args.report_name = chartPg.value.report_name
        args.filters = filters.value
        args.ignore_prepared_report = 1
      } else if (chartPg.value.chart_type === 'Custom') {
        // Custom chart: use method from chart source, filters as object
        args.chart_name = chartPg.value.name
        // filters should be an object for custom charts
        if (filters.value && typeof filters.value === 'string') {
          try {
            args.filters = JSON.parse(filters.value)
          } catch {
            args.filters = {}
          }
        } else {
          args.filters = filters.value || {}
        }
      } else {
        // Document type charts (Count/Sum/Average/Group By)
        args.chart_name = chartPg.value.name
        args.filters = filters.value
        args.refresh = refresh ? 1 : 0
        args.time_interval = selectedTimeInterval.value || settings.value.time_interval || null
        args.timespan = selectedTimespan.value || settings.value.timespan || null
        args.from_date = selectedFromDate.value || settings.value.from_date || null
        args.to_date = selectedToDate.value || settings.value.to_date || null
        args.heatmap_year = selectedHeatmapYear.value || settings.value.heatmap_year || null
      }

      const result = await api.call(method, args)
      let data = result.message || result

      if (chartPg.value.chart_type === 'Report') {
        data = extractReportChartData(data)
      }

      chartData.value = data as ChartData
    } catch (e: any) {
      error.value = e.message || 'Failed to fetch chart data'
      chartData.value = null
    } finally {
      loading.value = false
    }
  }

  function extractReportChartData(reportResult: any): ChartData {
    // Check if report has chart data
    if (reportResult?.chart?.data && chartPg.value?.use_report_chart) {
      return reportResult.chart.data
    }

    const yFields: string[] = []
    if (chartPg.value?.y_axis?.length) {
      chartPg.value.y_axis.forEach((field) => {
        yFields.push(field.y_field)
      })
    }

    if (!yFields.length || !chartPg.value?.x_field) {
      if (reportResult?.chart?.data) {
        return reportResult.chart.data
      }
      return { labels: [], datasets: [] }
    }

    const rows = (reportResult.result || []).filter((r: any) => r && Object.keys(r).length)
    const labels = rows.map((r: any) => r[chartPg.value!.x_field] ?? '')
    const datasets = yFields.map((yField) => ({
      name: yField,
      values: rows.map((r: any) => Number(r[yField]) || 0)
    }))

    return { labels, datasets }
  }

  async function refresh() {
    await fetchData(true)
  }

  async function reset() {
    selectedTimespan.value = ''
    selectedTimeInterval.value = ''
    selectedHeatmapYear.value = ''
    selectedFromDate.value = null
    selectedToDate.value = null
    filters.value = null
    settings.value = {}

    try {
      await api.call(
        'jingrow.desk.pagetype.dashboard_settings.dashboard_settings.save_chart_config',
        { reset: 1, config: {}, chart_name: options.chartName }
      )
    } catch {
      // ignore
    }

    await fetchData(true)
  }

  function setTimespan(val: string) {
    selectedTimespan.value = val
    if (val !== 'Select Date Range') {
      selectedFromDate.value = null
      selectedToDate.value = null
    }
    saveChartConfig({ timespan: val, from_date: null, to_date: null })
    fetchData(true)
  }

  function setTimeInterval(val: string) {
    selectedTimeInterval.value = val
    saveChartConfig({ time_interval: val })
    fetchData(true)
  }

  function setHeatmapYear(val: string | number) {
    selectedHeatmapYear.value = val
    saveChartConfig({ heatmap_year: val })
    fetchData(true)
  }

  function setDateRange(from: string, to: string) {
    selectedFromDate.value = from
    selectedToDate.value = to
    saveChartConfig({ timespan: 'Select Date Range', from_date: from, to_date: to })
    fetchData(true)
  }

  async function saveChartConfig(config: Record<string, any>) {
    Object.assign(settings.value, config)
    try {
      await api.call(
        'jingrow.desk.pagetype.dashboard_settings.dashboard_settings.save_chart_config',
        { reset: 0, config: settings.value, chart_name: options.chartName }
      )
    } catch {
      // ignore
    }
  }

  async function loadReportFilters(): Promise<Record<string, any> | null> {
    if (!chartPg.value) return null
    
    if (chartPg.value.chart_type === 'Report' && chartPg.value.report_name) {
      const reportName = chartPg.value.report_name
      const config = getRegisteredFilters(reportName)
      
      try {
        const defaultFilters: Record<string, any> = {}
        
        // Get filter definitions from registry or fallback to get_script API
        let filterFields: any[] = []
        if (config?.filters?.length) {
          filterFields = config.filters
        } else {
          const result = await api.call('jingrow.desk.query_report.get_script', {
            report_name: reportName
          })
          filterFields = result.message?.filters || result.filters || []
        }
        
        // Collect static defaults from filter definitions
        filterFields.forEach((f: any) => {
          if (f.fieldname && f.default !== undefined && f.default !== null && f.default !== '') {
            defaultFilters[f.fieldname] = f.default
          }
        })
        
        // Resolve dynamic defaults from extension
        if (config?.resolveDefaults) {
          try {
            const resolved = await config.resolveDefaults()
            if (resolved && typeof resolved === 'object') {
              Object.keys(resolved).forEach(key => {
                if (!defaultFilters[key] && resolved[key]) {
                  defaultFilters[key] = resolved[key]
                }
              })
            }
          } catch {
            // ignore
          }
        }
        
        return Object.keys(defaultFilters).length > 0 ? defaultFilters : null
      } catch {
        return null
      }
    }
    return null
  }

  async function init() {
    loading.value = true
    const loaded = await loadChartConfig()
    if (!loaded) {
      loading.value = false
      error.value = t('Chart not found')
      return
    }

    if (chartPg.value?.type === 'Heatmap' && !chartPg.value.heatmap_year) {
      chartPg.value.heatmap_year = new Date().getFullYear()
    }

    // Load report filters defaults (fiscal_year, company, etc.) if not already set
    if (!filters.value || Object.keys(filters.value).length === 0) {
      const reportDefaultFilters = await loadReportFilters()
      if (reportDefaultFilters && Object.keys(reportDefaultFilters).length > 0) {
        filters.value = reportDefaultFilters
      }
    }

    await fetchData(true)
  }

  onMounted(init)

  async function setFilters(newFilters: Record<string, any>) {
    filters.value = newFilters
    await saveChartConfig({ filters: newFilters })
    // Refresh chart data with new filters
    if (chartPg.value) {
      await fetchData(true)
    }
  }

  return {
    chartPg,
    chartData,
    loading,
    error,
    hasData,
    isTimeseries,
    isHeatmap,
    chartColors,
    settings,
    selectedTimespan,
    selectedTimeInterval,
    selectedHeatmapYear,
    selectedFromDate,
    selectedToDate,
    filters,
    refresh,
    reset,
    setTimespan,
    setTimeInterval,
    setHeatmapYear,
    setDateRange,
    setFilters,
    init
  }
}
