import VerticalSlider from '../Slider/VerticalSlider';
import { invoke } from '@tauri-apps/api/core';
import { InputChannel, Channel, OutputChannel } from '../../../types/dsp408State';
import { DSP } from '../../../types/types';
import { NotificationType } from '../../../types/types';
import Button from '../../Button/Button';
import ChannelGroup from '../ChannelGroup/ChannelGroup';
import { LinearGraph } from '../LinearGraph/LinearGraph';
import { useEffect, useRef, useState } from 'react';
import './GainSection.css';

const PANEL_DESIGN_WIDTH = 90;
const PANEL_DESIGN_HEIGHT = 400;

function ChannelGroupScaled({ title, children }: { title: string; children: React.ReactNode }) {
  const wrapperRef = useRef<HTMLDivElement>(null);
  const [scale, setScale] = useState(1);

  useEffect(() => {
    const el = wrapperRef.current;
    if (!el) return;
    const observer = new ResizeObserver((entries) => {
      const { height } = entries[0].contentRect;
      if (height === 0) return;
      setScale(height / PANEL_DESIGN_HEIGHT); // height is the only free axis here
    });
    observer.observe(el);
    return () => observer.disconnect();
  }, []);

  return (
    <div ref={wrapperRef} style={{ height: '100%', '--panel-scale': scale } as React.CSSProperties}>
      <ChannelGroup title={title}>{children}</ChannelGroup>
    </div>
  );
}

function GainPanel({
  dsp,
  notify,
  channel,
  title,
}: {
  dsp: DSP;
  notify: (message: string, type?: NotificationType, dspName?: string) => void;
  channel: Channel;
  title: string;
}) {
  const setGain = async (value: number) => {
    try {
      const success = await invoke<boolean>('set_channel_gain', {
        id: dsp.id,
        channel,
        db: value,
      });

      if (success) {
        notify(`Input A gain set to ${value} dB`, 'success', dsp.name);
      } else {
        notify('Failed to set input gain', 'error', dsp.name);
      }
    } catch (err) {
      notify(`Gain update failed: ${String(err)}`, 'error', dsp.name);
    }
  };

  const setMute = async (muted: boolean) => {
    try {
      const success = await invoke<boolean>('set_mute', {
        id: dsp.id,
        channel,
        muted,
      });

      if (success) {
        notify(`Input A ${muted ? 'muted' : 'unmuted'}`, 'success', dsp.name);
      } else {
        notify('Failed to change mute state', 'error', dsp.name);
      }
    } catch (err) {
      notify(`Mute update failed: ${String(err)}`, 'error', dsp.name);
    }
  };

  const setInverse = async (inverted: boolean) => {
    try {
      const success = await invoke<boolean>('set_channel_inverse_gain', {
        id: dsp.id,
        channel,
        inverted,
      });

      if (success) {
        notify(`Input A phase ${inverted ? 'inverted' : 'normal'}`, 'success', dsp.name);
      } else {
        notify('Failed to change phase', 'error', dsp.name);
      }
    } catch (err) {
      notify(`Phase update failed: ${String(err)}`, 'error', dsp.name);
    }
  };

  const [muted, setMuted] = useState(false);
  const [inverted, setInverted] = useState(false);
  const viewportRef = useRef<HTMLDivElement>(null);
  const [scale, setScale] = useState(1);

  const toggleMute = async () => {
    const next = !muted;
    setMuted(next);
    await setMute(next);
  };

  const toggleInverse = async () => {
    const next = !inverted;
    setInverted(next);
    await setInverse(next);
  };

  useEffect(() => {
    const el = viewportRef.current;
    if (!el) return;
    const observer = new ResizeObserver((entries) => {
      const { width, height } = entries[0].contentRect;
      if (width === 0 || height === 0) return;
      setScale(Math.min(width / PANEL_DESIGN_WIDTH, height / PANEL_DESIGN_HEIGHT));
    });
    observer.observe(el);
    return () => observer.disconnect();
  }, []);

  return (
    <div className="gain-panel-viewport" ref={viewportRef}>
      <div
        className="gain-panel"
        style={{
          width: PANEL_DESIGN_WIDTH,
          height: PANEL_DESIGN_HEIGHT,
          transform: `translate(-50%, -50%) scale(${scale})`,
        }}
      >
        <div className="gain-panel__title">{title}</div>

        <VerticalSlider
          min={-60}
          max={12}
          step={0.5}
          switchStepValue={-10}
          switchStep={0.1}
          unit=" dB"
          onChange={setGain}
          style={{ width: 70, height: 260, flex: '0 0 auto' }} // pin to design size, disable its own auto-shrink
        />

        <Button variant="toggle" className={muted ? 'active-danger' : ''} onClick={toggleMute}>
          {muted ? 'Muted' : 'Mute'}
        </Button>
        <Button
          variant="toggle"
          className={inverted ? 'active-warning' : ''}
          onClick={toggleInverse}
        >
          {inverted ? 'Inverted' : 'Invert'}
        </Button>
      </div>
    </div>
  );
}

function GainSection({
  dsp,
  notify,
}: {
  dsp: DSP;
  notify: (message: string, type?: NotificationType, dspName?: string) => void;
}) {
  const inputs: { channel: Channel; title: string }[] = [
    { channel: { Input: InputChannel.InA }, title: 'In A' },
    { channel: { Input: InputChannel.InB }, title: 'In B' },
    { channel: { Input: InputChannel.InC }, title: 'In C' },
    { channel: { Input: InputChannel.InD }, title: 'In D' },
  ];

  const outputs: { channel: Channel; title: string }[] = [
    { channel: { Output: OutputChannel.Out1 }, title: 'Out 1' },
    { channel: { Output: OutputChannel.Out2 }, title: 'Out 2' },
    { channel: { Output: OutputChannel.Out3 }, title: 'Out 3' },
    { channel: { Output: OutputChannel.Out4 }, title: 'Out 4' },
    { channel: { Output: OutputChannel.Out5 }, title: 'Out 5' },
    { channel: { Output: OutputChannel.Out6 }, title: 'Out 6' },
    { channel: { Output: OutputChannel.Out7 }, title: 'Out 7' },
    { channel: { Output: OutputChannel.Out8 }, title: 'Out 8' },
  ];

  return (
    <>
      <div className="linear-graph-section">
        <LinearGraph
          xMin={20}
          xMax={20000}
          yMin={-24}
          yMax={24}
          yStep={6}
          xLabel="Hz"
          yLabel="dB"
          draw={({ ctx, toPx }) => {
            // placeholder flat 0 dB response — swap for real EQ/filter data.
            // NOTE: this maps x linearly; for a real frequency response you'll
            // likely want a log-frequency x-axis instead (see chat notes).
            ctx.strokeStyle = '#4ade80';
            ctx.lineWidth = 2;
            const [x0, y0] = toPx(20, 0);
            const [x1, y1] = toPx(20000, 0);
            ctx.beginPath();
            ctx.moveTo(x0, y0);
            ctx.lineTo(x1, y1);
            ctx.stroke();
          }}
        />
      </div>

      <div className="gain-section">
        <div className="gain-panels">
          <ChannelGroup title="Input">
            {inputs.map(({ channel, title }) => (
              <GainPanel key={title} dsp={dsp} notify={notify} channel={channel} title={title} />
            ))}
          </ChannelGroup>

          <ChannelGroup title="Output">
            {outputs.map(({ channel, title }) => (
              <GainPanel key={title} dsp={dsp} notify={notify} channel={channel} title={title} />
            ))}
          </ChannelGroup>
        </div>
      </div>
    </>
  );
}

export default GainSection;
