import React from 'react'
import ReactApexChart from 'react-apexcharts'
import ApexCharts from 'apexcharts'
import './styles.css'

// Deterministic trade records. outcome is derived from the fluctuation sign,
// so brushing the fluctuation histogram visibly reshapes the outcome donut.
function tradesData() {
  var quarters = ['Q1', 'Q2', 'Q3', 'Q4']
  var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
  var out = []
  for (var i = 0; i < 120; i++) {
    var pct =
      3.0 * Math.sin(i / 5.0) +
      1.3 * Math.sin(i / 1.7) +
      0.4 * Math.cos(i / 3.1)
    var vol = Math.round(120 + 70 * Math.sin(i / 6.0) + 40 * Math.cos(i / 2.3))
    out.push({
      id: i + 1,
      quarter: quarters[Math.floor(i / 30) % 4],
      day: days[(i * 3) % 5],
      outcome: pct >= 0 ? 'Gain' : 'Loss',
      pct: Math.round(pct * 100) / 100,
      volume: Math.max(20, vol),
    })
  }
  return out
}

// Register the shared record set BEFORE the charts are constructed.
ApexCharts.crossfilter({ id: 'trades', records: tradesData() })

const ApexChart = () => {
  const [state, setState] = React.useState({
    series: [],
    options: {
      chart: {
        id: 'byQuarter',
        type: 'donut',
        height: 260,
        fontFamily: 'Helvetica, Arial, sans-serif',
        animations: { speed: 450 },
        link: {
          id: 'trades',
          dimension: function (r) {
            return r.quarter
          },
          reduce: 'count',
          dimOpacity: 0.18,
        },
      },
      title: { text: 'Quarter', align: 'left' },
      legend: { position: 'bottom', fontSize: '12px' },
      plotOptions: { pie: { expandOnClick: false } },
      dataLabels: {
        enabled: true,
        formatter: function (v, o) {
          return o.w.config.series[o.seriesIndex]
        },
      },
      colors: ['#2563EB', '#0ea5e9', '#22c55e', '#f59e0b'],
    },
    series1: [],
    options1: {
      chart: {
        id: 'byOutcome',
        type: 'donut',
        height: 260,
        fontFamily: 'Helvetica, Arial, sans-serif',
        animations: { speed: 450 },
        link: {
          id: 'trades',
          dimension: function (r) {
            return r.outcome
          },
          reduce: 'count',
          dimOpacity: 0.18,
        },
      },
      title: { text: 'Outcome', align: 'left' },
      legend: { position: 'bottom', fontSize: '12px' },
      plotOptions: { pie: { expandOnClick: false } },
      dataLabels: {
        enabled: true,
        formatter: function (v, o) {
          return o.w.config.series[o.seriesIndex]
        },
      },
      colors: ['#22c55e', '#ef4444'],
    },
    series2: [],
    options2: {
      chart: {
        id: 'byDay',
        type: 'bar',
        height: 260,
        fontFamily: 'Helvetica, Arial, sans-serif',
        animations: { speed: 450 },
        link: {
          id: 'trades',
          dimension: function (r) {
            return r.day
          },
          reduce: 'count',
          seriesName: 'Trades',
          dimOpacity: 0.18,
        },
      },
      title: { text: 'Day of week', align: 'left' },
      plotOptions: {
        bar: { columnWidth: '55%', borderRadius: 3, distributed: true },
      },
      legend: { show: false },
      dataLabels: { enabled: false },
      colors: ['#2563EB', '#0ea5e9', '#22c55e', '#f59e0b', '#F43F5E'],
    },
    series3: [],
    options3: {
      chart: {
        id: 'byFluctuation',
        type: 'bar',
        height: 240,
        fontFamily: 'Helvetica, Arial, sans-serif',
        animations: { speed: 400 },
        zoom: { enabled: false },
        selection: { enabled: true },
        toolbar: {
          autoSelected: 'selection',
          tools: {
            selection: true,
            zoom: false,
            pan: false,
            reset: false,
            download: false,
          },
        },
        link: {
          id: 'trades',
          dimension: function (r) {
            return r.pct
          },
          bins: { count: 26 },
          dimOpacity: 0.16,
        },
      },
      title: { text: 'Fluctuation % (brush a range)', align: 'left' },
      plotOptions: { bar: { columnWidth: '92%' } },
      dataLabels: { enabled: false },
      xaxis: {
        type: 'numeric',
        tickAmount: 8,
        labels: {
          formatter: function (v) {
            return Number(v).toFixed(1)
          },
        },
      },
      colors: ['#2563EB'],
    },
    series4: [],
    options4: {
      chart: {
        id: 'byVolume',
        type: 'bar',
        height: 240,
        fontFamily: 'Helvetica, Arial, sans-serif',
        animations: { speed: 400 },
        zoom: { enabled: false },
        selection: { enabled: true },
        toolbar: {
          autoSelected: 'selection',
          tools: {
            selection: true,
            zoom: false,
            pan: false,
            reset: false,
            download: false,
          },
        },
        link: {
          id: 'trades',
          dimension: function (r) {
            return r.volume
          },
          bins: { count: 22 },
          dimOpacity: 0.16,
        },
      },
      title: { text: 'Volume (brush a range)', align: 'left' },
      plotOptions: { bar: { columnWidth: '92%' } },
      dataLabels: { enabled: false },
      xaxis: {
        type: 'numeric',
        tickAmount: 8,
        labels: {
          formatter: function (v) {
            return Math.round(v)
          },
        },
      },
      colors: ['#0ea5e9'],
    },
  })

  React.useEffect(() => {
    // Five linked views (byQuarter..byVolume) + a live data table, all over the
    // shared 'trades' record set (registered in the head script). The wrapper owns
    // the render, so poll until every chart instance and the crossfilter exist,
    // then wire the table + readout + reset exactly as the vanilla build does.
    let cf
    let table
    let offChange
    const onReset = () => {
      if (cf) cf.reset()
    }

    const timer = window.setInterval(() => {
      cf = ApexCharts.getCrossfilter('trades')
      const ready =
        cf &&
        ApexCharts.getChartByID('byQuarter') &&
        ApexCharts.getChartByID('byOutcome') &&
        ApexCharts.getChartByID('byDay') &&
        ApexCharts.getChartByID('byFluctuation') &&
        ApexCharts.getChartByID('byVolume')
      if (!ready) return
      window.clearInterval(timer)

      const readout = document.getElementById('cfd-readout')

      // Bind the data table to the filtered rows; it re-renders on every change.
      table = cf.dataTable(document.getElementById('cfd-table'), {
        columns: [
          { field: 'id', label: '#' },
          { field: 'quarter', label: 'Quarter' },
          { field: 'day', label: 'Day' },
          { field: 'outcome', label: 'Outcome' },
          {
            field: 'pct',
            label: 'Fluctuation %',
            format: (v) => v.toFixed(2) + '%',
          },
          { field: 'volume', label: 'Volume' },
        ],
      })

      offChange = cf.on('change', (state) => {
        const ids = Object.keys(state.filters)
        const label = ids.length ? ids.join(', ') : 'none'
        readout.textContent =
          state.filteredCount +
          ' / ' +
          state.total +
          ' trades   (active: ' +
          label +
          ')'
      })

      // Seed the readout with the unfiltered totals.
      const s = cf.state()
      readout.textContent =
        s.filteredCount + ' / ' + s.total + ' trades   (active: none)'

      document.getElementById('cfd-reset').addEventListener('click', onReset)
    }, 50)

    return () => {
      window.clearInterval(timer)
      if (offChange) offChange()
      if (table) table.destroy()
      const btn = document.getElementById('cfd-reset')
      if (btn) btn.removeEventListener('click', onReset)
    }
  }, [])

  return (
    <div>
      <div className="cfd">
        <div className="cfd-bar">
          <button id="cfd-reset">Reset all filters</button>
          <span className="readout" id="cfd-readout"></span>
        </div>

        <div className="cfd-grid">
          <div className="cfd-card">
            <div id="chart">
              <ReactApexChart
                options={state.options}
                series={state.series}
                type="donut"
                height={260}
              />
            </div>
          </div>
          <div className="cfd-card">
            <div id="chart2">
              <ReactApexChart
                options={state.options1}
                series={state.series1}
                type="donut"
                height={260}
              />
            </div>
          </div>
          <div className="cfd-card">
            <div id="chart3">
              <ReactApexChart
                options={state.options2}
                series={state.series2}
                type="bar"
                height={260}
              />
            </div>
          </div>
          <div className="cfd-card brush">
            <div id="chart4">
              <ReactApexChart
                options={state.options3}
                series={state.series3}
                type="bar"
                height={240}
              />
            </div>
          </div>
          <div className="cfd-card brush">
            <div id="chart5">
              <ReactApexChart
                options={state.options4}
                series={state.series4}
                type="bar"
                height={240}
              />
            </div>
          </div>
        </div>

        <div className="cfd-tablewrap" id="cfd-table"></div>

        <div className="cfd-note">
          Five views over one record set registered with
          <code>ApexCharts.crossfilter(&#123; id, records &#125;)</code>. The
          donuts and the day bar filter by <b>clicking a bucket</b>; the two
          histograms filter by
          <b>brushing a range</b> (they set <code>chart.selection.enabled</code>
          ). Every selection combines: each chart re-aggregates over the rows
          passing all the <i>other</i> charts' filters (never its own), and the
          table below lists exactly those rows. Try brushing the fluctuation
          histogram and watch the outcome donut reshape.{' '}
          <b>Reset all filters</b> clears everything.
        </div>
      </div>
    </div>
  )
}

export default ApexChart
Crossfilter Dashboard - React Interactivity | ApexCharts.js | ApexCharts.js