import React, { useCallback, useEffect, useRef, useState } from 'react'
import { createRoot } from 'react-dom/client'
import './styles.css'

const tracks = [
  { no: '01', name: 'Streetlights', mood: 'midnight pulse', note: 146.83 },
  { no: '02', name: 'After the Last Bus', mood: 'slow electric', note: 110 },
  { no: '03', name: 'Soft Machinery', mood: 'morning static', note: 196 },
]

function useSoundSketch() {
  const audioRef = useRef(null)
  const timerRef = useRef(null)
  const [playing, setPlaying] = useState(false)
  const [track, setTrack] = useState(0)

  const stop = useCallback(() => {
    window.clearInterval(timerRef.current)
    timerRef.current = null
    if (audioRef.current) {
      const { context, master } = audioRef.current
      master.gain.cancelScheduledValues(context.currentTime)
      master.gain.setTargetAtTime(0, context.currentTime, 0.08)
      window.setTimeout(() => context.close(), 320)
      audioRef.current = null
    }
    setPlaying(false)
  }, [])

  const start = useCallback((trackIndex = track) => {
    stop()
    const AudioContext = window.AudioContext || window.webkitAudioContext
    if (!AudioContext) return
    const context = new AudioContext()
    const master = context.createGain()
    const filter = context.createBiquadFilter()
    master.gain.value = 0.0001
    filter.type = 'lowpass'
    filter.frequency.value = 850
    filter.Q.value = 4
    filter.connect(master)
    master.connect(context.destination)

    const base = tracks[trackIndex].note
    const voices = [
      { ratio: 1, type: 'sine', gain: 0.16 },
      { ratio: 1.5, type: 'triangle', gain: 0.05 },
      { ratio: 0.5, type: 'sine', gain: 0.11 },
    ]

    voices.forEach((voice, index) => {
      const osc = context.createOscillator()
      const gain = context.createGain()
      const lfo = context.createOscillator()
      const lfoGain = context.createGain()
      osc.type = voice.type
      osc.frequency.value = base * voice.ratio
      osc.detune.value = index * 4 - 4
      gain.gain.value = voice.gain
      lfo.frequency.value = 0.08 + index * 0.035
      lfoGain.gain.value = 8 + index * 3
      lfo.connect(lfoGain)
      lfoGain.connect(osc.detune)
      osc.connect(gain)
      gain.connect(filter)
      osc.start()
      lfo.start()
    })

    master.gain.exponentialRampToValueAtTime(0.16, context.currentTime + 1.4)
    audioRef.current = { context, master, filter }
    let bright = false
    timerRef.current = window.setInterval(() => {
      bright = !bright
      filter.frequency.setTargetAtTime(bright ? 1500 : 540, context.currentTime, 0.65)
    }, 2200 + trackIndex * 350)
    setTrack(trackIndex)
    setPlaying(true)
  }, [stop, track])

  const toggle = useCallback(() => playing ? stop() : start(track), [playing, start, stop, track])

  const selectTrack = useCallback((index) => {
    setTrack(index)
    if (playing) start(index)
  }, [playing, start])

  useEffect(() => stop, [stop])
  return { playing, track, toggle, selectTrack }
}

function SignalField({ active }) {
  const canvasRef = useRef(null)

  useEffect(() => {
    const canvas = canvasRef.current
    const ctx = canvas.getContext('2d')
    let animation
    let width = 0
    let height = 0
    let dpr = 1
    const pointer = { x: 0.66, y: 0.36, targetX: 0.66, targetY: 0.36 }
    const dots = Array.from({ length: 58 }, (_, index) => ({
      x: (index * 73 % 100) / 100,
      y: (index * 47 % 97) / 97,
      size: 0.7 + (index % 5) * 0.35,
      speed: 0.2 + (index % 7) * 0.05,
    }))

    const resize = () => {
      dpr = Math.min(window.devicePixelRatio || 1, 2)
      width = window.innerWidth
      height = window.innerHeight
      canvas.width = width * dpr
      canvas.height = height * dpr
      canvas.style.width = `${width}px`
      canvas.style.height = `${height}px`
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
    }

    const move = (event) => {
      pointer.targetX = event.clientX / width
      pointer.targetY = event.clientY / height
    }

    const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches

    const draw = (time) => {
      const t = time * 0.00035
      pointer.x += (pointer.targetX - pointer.x) * 0.035
      pointer.y += (pointer.targetY - pointer.y) * 0.035
      ctx.clearRect(0, 0, width, height)

      const glow = ctx.createRadialGradient(
        pointer.x * width, pointer.y * height, 0,
        pointer.x * width, pointer.y * height, Math.max(width, height) * 0.62,
      )
      glow.addColorStop(0, active ? 'rgba(37, 91, 255, .34)' : 'rgba(37, 91, 255, .21)')
      glow.addColorStop(0.42, 'rgba(28, 61, 171, .09)')
      glow.addColorStop(1, 'rgba(8, 11, 12, 0)')
      ctx.fillStyle = glow
      ctx.fillRect(0, 0, width, height)

      ctx.lineWidth = 1
      for (let band = 0; band < 4; band += 1) {
        ctx.beginPath()
        for (let x = -40; x <= width + 40; x += 12) {
          const normalized = x / width
          const y = height * (0.28 + band * 0.14)
            + Math.sin(normalized * 8 + t * (1.2 + band * 0.15)) * (24 + band * 8)
            + Math.sin(normalized * 17 - t * 0.7) * 8
            + (pointer.y - 0.5) * 50 * Math.sin(normalized * Math.PI)
          if (x === -40) ctx.moveTo(x, y)
          else ctx.lineTo(x, y)
        }
        ctx.strokeStyle = band === 1
          ? `rgba(200,255,61,${active ? 0.19 : 0.08})`
          : `rgba(231,225,213,${0.035 + band * 0.008})`
        ctx.stroke()
      }

      dots.forEach((dot, index) => {
        const x = ((dot.x + t * dot.speed * 0.03) % 1) * width
        const y = (dot.y + Math.sin(t + index) * 0.008) * height
        const distance = Math.hypot(x - pointer.x * width, y - pointer.y * height)
        const near = Math.max(0, 1 - distance / 240)
        ctx.beginPath()
        ctx.arc(x, y, dot.size + near * 1.6, 0, Math.PI * 2)
        ctx.fillStyle = near > 0.1 ? `rgba(200,255,61,${0.18 + near * 0.6})` : 'rgba(231,225,213,.16)'
        ctx.fill()
      })
      if (!reducedMotion) animation = requestAnimationFrame(draw)
    }

    resize()
    window.addEventListener('resize', resize)
    window.addEventListener('pointermove', move)
    if (!reducedMotion) animation = requestAnimationFrame(draw)
    else draw(0)
    return () => {
      cancelAnimationFrame(animation)
      window.removeEventListener('resize', resize)
      window.removeEventListener('pointermove', move)
    }
  }, [active])

  return <canvas className="signal-field" ref={canvasRef} aria-hidden="true" />
}

function Arrow() {
  return <span aria-hidden="true">↗</span>
}

function App() {
  const { playing, track, toggle, selectTrack } = useSoundSketch()
  const [menuOpen, setMenuOpen] = useState(false)

  return (
    <div className={playing ? 'site is-playing' : 'site'}>
      <SignalField active={playing} />
      <div className="grain" aria-hidden="true" />

      <header className="nav">
        <a className="brand" href="#top" aria-label="Another Dan, home">
          <span className="brand-mark"><i /></span>
          <span>Another Dan</span>
        </a>
        <button className="menu-toggle" onClick={() => setMenuOpen(!menuOpen)} aria-expanded={menuOpen} aria-label="Toggle navigation">
          <span /> <span />
        </button>
        <nav className={menuOpen ? 'nav-links open' : 'nav-links'} aria-label="Main navigation">
          <a href="#listen" onClick={() => setMenuOpen(false)}>Listen</a>
          <a href="#story" onClick={() => setMenuOpen(false)}>Story</a>
          <a href="#use-it" onClick={() => setMenuOpen(false)}>Use the music</a>
        </nav>
        <span className="location"><i /> CPH · 55.6761° N</span>
      </header>

      <main id="top">
        <section className="hero" aria-labelledby="hero-title">
          <div className="hero-copy">
            <p className="eyebrow"><span>One human</span><span>Many instruments</span></p>
            <h1 id="hero-title">
              <span>Sounds for the</span>
              <span className="outline">strange</span>
              <span>in-between.</span>
            </h1>
            <div className="hero-bottom">
              <p>Small-room recordings, wide-open feelings. A one-man band transmitting from Copenhagen.</p>
              <a className="round-link" href="#listen" aria-label="Listen to the latest transmission">↓</a>
            </div>
          </div>

          <aside className="now-playing" aria-label="Interactive sound sketch">
            <div className="card-meta">
              <span>AD · 001</span>
              <span>Sound sketch</span>
            </div>
            <button className="record" onClick={toggle} aria-label={playing ? 'Pause sound sketch' : 'Play sound sketch'}>
              <span className="record-rings" aria-hidden="true">
                <i /><i /><i /><i />
              </span>
              <span className="record-center">{playing ? 'Ⅱ' : '▶'}</span>
            </button>
            <div className="card-title">
              <span>Playing now</span>
              <strong>{tracks[track].name}</strong>
            </div>
            <div className="meter" aria-hidden="true">
              {Array.from({ length: 24 }, (_, i) => <i key={i} style={{ '--i': i }} />)}
            </div>
            <p className="sound-note">Tap to hear a tiny generative sound sketch — headphones welcome.</p>
          </aside>
        </section>

        <div className="ticker" aria-hidden="true">
          <div>
            <span>NEW TRANSMISSION</span><b>✦</b><span>NIGHT BUS TAPES</span><b>✦</b><span>MADE IN COPENHAGEN</span><b>✦</b>
            <span>NEW TRANSMISSION</span><b>✦</b><span>NIGHT BUS TAPES</span><b>✦</b><span>MADE IN COPENHAGEN</span><b>✦</b>
          </div>
        </div>

        <section className="release section" id="listen" aria-labelledby="release-title">
          <div className="section-index">01 / Listen</div>
          <div className="release-heading">
            <p className="kicker">Latest transmission</p>
            <h2 id="release-title">Night Bus<br />Tapes</h2>
            <p className="release-note">Three sketches made after midnight, somewhere between the last train home and the first coffee of the day.</p>
          </div>

          <div className="track-list">
            {tracks.map((item, index) => (
              <button key={item.name} className={track === index ? 'track active' : 'track'} onClick={() => selectTrack(index)}>
                <span className="track-no">{item.no}</span>
                <span className="track-name">{item.name}</span>
                <span className="track-mood">{item.mood}</span>
                <span className="track-icon">{track === index && playing ? '◼' : '▶'}</span>
              </button>
            ))}
            <div className="streaming">
              <span>Find full releases</span>
              <div>
                <a href="https://open.spotify.com/search/Another%20Dan" target="_blank" rel="noreferrer">Spotify <Arrow /></a>
                <a href="https://music.youtube.com/search?q=Another%20Dan" target="_blank" rel="noreferrer">YouTube Music <Arrow /></a>
                <a href="https://bandcamp.com/search?q=Another%20Dan" target="_blank" rel="noreferrer">Bandcamp <Arrow /></a>
              </div>
            </div>
          </div>
        </section>

        <section className="story section" id="story" aria-labelledby="story-title">
          <div className="section-index">02 / Story</div>
          <div className="story-grid">
            <h2 id="story-title">One room.<br />One take.<br /><em>Another Dan.</em></h2>
            <div className="story-copy">
              <p className="lead">No grand studio. No mysterious collective. Just one guy in Copenhagen, interpreting what a one-man band can be.</p>
              <p>The songs start with a pulse, a half-remembered melody, or the hum of the city outside. Guitars meet worn-out keyboards; tidy ideas get a little lost. That’s usually where the good part begins.</p>
              <div className="coordinates">
                <span>55°40′34″N</span>
                <i />
                <span>12°34′06″E</span>
              </div>
            </div>
          </div>
          <div className="manifesto" aria-label="Music qualities">
            <span>Human</span><span>Odd</span><span>Warm</span><span>Loud enough</span>
          </div>
        </section>

        <section className="use section" id="use-it" aria-labelledby="use-title">
          <div className="license-card">
            <div className="section-index">03 / Use it</div>
            <div className="license-stamp" aria-hidden="true"><span>RF</span><small>ROYALTY<br />FRIENDLY</small></div>
            <div className="license-copy">
              <p className="kicker">For makers, editors & storytellers</p>
              <h2 id="use-title">Put the music<br />in your thing.</h2>
              <p>Making a film, podcast, game, or something harder to explain? The music is available royalty-free for independent creators. Just send a note first and tell me what you’re building.</p>
              <a className="email-button" href="mailto:dan@anotherdan.com?subject=Using%20your%20music">
                <span>Say hello</span><strong>dan@anotherdan.com</strong><Arrow />
              </a>
            </div>
          </div>
        </section>
      </main>

      <footer>
        <a className="brand footer-brand" href="#top"><span className="brand-mark"><i /></span><span>Another Dan</span></a>
        <p>Independent sounds from Copenhagen.<br />© {new Date().getFullYear()} — still making noise.</p>
        <a className="back-top" href="#top">Back to top ↑</a>
      </footer>
    </div>
  )
}

createRoot(document.getElementById('root')).render(<App />)
