import Phaser from 'phaser';
import { Director } from '../core/Director';
import { Audio } from '../core/Audio';
import { activateDay, evalEnding, interactScript, talkScript, taskActive, completeTask, DNode } from '../core/quests';
import { NPCS } from '../data/content';
import type { UIScene } from './UIScene';

const SPEED = 96;
const EXPR: Record<string, number> = { smile: 0, flat: 1, off: 2 };
const OUTDOOR = new Set(['plaza', 'lake', 'field', 'well', 'fog']);
const narr = (text: string): DNode => ({ speaker: '', text });

interface Ent { id: string; kind: 'named' | 'bird' | 'extra'; sprite: Phaser.GameObjects.Sprite; anim?: string; tween?: Phaser.Tweens.Tween; }

export class WorldScene extends Phaser.Scene {
  mapKey!: string;
  map!: Phaser.Tilemaps.Tilemap;
  obs!: Phaser.Tilemaps.TilemapLayer;
  player!: Phaser.Physics.Arcade.Sprite;
  facing = 'down';
  cursors!: Phaser.Types.Input.Keyboard.CursorKeys;
  wasd!: any;
  ents: Ent[] = [];
  interacts: { x: number; y: number; kind: string; props: any }[] = [];
  doors: { rect: Phaser.Geom.Rectangle; target: string; dest: string }[] = [];
  private sat!: any; private vig!: any; private neg!: any;
  private target: any = null;
  private doorCd = 0; private idle = 0; private rainT = 0; private trans = false;
  private negFrames = 0; private offFlash = false;
  private t14 = false; private t14t = 0; private t14fade = false;
  private forced = false; private forcedT = 0;
  private handlers: Record<string, Function> = {};

  constructor() { super('World'); }

  get ui(): UIScene { return this.scene.get('UI') as UIScene; }

  create(data: { map: string; spawn: string }) {
    this.mapKey = data.map;
    this.ents = []; this.interacts = []; this.doors = [];
    this.target = null; this.doorCd = 300; this.idle = 0; this.trans = false;
    this.t14 = false; this.t14t = 0; this.t14fade = false; this.forced = false;

    this.map = this.make.tilemap({ key: 'map_' + data.map });
    // Tiled 地图数据被 Phaser 缓存并共享；重访地图前清掉上次的 tilemapLayer 引用，否则 createLayer 返回 null
    this.map.layers.forEach((l: any) => (l.tilemapLayer = null));
    const ts = this.map.addTilesetImage('tiles', 'tiles')!;
    let ground = this.map.createLayer('ground', ts, 0, 0);
    let obsL = this.map.createLayer('obstacles', ts, 0, 0);
    if (!ground || !obsL) { this.map.layers.forEach((l: any) => (l.tilemapLayer = null)); ground = ground || this.map.createLayer('ground', ts, 0, 0); obsL = obsL || this.map.createLayer('obstacles', ts, 0, 0); }
    ground!.setDepth(0);
    this.obs = obsL!; this.obs.setDepth(1);
    this.obs.setCollisionByExclusion([-1]);

    const W = this.map.widthInPixels, H = this.map.heightInPixels;
    this.physics.world.setBounds(0, 0, W, H);
    this.cameras.main.setBounds(0, 0, W, H);
    this.cameras.main.setBackgroundColor('#101014');

    // spawn point
    const objs = this.map.getObjectLayer('objects')?.objects || [];
    const sp = objs.find((o) => o.name === data.spawn) || objs.find((o) => o.type === 'spawn');
    const px = (sp?.x || 32) + 8, py = (sp?.y || 32) + 8;
    this.player = this.physics.add.sprite(px, py, 'player', 0).setDepth(50);
    this.player.body!.setSize(10, 7); (this.player.body as Phaser.Physics.Arcade.Body).setOffset(3, 16);
    this.player.setCollideWorldBounds(true);
    this.physics.add.collider(this.player, this.obs);
    // 小地图整屏居中，大地图跟随并钳制边界（§12）
    if (W <= 320 && H <= 180) this.cameras.main.centerOn(W / 2, H / 2);
    else this.cameras.main.startFollow(this.player, true, 0.18, 0.18);
    this.cameras.main.roundPixels = true;

    // entities + interacts + doors
    for (const o of objs) {
      if (o.type === 'npc') this.spawnEntity(o);
      else if (o.type === 'interact') this.interacts.push({ x: o.x! + 8, y: o.y! + 8, kind: prop(o, 'kind'), props: propsObj(o) });
      else if (o.type === 'door') this.doors.push({ rect: new Phaser.Geom.Rectangle(o.x!, o.y!, o.width!, o.height!), target: prop(o, 'target'), dest: prop(o, 'dest') });
    }

    // postFX
    const cam = this.cameras.main;
    this.sat = cam.postFX.addColorMatrix();
    this.vig = cam.postFX.addVignette(0.5, 0.5, 0.82, 0);
    this.neg = cam.postFX.addColorMatrix();
    this.updateFX();

    // input
    this.cursors = this.input.keyboard!.createCursorKeys();
    this.wasd = this.input.keyboard!.addKeys('W,A,S,D');
    // E 交互由持久 UIScene 的 DOM 监听转发到 tryInteract()（见 UIScene）

    // listeners
    this.on('stage', () => this.updateFX());
    this.on('negativeFlash', () => { this.negFrames = 2; });
    this.on('sleep', () => this.startSleep());
    this.on('festival', () => this.onFestival());
    this.on('fog', () => this.onFog());
    this.on('picnic', () => this.doPicnic());
    this.on('sad', () => this.headTurn());
    this.on('dusk', () => { if (Director.day === 6) { Director.flags.rain = true; this.ui.setRain(true); } });

    this.cameras.main.fadeIn(400, 0, 0, 0);
    if (this.mapKey === 'home' && data.spawn === 'start') this.ui.setRain(!!Director.flags.rain);
    else this.ui.setRain(!!Director.flags.rain && OUTDOOR.has(this.mapKey));
    this.events.once('shutdown', () => this.cleanup());
  }

  private on(ev: string, fn: (...a: any[]) => void) { this.handlers[ev] = fn; Director.on(ev, fn); }
  private cleanup() { for (const [ev, fn] of Object.entries(this.handlers)) Director.off(ev, fn as any); this.handlers = {}; }

  private spawnEntity(o: any) {
    const id = prop(o, 'npc');
    const x = o.x + 8, y = o.y + 16;
    if (id === 'bird1' || id === 'bird2') {
      const s = this.add.sprite(x, y, 'birds', 0).setOrigin(0.5, 1).setDepth(40); s.play('bird-flap');
      this.ents.push({ id, kind: 'bird', sprite: s });
    } else if (id.startsWith('extra')) {
      const k = parseInt(id.slice(5), 10);
      const s = this.add.sprite(x, y, 'extras', (k - 1) * 2).setOrigin(0.5, 1).setDepth(40); s.play('extra' + k);
      const tw = this.tweens.add({ targets: s, x: x + 24, duration: 2200, yoyo: true, repeat: -1, onYoyo: () => s.setFlipX(true), onRepeat: () => s.setFlipX(false) });
      this.ents.push({ id, kind: 'extra', sprite: s, anim: 'extra' + k, tween: tw });
    } else {
      const s = this.add.sprite(x, y, NPCS[id]?.sprite || 'npc_afu', 0).setOrigin(0.5, 1).setDepth(40);
      this.ents.push({ id, kind: 'named', sprite: s });
    }
  }

  update(_t: number, dtMs: number) {
    const dt = dtMs;
    this.doorCd -= dt;
    if (this.negFrames > 0) { this.neg.negative(); this.negFrames--; if (this.negFrames === 0) this.neg.reset(); }
    else if (Director.stage === 4 && Math.random() < 0.00011 * dt) { this.negFrames = 2; }
    if (Director.stage === 4 && !this.offFlash && Math.random() < 0.00017 * dt) { this.offFlash = true; this.time.delayedCall(100, () => (this.offFlash = false)); }
    this.updateEntities();

    const busy = this.ui.busy;
    if (this.t14) { this.tickT14(dt); return; }
    if (busy) { this.player.setVelocity(0); this.setIdleAnim(); return; }

    // movement
    let vx = 0, vy = 0;
    if (this.cursors.left.isDown || this.wasd.A.isDown) vx = -1;
    else if (this.cursors.right.isDown || this.wasd.D.isDown) vx = 1;
    if (this.cursors.up.isDown || this.wasd.W.isDown) vy = -1;
    else if (this.cursors.down.isDown || this.wasd.S.isDown) vy = 1;
    const moving = vx !== 0 || vy !== 0;
    if (moving) { const l = Math.hypot(vx, vy); this.player.setVelocity((vx / l) * SPEED, (vy / l) * SPEED); }
    else this.player.setVelocity(0);
    if (vx < 0) this.facing = 'left'; else if (vx > 0) this.facing = 'right'; else if (vy < 0) this.facing = 'up'; else if (vy > 0) this.facing = 'down';
    if (moving) this.player.anims.play('walk-' + this.facing, true); else this.setIdleAnim();

    this.detectTarget();
    this.checkDoors();
    this.tickIdle(dt, moving);
    this.tickRain(dt);
    this.tickForced(dt);
  }

  private setIdleAnim() {
    this.player.anims.stop();
    this.player.setFrame({ down: 0, left: 4, right: 8, up: 12 }[this.facing]!);
  }

  private detectTarget() {
    let best: any = null, bd = 24 * 24;
    const test = (x: number, y: number, obj: any) => { const d = (x - this.player.x) ** 2 + (y - this.player.y) ** 2; if (d < bd) { bd = d; best = obj; } };
    for (const it of this.interacts) test(it.x, it.y, { type: 'interact', it });
    for (const e of this.ents) if (e.kind !== 'extra') test(e.sprite.x, e.sprite.y - 8, { type: 'npc', e });
    this.target = best;
    this.ui.showPrompt(best ? '按 E' : null);
  }

  tryInteract() { if (!this.ui.busy && !this.t14 && !this.trans && this.target) this.doInteract(); }

  private doInteract() {
    if (this.target.type === 'npc') {
      const e: Ent = this.target.e;
      if (e.kind === 'bird') { this.ui.showDialogue([narr('双子鸟：叽——叽——（音乐好像就是从它们那儿飘出来的。）')]); return; }
      this.ui.showDialogue(talkScript(e.id));
    } else {
      const it = this.target.it;
      const script = interactScript(it.kind, it.props);
      if (script) this.ui.showDialogue(script);
    }
  }

  private checkDoors() {
    if (this.doorCd > 0 || this.trans) return;
    const pr = new Phaser.Geom.Rectangle(this.player.x - 5, this.player.y - 3, 10, 8);
    for (const d of this.doors) {
      if (Phaser.Geom.Rectangle.Overlaps(pr, d.rect)) {
        if (this.forced) { Director.escapeCount++; this.ui.toast('（你从欢乐庆典的边缘走开了……）'); this.endForced(); }
        this.gotoMap(d.target, d.dest, 200);
        return;
      }
    }
  }

  private gotoMap(map: string, spawn: string, fade = 150) {
    if (this.trans) return;
    this.trans = true; this.doorCd = 400; this.player.setVelocity(0);
    this.cameras.main.fadeOut(fade, 0, 0, 0);
    this.cameras.main.once('camerafadeoutcomplete', () => this.scene.restart({ map, spawn }));
  }

  private tickIdle(dt: number, moving: boolean) {
    if (moving || this.target) { this.idle = 0; return; }
    this.idle += dt;
    if (this.idle >= 10000) { this.idle = 0; Director.sadBehavior('发呆'); }
  }

  private tickRain(dt: number) {
    if (!Director.flags.rain) return;
    if (OUTDOOR.has(this.mapKey)) { this.rainT += dt; if (this.rainT >= 10000) { this.rainT = 0; Director.sadBehavior('雨中久站'); } }
    else this.rainT = 0;
  }

  private tickForced(dt: number) {
    if (this.forced) { this.forcedT += dt; if (this.forcedT >= 10000) { Director.setHappiness(50); this.ui.toast('欢乐庆典把你的幸福拉回了 50。'); this.endForced(); } return; }
    if (Director.day < 7 && Director.lowHappySince && performance.now() - Director.lowHappySince >= 120000) this.startForced();
  }
  private startForced() { this.forced = true; this.forcedT = 0; Director.lowHappySince = 0; this.ui.setForced(true); this.ui.toast('※ 欢乐庆典 ※ 大家都在等你开心起来！'); }
  private endForced() { this.forced = false; this.ui.setForced(false); }

  private headTurn() {
    let best: Ent | null = null, bd = 1e9;
    for (const e of this.ents) if (e.kind === 'named') { const d = (e.sprite.x - this.player.x) ** 2 + (e.sprite.y - this.player.y) ** 2; if (d < bd) { bd = d; best = e; } }
    if (!best) return;
    best.sprite.setFlipX(this.player.x < best.sprite.x);
    this.tweens.add({ targets: best.sprite, scaleX: 1.15, scaleY: 1.15, duration: 120, yoyo: true });
  }

  private updateEntities() {
    for (const e of this.ents) {
      if (e.kind === 'named') {
        let expr = Director.npcExpression(e.id);
        if (this.offFlash && Director.npcStage(e.id) === 4) expr = 'off';
        e.sprite.setFrame(EXPR[expr]);
        if (this.t14 || Director.npcStage(e.id) >= 3) e.sprite.setFlipX(this.player.x < e.sprite.x);
        else e.sprite.setFlipX(false);
      } else if (e.kind === 'extra') {
        if (Director.stage === 4 && e.id === 'extra1') { e.tween?.pause(); e.sprite.anims.stop(); e.sprite.setFlipX(this.player.x < e.sprite.x); }
        else { e.tween?.resume(); if (!e.sprite.anims.isPlaying) e.sprite.play(e.anim!); }
      }
    }
  }

  private updateFX() {
    const p = Director.params;
    this.sat.saturate((p.saturation - 1) * 1.5, false);
    this.vig.strength = p.vignette;
    this.vig.radius = 0.82 - p.vignette * 0.25;
  }

  // ---------------- day flow ----------------
  private startSleep() {
    if (Director.day >= 7 || this.trans) return;
    this.trans = true; this.ui.busy = true; this.player.setVelocity(0);
    this.cameras.main.fadeOut(600, 0, 0, 0);
    this.cameras.main.once('camerafadeoutcomplete', () => {
      const prev = Director.day;
      this.ui.playDream(prev, () => {
        activateDay(prev + 1);
        const nd = Director.day;
        // 先在持久的 UIScene 上解锁再重启本场景；delayedCall 会随本场景 restart 被清除，绝不能放到 restart 之后
        this.ui.busy = false;
        Director.flags.rain = false; this.ui.setRain(false);
        this.scene.restart({ map: 'home', spawn: 'start' });
        this.ui.toast(`Day ${nd}`);
      });
    });
  }

  private onFestival() {
    if (Director.day === 7) {
      const e = evalEnding(false);
      if (e) this.startEnding(e);
      else if (Director.awareness >= 70 && Director.clues.size >= 6) this.ui.showDialogue([narr('广场很热闹……但村庄尽头那道雾，好像在等你。')]);
      else this.startEnding('E2');
    } else if (Director.day === 6 && taskActive('T14')) this.startT14();
    else this.ui.showDialogue([narr('庆典的舞台，还没到时候呢。')]);
  }

  private onFog() {
    if (Director.day === 7) {
      if (Director.escapeCount >= 3 && Director.happiness <= 10) this.startEnding('E4');
      else if (Director.awareness >= 70 && Director.clues.size >= 6) this.startEnding('E3');
      else this.ui.showDialogue([narr('雾还是雾。你还没准备好。')]);
    } else this.ui.showDialogue([narr('雾在轻轻涌动，像在呼吸。')]);
  }

  startEnding(id: string) {
    if (Director.endingShown) return;
    Director.endingShown = true;
    this.cameras.main.fadeOut(500, 0, 0, 0);
    this.cameras.main.once('camerafadeoutcomplete', () => { this.scene.stop('UI'); this.scene.start('Ending', { id }); });
  }

  private startT14() {
    this.t14 = true; this.t14t = 0; this.t14fade = false;
    this.ui.busy = true; this.player.setVelocity(0); this.setIdleAnim();
    this.ui.toast('保持微笑……原地站好，别动。');
    this.cameras.main.zoomTo(1.9, 60000, 'Linear');
  }
  private tickT14(dt: number) {
    this.t14t += dt;
    if (!this.t14fade && this.t14t >= 40000) { this.t14fade = true; Audio.fadeMaster(0, 20); } // 40→60s 渐隐至零
    if (this.t14t >= 60000) {
      this.t14 = false; this.ui.busy = false;
      Audio.restoreMaster(1.5);
      this.cameras.main.zoomTo(1, 700);
      completeTask('T14');
    }
  }

  private doPicnic() {
    this.ui.busy = true; this.player.setVelocity(0);
    this.cameras.main.fadeOut(400, 0, 0, 0);
    this.cameras.main.once('camerafadeoutcomplete', () => {
      this.cameras.main.fadeIn(400, 0, 0, 0);
      this.ui.showDialogue([
        narr('你在湖边铺好的野餐布上坐下。'),
        narr('湖面倒映着你——'),
        narr('倒影却慢了一拍才动。'),
        narr('……大概是风吧。'),
      ], () => { completeTask('T7'); this.ui.busy = false; });
    });
  }

  // debug hooks (called by UIScene)
  debugTeleport(map: string) { this.gotoMap(map, 'sp_from_plaza'); }
}

function prop(o: any, k: string): string { const p = (o.properties || []).find((x: any) => x.name === k); return p ? p.value : ''; }
function propsObj(o: any): any { const r: any = {}; for (const p of (o.properties || [])) r[p.name] = p.value; return r; }
