/**
 * Ambient life: cosmetic critters + drifting details that make the world feel
 * alive. Nothing here touches game state — pure decoration.
 *
 * Layers (called from render.ts):
 *  - collectEntityDraws: chickens + ducks, depth-sorted with the player
 *  - drawAir: butterflies, falling leaves, birds, fish jumps, chimney smoke
 *  - drawNightGlow: fireflies (post color-grade, so they punch through dusk)
 */
import { COLS, ROWS, TILE, tileHash, walkable, type Tile } from './world.ts';
import type { GameState } from './game.ts';
import type { Assets } from './assets.ts';

interface Drawable { y: number; draw: () => void }

interface Chicken {
  x: number; y: number; tx: number; ty: number;
  state: 'walk' | 'idle' | 'peck'; stateT: number; flip: boolean; phase: number;
}
interface Duck { x: number; y: number; tx: number; ty: number; dabbleT: number; flip: boolean; phase: number }
interface Butterfly { x: number; y: number; phase: number; hue: number }
interface Bird { x: number; y: number; vx: number; phase: number }
interface Leaf { x: number; y: number; vy: number; phase: number; life: number; maxLife: number; color: string }
interface Fish { x: number; y: number; t: number; flip: boolean }
interface Ripple { x: number; y: number; t: number }
interface Puff { x: number; y: number; t: number; drift: number }

const chickens: Chicken[] = [];
const ducks: Duck[] = [];
const butterflies: Butterfly[] = [];
const birds: Bird[] = [];
const leaves: Leaf[] = [];
const fishes: Fish[] = [];
const ripples: Ripple[] = [];
const puffs: Puff[] = [];

let waterTiles: Array<{ x: number; y: number }> = [];
let treeTiles: Array<{ x: number; y: number }> = [];
let tilesRef: Tile[][] | null = null;
let birdTimer = 5;
let fishTimer = 4;
let leafTimer = 1;
let puffTimer = 0;

/** 1 in full daylight, 0 deep at night */
export function dayFactor(time: number): number {
  return Math.max(0, Math.min(1, (0.8 - time) / 0.18));
}

function reset(g: GameState): void {
  tilesRef = g.tiles;
  waterTiles = [];
  treeTiles = [];
  for (let y = 0; y < ROWS; y++) {
    for (let x = 0; x < COLS; x++) {
      if (g.tiles[y][x].kind === 'water') waterTiles.push({ x, y });
      if (g.tiles[y][x].tree) treeTiles.push({ x, y });
    }
  }
  chickens.length = 0;
  for (let i = 0; i < 3; i++) {
    chickens.push({
      x: 10.5 + i * 0.9, y: 4.5 + i * 0.7, tx: 10.5 + i, ty: 5,
      state: 'idle', stateT: i * 0.8, flip: i % 2 === 0, phase: i * 2.1,
    });
  }
  ducks.length = 0;
  const deep = waterTiles.filter((w) => w.x > 16 && w.y < 5);
  for (let i = 0; i < 2 && deep.length; i++) {
    const w = deep[Math.floor(Math.random() * deep.length)];
    ducks.push({ x: w.x + 0.5, y: w.y + 0.5, tx: w.x + 0.5, ty: w.y + 0.5, dabbleT: 0, flip: i === 0, phase: i * 3 });
  }
  butterflies.length = 0;
  for (let i = 0; i < 4; i++) {
    butterflies.push({
      x: 3 + tileHash(i, 7) * (COLS - 6), y: 3 + tileHash(7, i) * (ROWS - 6),
      phase: i * 1.7, hue: i % 2 ? 35 : 0,
    });
  }
}

function chickenSpotOk(g: GameState, x: number, y: number): boolean {
  const tx = Math.floor(x), ty = Math.floor(y);
  if (!walkable(g.tiles, tx, ty)) return false;
  const t = g.tiles[ty][tx];
  return t.kind !== 'soil' && !t.crop && t.deco === null;
}

export function updateAmbient(g: GameState, dt: number): void {
  if (g.tiles !== tilesRef) reset(g);
  const day = dayFactor(g.time);

  // --- chickens: wander, pause, peck ---
  for (const c of chickens) {
    c.stateT -= dt;
    if (c.state === 'walk') {
      const dx = c.tx - c.x, dy = c.ty - c.y;
      const d = Math.hypot(dx, dy);
      if (d < 0.08 || c.stateT <= 0) {
        c.state = Math.random() < 0.55 ? 'peck' : 'idle';
        c.stateT = 0.8 + Math.random() * 1.8;
      } else {
        c.x += (dx / d) * 0.85 * dt;
        c.y += (dy / d) * 0.85 * dt;
        c.flip = dx < 0;
      }
    } else if (c.stateT <= 0) {
      for (let tries = 0; tries < 8; tries++) {
        const nx = 9.5 + Math.random() * 5.5; // grassy yard between field and path
        const ny = 2.5 + Math.random() * 4.5;
        if (chickenSpotOk(g, nx, ny)) {
          c.tx = nx; c.ty = ny; c.state = 'walk'; c.stateT = 4;
          break;
        }
      }
      if (c.state !== 'walk') c.stateT = 1;
    }
  }

  // --- ducks: drift around the pond ---
  for (const d of ducks) {
    d.dabbleT -= dt;
    const dx = d.tx - d.x, dy = d.ty - d.y;
    const dist = Math.hypot(dx, dy);
    if (dist < 0.1) {
      if (d.dabbleT < -3 && waterTiles.length) {
        const w = waterTiles[Math.floor(Math.random() * waterTiles.length)];
        d.tx = w.x + 0.5; d.ty = w.y + 0.5;
        d.dabbleT = 1.2; // dabble before setting off
      }
    } else {
      d.x += (dx / dist) * 0.45 * dt;
      d.y += (dy / dist) * 0.45 * dt;
      d.flip = dx < 0;
    }
  }

  // --- butterflies: lazy figure-eights over the grass (daytime) ---
  for (const b of butterflies) {
    b.phase += dt;
    b.x += Math.sin(b.phase * 0.7) * 0.5 * dt + Math.sin(b.phase * 0.13) * 0.4 * dt;
    b.y += Math.cos(b.phase * 0.9) * 0.4 * dt;
    b.x = Math.max(1.5, Math.min(COLS - 1.5, b.x));
    b.y = Math.max(1.5, Math.min(ROWS - 1.5, b.y));
  }

  // --- birds crossing the sky ---
  birdTimer -= dt;
  if (birdTimer <= 0 && day > 0.3) {
    birdTimer = 7 + Math.random() * 12;
    const ltr = Math.random() < 0.5;
    const n = 1 + Math.floor(Math.random() * 3);
    for (let i = 0; i < n; i++) {
      birds.push({
        x: (ltr ? -3 : COLS + 3) * TILE - i * 26 * (ltr ? 1 : -1),
        y: (1 + Math.random() * (ROWS - 4)) * TILE + i * 14,
        vx: (ltr ? 1 : -1) * (110 + Math.random() * 40),
        phase: Math.random() * 7,
      });
    }
  }
  for (let i = birds.length - 1; i >= 0; i--) {
    const b = birds[i];
    b.x += b.vx * dt;
    b.phase += dt;
    if (b.x < -4 * TILE || b.x > (COLS + 4) * TILE) birds.splice(i, 1);
  }

  // --- leaves fluttering off trees ---
  leafTimer -= dt;
  if (leafTimer <= 0 && treeTiles.length) {
    leafTimer = 0.9 + Math.random() * 1.6;
    const t = treeTiles[Math.floor(Math.random() * treeTiles.length)];
    const life = 3 + Math.random() * 2;
    leaves.push({
      x: (t.x + 0.2 + Math.random() * 0.6) * TILE,
      y: (t.y - 0.6 + Math.random() * 0.5) * TILE,
      vy: 14 + Math.random() * 10,
      phase: Math.random() * 7,
      life, maxLife: life,
      color: Math.random() < 0.3 ? '#c9a23e' : '#7da84e',
    });
  }
  for (let i = leaves.length - 1; i >= 0; i--) {
    const l = leaves[i];
    l.life -= dt;
    l.phase += dt;
    l.y += l.vy * dt;
    l.x += Math.sin(l.phase * 2.4) * 16 * dt;
    if (l.life <= 0) leaves.splice(i, 1);
  }

  // --- fish jumping in the pond ---
  fishTimer -= dt;
  if (fishTimer <= 0 && waterTiles.length) {
    fishTimer = 5 + Math.random() * 7;
    const w = waterTiles[Math.floor(Math.random() * waterTiles.length)];
    fishes.push({ x: (w.x + 0.5) * TILE, y: (w.y + 0.5) * TILE, t: 0, flip: Math.random() < 0.5 });
    ripples.push({ x: (w.x + 0.5) * TILE, y: (w.y + 0.5) * TILE, t: 0 });
  }
  for (let i = fishes.length - 1; i >= 0; i--) {
    const f = fishes[i];
    f.t += dt;
    if (f.t >= 0.85) {
      ripples.push({ x: f.x + (f.flip ? -14 : 14), y: f.y, t: 0 });
      fishes.splice(i, 1);
    }
  }
  for (let i = ripples.length - 1; i >= 0; i--) {
    ripples[i].t += dt;
    if (ripples[i].t > 1.1) ripples.splice(i, 1);
  }

  // --- chimney smoke ---
  puffTimer -= dt;
  if (puffTimer <= 0) {
    puffTimer = 1.1 + Math.random() * 0.5;
    puffs.push({ x: 0, y: 0, t: 0, drift: Math.random() * 2 - 0.5 });
  }
  for (let i = puffs.length - 1; i >= 0; i--) {
    puffs[i].t += dt;
    if (puffs[i].t > 4) puffs.splice(i, 1);
  }
}

// ---------------------------------------------------------------------------
// drawing
// ---------------------------------------------------------------------------

function drawTwoFrame(
  ctx: CanvasRenderingContext2D, assets: Assets, frames: 'chicken' | 'duck',
  frame: number, cx: number, by: number, h: number, flip: boolean,
): void {
  const s = assets[frames][frame];
  const w = (s.canvas.width / s.canvas.height) * h;
  ctx.save();
  ctx.translate(cx, by);
  ctx.scale(flip ? -1 : 1, 1);
  ctx.drawImage(s.canvas, -w / 2, -h, w, h);
  ctx.restore();
}

export function collectEntityDraws(
  draws: Drawable[], ctx: CanvasRenderingContext2D, assets: Assets, elapsed: number,
): void {
  for (const c of chickens) {
    const by = c.y * TILE + TILE * 0.32;
    draws.push({
      y: by,
      draw: () => {
        ctx.fillStyle = 'rgba(30,50,25,.22)';
        ctx.beginPath();
        ctx.ellipse(c.x * TILE, by - 1, TILE * 0.17, TILE * 0.06, 0, 0, Math.PI * 2);
        ctx.fill();
        const hop = c.state === 'walk' ? Math.abs(Math.sin((elapsed + c.phase) * 10)) * 2.5 : 0;
        const peck = c.state === 'peck' && Math.sin((elapsed + c.phase) * 6) > 0;
        drawTwoFrame(ctx, assets, 'chicken', peck ? 1 : 0, c.x * TILE, by - hop, TILE * 0.52, c.flip);
      },
    });
  }
  for (const d of ducks) {
    const bob = Math.sin((elapsed + d.phase) * 1.8) * 1.5;
    const by = d.y * TILE + TILE * 0.22 + bob;
    draws.push({
      y: by,
      draw: () => {
        // wake ripple
        ctx.strokeStyle = 'rgba(230,245,255,.35)';
        ctx.lineWidth = 1.5;
        ctx.beginPath();
        ctx.ellipse(d.x * TILE, by - 2, TILE * 0.3, TILE * 0.09, 0, 0, Math.PI * 2);
        ctx.stroke();
        const dabbling = d.dabbleT > 0 && Math.sin((elapsed + d.phase) * 4) > -0.2;
        drawTwoFrame(ctx, assets, 'duck', dabbling ? 1 : 0, d.x * TILE, by, TILE * 0.42, d.flip);
      },
    });
  }
}

export function drawAir(
  ctx: CanvasRenderingContext2D, g: GameState, elapsed: number, smokeFrom: { x: number; y: number },
): void {
  const day = dayFactor(g.time);

  // ripples (on the water, under everything airborne)
  for (const r of ripples) {
    const k = r.t / 1.1;
    ctx.strokeStyle = `rgba(235,248,255,${(1 - k) * 0.55})`;
    ctx.lineWidth = 1.6;
    ctx.beginPath();
    ctx.ellipse(r.x, r.y, 4 + k * 16, (4 + k * 16) * 0.45, 0, 0, Math.PI * 2);
    ctx.stroke();
  }

  // jumping fish: little silver arc
  for (const f of fishes) {
    const k = f.t / 0.85;
    const dir = f.flip ? -1 : 1;
    const fx = f.x + dir * (k - 0.5) * 28;
    const fy = f.y - Math.sin(k * Math.PI) * 26;
    ctx.save();
    ctx.translate(fx, fy);
    ctx.rotate(dir * (k - 0.5) * 1.8);
    ctx.fillStyle = '#9fc4d8';
    ctx.beginPath();
    ctx.ellipse(0, 0, 7, 3, 0, 0, Math.PI * 2);
    ctx.fill();
    ctx.fillStyle = '#7ba6bd';
    ctx.beginPath();
    ctx.moveTo(-dir * 6, 0);
    ctx.lineTo(-dir * 11, -3);
    ctx.lineTo(-dir * 11, 3);
    ctx.fill();
    ctx.restore();
  }

  // leaves
  for (const l of leaves) {
    const a = Math.min(1, l.life / l.maxLife * 2) * 0.85;
    ctx.save();
    ctx.translate(l.x, l.y);
    ctx.rotate(Math.sin(l.phase * 2.4) * 0.9);
    ctx.globalAlpha = a;
    ctx.fillStyle = l.color;
    ctx.beginPath();
    ctx.ellipse(0, 0, 3.4, 1.8, 0.5, 0, Math.PI * 2);
    ctx.fill();
    ctx.restore();
  }
  ctx.globalAlpha = 1;

  // butterflies (fade out at dusk)
  if (day > 0.05) {
    for (const b of butterflies) {
      const flap = Math.sin((elapsed + b.phase) * 14);
      const bx = b.x * TILE;
      const by = b.y * TILE + Math.sin((elapsed + b.phase) * 2.3) * 4;
      ctx.save();
      ctx.translate(bx, by);
      ctx.globalAlpha = day * 0.95;
      ctx.fillStyle = b.hue ? '#f2b53e' : '#f5f0ff';
      const w = 4.4, sq = 0.25 + Math.abs(flap) * 0.75;
      ctx.beginPath();
      ctx.ellipse(-w * 0.55 * sq, 0, w * sq, w * 0.62, -0.4, 0, Math.PI * 2);
      ctx.ellipse(w * 0.55 * sq, 0, w * sq, w * 0.62, 0.4, 0, Math.PI * 2);
      ctx.fill();
      ctx.fillStyle = 'rgba(60,45,30,.9)';
      ctx.fillRect(-0.7, -2.6, 1.4, 5.2);
      ctx.restore();
    }
    ctx.globalAlpha = 1;
  }

  // birds
  if (birds.length) {
    ctx.fillStyle = 'rgba(52,48,60,.85)';
    for (const b of birds) {
      const flap = Math.sin(b.phase * 11);
      const dir = b.vx > 0 ? 1 : -1;
      ctx.save();
      ctx.translate(b.x, b.y + Math.sin(b.phase * 2) * 3);
      ctx.scale(dir, 1);
      ctx.beginPath();
      ctx.ellipse(0, 0, 5, 2.2, 0, 0, Math.PI * 2);
      ctx.moveTo(-1, 0);
      ctx.quadraticCurveTo(-5, -5 - flap * 4, -10, -2 - flap * 5);
      ctx.quadraticCurveTo(-4, 1 - flap, -1, 1);
      ctx.fill();
      ctx.restore();
    }
  }

  // chimney smoke
  for (const p of puffs) {
    const k = p.t / 4;
    ctx.globalAlpha = (1 - k) * 0.34;
    ctx.fillStyle = '#e8e4da';
    ctx.beginPath();
    ctx.arc(smokeFrom.x + Math.sin(p.t * 1.3) * 5 + p.drift * p.t * 7, smokeFrom.y - p.t * 17, 3.5 + k * 9, 0, Math.PI * 2);
    ctx.fill();
  }
  ctx.globalAlpha = 1;
}

/** fireflies — drawn after the night color grade so they glow */
export function drawNightGlow(ctx: CanvasRenderingContext2D, g: GameState, elapsed: number): void {
  const night = 1 - dayFactor(g.time);
  if (night < 0.25) return;
  ctx.save();
  ctx.globalCompositeOperation = 'lighter';
  for (let i = 0; i < 14; i++) {
    const hx = tileHash(i * 3 + 2, i + 11);
    const hy = tileHash(i + 5, i * 7 + 3);
    const fx = (2 + hx * (COLS - 4)) * TILE + Math.sin(elapsed * 0.5 + i * 2.2) * TILE * 0.9;
    const fy = (2 + hy * (ROWS - 4)) * TILE + Math.cos(elapsed * 0.4 + i * 1.7) * TILE * 0.6;
    const pulse = Math.max(0, Math.sin(elapsed * 1.8 + i * 2.9));
    const a = night * pulse * 0.85;
    if (a < 0.04) continue;
    const grad = ctx.createRadialGradient(fx, fy, 0, fx, fy, 9);
    grad.addColorStop(0, `rgba(220,255,140,${a})`);
    grad.addColorStop(0.35, `rgba(180,230,90,${a * 0.4})`);
    grad.addColorStop(1, 'rgba(180,230,90,0)');
    ctx.fillStyle = grad;
    ctx.beginPath();
    ctx.arc(fx, fy, 9, 0, Math.PI * 2);
    ctx.fill();
  }
  ctx.restore();
}
