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

// Deterministic trade records (one row per trade). No randomness, but the
// distributions are intentionally uneven so the crossfilter is visible:
//   - quarters have different totals (the donut is 18 / 12 / 16 / 14, not 4x15)
//   - each quarter peaks on a different weekday
//   - outcome leans Gain early in the week and Loss later
// So clicking a quarter OR an outcome noticeably reshapes the day bar below.
function tradesData() {
  var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']

  // quarter -> trades per weekday (Mon..Fri); column totals differ per quarter.
  var plan = {
    Q1: [8, 4, 3, 2, 1], // early-week heavy
    Q2: [1, 2, 5, 3, 1], // mid-week heavy
    Q3: [2, 2, 3, 4, 5], // late-week heavy
    Q4: [3, 6, 2, 2, 1], // Tuesday spike
  }
  // Share of Gains by weekday index (Mon..Fri): high early, low late.
  var gainByDay = [0.85, 0.7, 0.5, 0.3, 0.15]

  var out = []
  Object.keys(plan).forEach(function (q) {
    plan[q].forEach(function (count, di) {
      var gains = Math.round(count * gainByDay[di])
      for (var k = 0; k < count; k++) {
        out.push({
          q: q,
          day: days[di],
          gl: k < gains ? 'Gain' : 'Loss',
        })
      }
    })
  })
  return out
}

// Register the shared record set BEFORE the charts are constructed, so each
// chart's initial paint is already the aggregation (no empty flash).
ApexCharts.crossfilter({ id: 'trades', records: tradesData() })

// Readout formatter, shared by the vanilla-js, React and Vue builds.
function fmtFilters(state) {
  var keys = Object.keys(state.filters)
  if (!keys.length) return 'No filter (all ' + state.total + ' trades)'
  var parts = keys.map(function (id) {
    return id + ': ' + state.filters[id].join(', ')
  })
  return (
    parts.join('  |  ') +
    '   ->   ' +
    state.filteredCount +
    ' / ' +
    state.total +
    ' trades'
  )
}

const ApexChart = () => {
  const [state, setState] = React.useState({
    series: [],
    options: {
      chart: {
        id: 'byQuarter',
        type: 'donut',
        height: 300,
        fontFamily: 'Helvetica, Arial, sans-serif',
        animations: { speed: 500 },
        link: {
          id: 'trades',
          dimension: function (r) {
            return r.q
          },
          reduce: 'count',
          dimOpacity: 0.18,
        },
      },
      title: { text: 'By quarter', align: 'left' },
      legend: { position: 'bottom' },
      plotOptions: { pie: { expandOnClick: false } },
      dataLabels: {
        enabled: true,
        formatter: function (val, opts) {
          return opts.w.config.series[opts.seriesIndex]
        },
        style: { colors: ['#334155'], fontWeight: 600 },
        dropShadow: { enabled: false },
      },
      colors: ['#2563EB', '#38bdf8', '#4ade80', '#fbbf24'],
      stroke: { width: 2, colors: ['#fff'] },
    },
    series1: [],
    options1: {
      chart: {
        id: 'byOutcome',
        type: 'donut',
        height: 300,
        fontFamily: 'Helvetica, Arial, sans-serif',
        animations: { speed: 500 },
        link: {
          id: 'trades',
          dimension: function (r) {
            return r.gl
          },
          reduce: 'count',
          order: 'asc', // Gain before Loss, so the colors below map semantically
          dimOpacity: 0.18,
        },
      },
      title: { text: 'By outcome', align: 'left' },
      legend: { position: 'bottom' },
      plotOptions: { pie: { expandOnClick: false } },
      dataLabels: {
        enabled: true,
        formatter: function (val, opts) {
          return opts.w.config.series[opts.seriesIndex]
        },
        style: { colors: ['#334155'], fontWeight: 600 },
        dropShadow: { enabled: false },
      },
      colors: ['#4ade80', '#f87171'],
      stroke: { width: 2, colors: ['#fff'] },
    },
    series2: [],
    options2: {
      chart: {
        id: 'byDay',
        type: 'bar',
        height: 280,
        fontFamily: 'Helvetica, Arial, sans-serif',
        animations: { speed: 500 },
        link: {
          id: 'trades',
          dimension: function (r) {
            return r.day
          },
          reduce: 'count',
          seriesName: 'Trades',
          // `order` also takes a comparator: keep the weekdays in calendar order
          // instead of the order they first appear in the records.
          order: function (a, b) {
            var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
            return days.indexOf(a) - days.indexOf(b)
          },
          dimOpacity: 0.18,
        },
      },
      title: { text: 'By day of week (click a bar too)', align: 'left' },
      plotOptions: {
        bar: { columnWidth: '55%', borderRadius: 3, distributed: true },
      },
      legend: { show: false },
      dataLabels: { enabled: false },
      colors: ['#2563EB', '#38bdf8', '#4ade80', '#fbbf24', '#f472b6'],
    },
  })

  React.useEffect(() => {
    // The react-apexcharts wrapper owns the render, and the crossfilter engine is
    // registered by the shared head script (ApexCharts.crossfilter). Poll until the
    // engine exists, then wire the readout + Reset button the same way the vanilla
    // build does. (fmtFilters lives in the shared head script.)
    const timer = window.setInterval(() => {
      const cf = ApexCharts.getCrossfilter('trades')
      if (!cf) return
      window.clearInterval(timer)

      const readout = document.getElementById('cf-readout')
      cf.on('change', (state) => {
        readout.textContent = fmtFilters(state)
      })

      document
        .getElementById('cf-reset')
        .addEventListener('click', () => cf.reset())
    }, 50)

    return () => window.clearInterval(timer)
  }, [])

  return (
    <div>
      <div className="cf-wrap">
        <div className="cf-bar">
          <button id="cf-reset">Reset filters</button>
          <span className="readout" id="cf-readout">
            Click a slice or bar to filter every chart
          </span>
        </div>

        <div className="cf-grid">
          <div className="cf-card">
            <div id="chart">
              <ReactApexChart
                options={state.options}
                series={state.series}
                type="donut"
                height={300}
              />
            </div>
          </div>
          <div className="cf-card">
            <div id="chart2">
              <ReactApexChart
                options={state.options1}
                series={state.series1}
                type="donut"
                height={300}
              />
            </div>
          </div>
          <div className="cf-card full">
            <div id="chart3">
              <ReactApexChart
                options={state.options2}
                series={state.series2}
                type="bar"
                height={280}
              />
            </div>
          </div>
        </div>

        <div className="cf-note">
          All three charts declare a <code>chart.link.dimension</code> over one
          record set registered with{' '}
          <code>ApexCharts.crossfilter(&#123; id, records &#125;)</code>.
          Clicking a slice or bar toggles that bucket: the clicked chart dims
          its other buckets, and every other chart{' '}
          <b>re-aggregates over the filtered trades</b> and animates to its new
          values. Selections combine (a chart never filters itself), so you
          always see what is still available.
          <b>Reset filters</b> clears everything. Needs the <code>link</code>{' '}
          feature.
        </div>
      </div>
    </div>
  )
}

export default ApexChart
Crossfilter (Categorical) - React Interactivity | ApexCharts.js | ApexCharts.js