import { CONFIG } from '../config.js';
import { state, resetState } from './state.js';
import { Loop } from './loop.js';
import { saveHighscore } from '../systems/storage.js';
import { ReactorObject, getObjectEffect } from '../gameplay/objects.js';
import { Effects } from '../gameplay/effects.js';
import { AudioSystem } from '../systems/audio.js';

export class Game {
  constructor(canvas, ui, gameOverUi) {
    this.canvas = canvas;
    this.ctx = canvas.getContext('2d');
    this.ui = ui;
    this.gameOverUi = gameOverUi;
    this.dpr = Math.min(window.devicePixelRatio || 1, 2);
    this.loop = new Loop(delta => this.update(delta), () => this.render());
    this.resize = this.resize.bind(this);
    this.objects = [];
    this.effects = new Effects();
    this.audio = new AudioSystem();
    this.warningCooldown = 0;
    this.spawnTimer = 0;
  }

  init() {
    window.addEventListener('resize', this.resize);
    this.resize();
    this.render();
  }

  start() {
    resetState();
    this.objects.length = 0;
    this.effects.reset();
    this.spawnTimer = .35;
    this.warningCooldown = 0;
    this.gameOverUi.hide();
    this.loop.start();
    this.ui.update(state);
  }

  stop() {
    this.loop.stop();
    state.running = false;
    this.commitHighscore();
    this.ui.update(state);
  }

  actionAt(x, y) {
    if (!state.running) return;
    for (let i = this.objects.length - 1; i >= 0; i--) {
      const object = this.objects[i];
      if (!object.contains(x, y)) continue;
      this.resolveObject(object);
      this.objects.splice(i, 1);
      return;
    }
    state.combo = 0;
  }

  resolveObject(object) {
    const effect = getObjectEffect(object);
    state.temperature += effect.temperature;
    state.score = Math.max(0, state.score + effect.score + Math.max(0, state.combo - 1) * 3);
    state.hits += 1;

    const harmful = effect.score < 0;
    this.audio.collect(object.type);
    state.combo = harmful ? 0 : Math.min(9, state.combo + 1);
    const sign = effect.temperature > 0 ? '+' : '';
    this.effects.burst(object.x, object.y, effect.color, `${sign}${effect.temperature}°`, harmful);
    this.checkGameOver();
  }

  update(delta) {
    this.effects.update(delta);
    if (!state.running) return;
    state.elapsed += delta;
    state.score += Math.floor(delta * 8);
    state.temperature += CONFIG.gameplay.passiveHeatPerSecond * delta;

    this.spawnTimer -= delta;
    if (this.spawnTimer <= 0) {
      this.spawnObject();
      const interval = Math.max(
        CONFIG.gameplay.minimumSpawnInterval,
        CONFIG.gameplay.startSpawnInterval - state.elapsed * CONFIG.gameplay.spawnAcceleration
      );
      this.spawnTimer = interval;
    }

    for (const object of this.objects) object.update(delta);
    const expired = this.objects.filter(object => object.expired);
    if (expired.length) {
      state.misses += expired.length;
      state.combo = 0;
      state.temperature += expired.length * CONFIG.gameplay.missPenalty;
    }
    this.objects = this.objects.filter(object => !object.expired);
    this.warningCooldown = Math.max(0, this.warningCooldown - delta);
    const critical = state.temperature <= 15 || state.temperature >= 85;
    if (critical && this.warningCooldown <= 0) {
      this.audio.warning();
      this.warningCooldown = 1.15;
    }
    this.checkGameOver();
    this.ui.update(state);
  }

  spawnObject() {
    if (this.objects.length >= 8) return;
    let object;
    let attempts = 0;
    do {
      object = new ReactorObject({
        width: this.width,
        height: this.height,
        elapsed: state.elapsed,
        lifetime: CONFIG.gameplay.objectLifetime
      });
      attempts += 1;
    } while (attempts < 14 && this.overlapsReactor(object));
    this.objects.push(object);
  }

  overlapsReactor(object) {
    const reactorRadius = Math.min(this.width, this.height) * CONFIG.gameplay.reactorRadiusFactor * 1.65;
    return Math.hypot(object.x - this.width / 2, object.y - this.height / 2) < reactorRadius + object.radius;
  }

  checkGameOver() {
    if (!state.running) return;
    if (state.temperature > 0 && state.temperature < 100) return;

    state.temperature = Math.max(0, Math.min(100, state.temperature));
    state.gameOverReason = state.temperature >= 100 ? 'overheated' : 'frozen';
    state.gameOver = true;
    state.running = false;
    const previousHighscore = state.highscore;
    const isNewRecord = state.score > previousHighscore;
    this.commitHighscore();
    this.effects.explosion(this.width / 2, this.height / 2, state.temperature >= 100 ? '#ff4a00' : '#39dfff');
    this.audio.explosion();
    this.ui.update(state);
    window.setTimeout(() => {
      this.gameOverUi.show(state, { previousHighscore, isNewRecord });
      if (isNewRecord) window.setTimeout(() => this.audio.record(), 360);
    }, 620);
  }

  commitHighscore() {
    if (state.score > state.highscore) {
      state.highscore = state.score;
      saveHighscore(state.highscore);
    }
  }

  resize() {
    const { innerWidth: width, innerHeight: height } = window;
    this.canvas.width = Math.floor(width * this.dpr);
    this.canvas.height = Math.floor(height * this.dpr);
    this.canvas.style.width = `${width}px`;
    this.canvas.style.height = `${height}px`;
    this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);
    this.width = width;
    this.height = height;
  }

  render() {
    const ctx = this.ctx;
    const w = this.width;
    const h = this.height;
    if (!w || !h) return;

    ctx.save();
    if (this.effects.shake > 0) {
      ctx.translate((Math.random() - .5) * this.effects.shake, (Math.random() - .5) * this.effects.shake);
    }

    const gradient = ctx.createRadialGradient(w/2, h/2, 20, w/2, h/2, Math.max(w,h)*.7);
    gradient.addColorStop(0, '#24100a');
    gradient.addColorStop(.45, '#08080c');
    gradient.addColorStop(1, '#020204');
    ctx.fillStyle = gradient;
    ctx.fillRect(-20,-20,w+40,h+40);

    const t = performance.now() / 1000;
    this.drawGrid(ctx,w,h,t);
    for (const object of this.objects) object.draw(ctx, t);
    this.drawReactor(ctx,w/2,h/2,t);
    this.effects.draw(ctx,w,h);
    ctx.restore();
  }

  drawGrid(ctx,w,h,t) {
    ctx.save();
    ctx.globalAlpha = .14;
    ctx.strokeStyle = '#ff5a00';
    ctx.lineWidth = 1;
    const spacing = 54;
    const offset = (t * 8) % spacing;
    for (let x = -spacing + offset; x < w + spacing; x += spacing) {
      ctx.beginPath(); ctx.moveTo(x,0); ctx.lineTo(x,h); ctx.stroke();
    }
    for (let y = -spacing + offset; y < h + spacing; y += spacing) {
      ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(w,y); ctx.stroke();
    }
    ctx.restore();
  }

  drawReactor(ctx,x,y,t) {
    const base = Math.min(this.width,this.height) * .13;
    const tempStress = Math.abs(state.temperature - 50) / 50;
    const pulseSpeed = 2.5 + tempStress * 5;
    const pulse = 1 + Math.sin(t*pulseSpeed)*(.035 + tempStress * .03);
    ctx.save();
    ctx.translate(x,y);
    ctx.scale(pulse,pulse);

    for (let i=5;i>=1;i--) {
      ctx.beginPath();
      ctx.arc(0,0,base*(1+i*.18),0,Math.PI*2);
      ctx.strokeStyle = `rgba(255,90,0,${.05*i})`;
      ctx.lineWidth = 3;
      ctx.stroke();
    }

    ctx.rotate(t*(.16 + tempStress * .24));
    ctx.setLineDash([18,10]);
    ctx.beginPath();
    ctx.arc(0,0,base*1.35,0,Math.PI*2);
    ctx.strokeStyle = state.temperature < 30 ? '#43dfff' : '#ff6a00';
    ctx.lineWidth = 12;
    ctx.stroke();
    ctx.setLineDash([]);

    const cold = Math.max(0, (50 - state.temperature) / 50);
    const hot = Math.max(0, (state.temperature - 50) / 50);
    const mid = 1 - Math.max(cold, hot);
    const inner = cold > hot ? '#d8fdff' : hot > .45 ? '#fff2a0' : '#fff7c8';
    const middle = cold > hot ? '#53eaff' : hot > .45 ? '#ffb000' : '#ffd000';
    const edge = cold > hot ? '#005dff' : hot > .45 ? '#ff2100' : '#ff5a00';
    const rim = cold > hot ? '#00154f' : hot > .45 ? '#4b0000' : '#6d0800';
    const core = ctx.createRadialGradient(-base*.2,-base*.2,base*.08,0,0,base);
    core.addColorStop(0,inner);
    core.addColorStop(.18, middle);
    core.addColorStop(.55, edge);
    core.addColorStop(1, rim);
    ctx.shadowColor = cold > hot ? '#00aaff' : hot > .45 ? '#ff2300' : '#ff5a00';
    ctx.shadowBlur = 45 + tempStress * 30;
    ctx.beginPath();
    ctx.arc(0,0,base,0,Math.PI*2);
    ctx.fillStyle = core;
    ctx.fill();

    ctx.shadowBlur = 0;
    ctx.strokeStyle = 'rgba(255,255,255,.4)';
    ctx.lineWidth = 2;
    for (let i=0;i<8;i++) {
      const a = i*Math.PI/4 + Math.sin(t*1.5+i)*.08;
      ctx.beginPath();
      ctx.moveTo(Math.cos(a)*base*.15,Math.sin(a)*base*.15);
      ctx.lineTo(Math.cos(a+.25)*base*.9,Math.sin(a+.25)*base*.9);
      ctx.stroke();
    }
    ctx.restore();
  }
}
