/**
 * AvatarController
 *
 * 调试 / 桥接层 API。挂在 window.avatar 上。
 *
 * 三层能力：
 * - 原子操作：setExpression / playMotion / setMouthOpen
 * - 状态切换：setState (idle/listening/thinking/speaking)
 * - 语义动作：playAction (greet/happy/shy/surprised/think/wave/sing)
 * - 程序化跳舞：startDance / stopDance
 *
 * 嘴型 & 跳舞通过 ICubismUpdater 机制接入框架渲染循环，
 * 不会被 motion update 覆盖。
 */

import { LAppDelegate } from './lappdelegate';
import { LAppModel } from './lappmodel';
import * as LAppDefine from './lappdefine';
import { IParameterProvider } from '@framework/motion/iparameterprovider';
import {
  ICubismUpdater,
  CubismUpdateOrder
} from '@framework/motion/icubismupdater';
import { CubismFramework } from '@framework/live2dcubismframework';
import { CubismModel } from '@framework/model/cubismmodel';
import { CubismIdHandle } from '@framework/id/cubismid';

type CharacterState = 'idle' | 'listening' | 'thinking' | 'speaking';

/** 嘴型参数 provider（每帧 LipSyncUpdater 调用） */
class ManualLipSyncProvider extends IParameterProvider {
  public value = 0;
  update(): boolean { return true; }
  getParameter(): number { return this.value; }
}

/**
 * 跳舞 updater —— 每帧驱动头/身参数，做正弦波摇摆。
 * 注册到 _updateScheduler，执行时机晚于 LipSync，保证不会被覆盖。
 */
class DanceUpdater extends ICubismUpdater {
  public active = false;
  public phase = 0;
  public intensity = 1.0;

  private _idAngleX: CubismIdHandle | null = null;
  private _idAngleY: CubismIdHandle | null = null;
  private _idAngleZ: CubismIdHandle | null = null;
  private _idBodyX: CubismIdHandle | null = null;
  private _idBodyZ: CubismIdHandle | null = null;
  private _idsResolved = false;

  constructor() {
    super(CubismUpdateOrder.CubismUpdateOrder_Max - 1);
    // 不能在这里调 CubismFramework.getIdManager()：
    // 模块加载时框架尚未 startUp，会抛 null pointer 让整个 module 失败 → window.avatar 不会被设置
  }

  private _resolveIds(): void {
    const idMgr = CubismFramework.getIdManager();
    if (!idMgr) return;
    this._idAngleX = idMgr.getId('ParamAngleX');
    this._idAngleY = idMgr.getId('ParamAngleY');
    this._idAngleZ = idMgr.getId('ParamAngleZ');
    this._idBodyX = idMgr.getId('ParamBodyAngleX');
    this._idBodyZ = idMgr.getId('ParamBodyAngleZ');
    this._idsResolved = true;
  }

  onLateUpdate(model: CubismModel, dt: number): void {
    if (!model) return;

    if (this.active) {
      this.intensity = Math.min(1, this.intensity + dt * 1.5);
    } else {
      this.intensity = Math.max(0, this.intensity - dt * 1.5);
      if (this.intensity <= 0) return;
    }

    if (!this._idsResolved) this._resolveIds();
    if (!this._idsResolved) return;

    this.phase += dt;
    const t = this.phase;
    const k = this.intensity;
    const TWO_PI = Math.PI * 2;

    const bodyX = Math.sin(t * TWO_PI * 1.0) * 18 * k;
    const bodyZ = Math.sin(t * TWO_PI * 0.5) * 8 * k;
    const headX = Math.sin(t * TWO_PI * 1.0 + Math.PI / 2) * 12 * k;
    const headZ = Math.sin(t * TWO_PI * 1.0 + Math.PI / 4) * 18 * k;
    const headY = Math.abs(Math.sin(t * TWO_PI * 2.0)) * 10 * k;

    if (this._idAngleX) model.addParameterValueById(this._idAngleX, headX);
    if (this._idAngleY) model.addParameterValueById(this._idAngleY, -headY);
    if (this._idAngleZ) model.addParameterValueById(this._idAngleZ, headZ);
    if (this._idBodyX) model.addParameterValueById(this._idBodyX, bodyX);
    if (this._idBodyZ) model.addParameterValueById(this._idBodyZ, bodyZ);
  }
}

class AvatarControllerImpl {
  private _provider: ManualLipSyncProvider = new ManualLipSyncProvider();
  private _providerInstalled = false;
  private _danceUpdater: DanceUpdater = new DanceUpdater();
  private _danceInstalled = false;
  private _danceMotionTimer: number | null = null;
  private _speakingMouthTimer: number | null = null;

  private getModel(): LAppModel | null {
    const sub = LAppDelegate.getInstance().getSubdelegate(0);
    if (!sub) return null;
    const mgr = sub.getLive2DManager();
    if (!mgr) return null;
    return mgr.getModel(0) ?? null;
  }

  /** 第一次需要嘴型时挂上 lipsync provider */
  private ensureProvider(): boolean {
    if (this._providerInstalled) return true;
    const model = this.getModel();
    if (!model || !model._lipSyncUpdater) return false;
    model._lipSyncUpdater.setAudioProvider(this._provider);
    this._providerInstalled = true;
    return true;
  }

  /** 第一次需要跳舞时挂上 dance updater */
  private ensureDanceUpdater(): boolean {
    if (this._danceInstalled) return true;
    const model = this.getModel();
    if (!model || !model._updateScheduler) return false;
    model._updateScheduler.addUpdatableList(this._danceUpdater);
    this._danceInstalled = true;
    return true;
  }

  // ---------- 原子操作 ----------

  setExpression(name: string): boolean {
    const model = this.getModel();
    if (!model) return false;
    model.setExpression(name);
    console.log(`[avatar] setExpression("${name}")`);
    return true;
  }

  listExpressions(): string[] {
    const model = this.getModel();
    if (!model) return [];
    const expressions: Map<string, unknown> = (model as any)._expressions;
    return expressions ? [...expressions.keys()] : [];
  }

  playMotion(group: string, no: number): boolean {
    const model = this.getModel();
    if (!model) return false;
    model.startMotion(group, no, LAppDefine.PriorityForce);
    console.log(`[avatar] playMotion("${group}", ${no})`);
    return true;
  }

  listMotions(): Record<string, number> {
    const model = this.getModel();
    if (!model) return {};
    const setting = (model as any)._modelSetting;
    if (!setting) return {};
    const groupCount = setting.getMotionGroupCount();
    const result: Record<string, number> = {};
    for (let i = 0; i < groupCount; i++) {
      const groupName = setting.getMotionGroupName(i);
      result[groupName] = setting.getMotionCount(groupName);
    }
    return result;
  }

  setMouthOpen(value: number): void {
    if (!this.ensureProvider()) return;
    this._provider.value = Math.max(0, Math.min(1, value));
  }

  // ---------- 状态切换 ----------

  setState(state: CharacterState): boolean {
    const stateMotionMap: Record<CharacterState, [string, number]> = {
      idle: ['Idle', 0],
      listening: ['Idle', 1],
      thinking: ['TapBody', 0],
      speaking: ['TapBody', 1]
    };
    const [group, no] = stateMotionMap[state] ?? ['Idle', 0];
    console.log(`[avatar] setState("${state}") -> playMotion("${group}", ${no})`);
    const ok = this.playMotion(group, no);

    if (state === 'speaking') this._startSpeakingMouth();
    else this._stopSpeakingMouth();
    return ok;
  }

  private _startSpeakingMouth(): void {
    this._stopSpeakingMouth();
    if (!this.ensureProvider()) return;
    const start = performance.now();
    this._speakingMouthTimer = window.setInterval(() => {
      const t = (performance.now() - start) / 1000;
      this._provider.value = Math.abs(Math.sin(t * Math.PI * 3.5));
    }, 33);
  }

  private _stopSpeakingMouth(): void {
    if (this._speakingMouthTimer !== null) {
      clearInterval(this._speakingMouthTimer);
      this._speakingMouthTimer = null;
      this._provider.value = 0;
    }
  }

  // ---------- 语义动作 ----------

  /** 一组预设的"语义动作" —— 未来 LLM FunctionCall 直接调这里 */
  async playAction(name: string): Promise<void> {
    console.log(`[avatar] playAction("${name}")`);
    switch (name) {
      case 'greet':
        this.setExpression('F02'); // 笑脸
        this.playMotion('TapBody', 0);
        break;
      case 'happy':
        this.setExpression('F02');
        this.playMotion('TapBody', 1);
        break;
      case 'shy':
        this.setExpression('F05'); // 害羞脸（Haru 表情挑一个）
        this.playMotion('Dance', 5);
        break;
      case 'surprised':
        this.setExpression('F06');
        this.playMotion('Dance', 10);
        break;
      case 'think':
        this.setExpression('F08');
        this.playMotion('Dance', 14);
        break;
      case 'wave':
        this.setExpression('F02');
        this.playMotion('TapBody', 2);
        break;
      case 'sing':
        this.setExpression('F02');
        this.playMotion('TapBody', 3);
        // 同时给一点嘴型律动
        this._startSpeakingMouth();
        setTimeout(() => this._stopSpeakingMouth(), 4000);
        break;
      default:
        console.warn(`[avatar] unknown action: ${name}`);
    }
  }

  /** 列出可用语义动作 */
  listActions(): string[] {
    return ['greet', 'happy', 'shy', 'surprised', 'think', 'wave', 'sing'];
  }

  // ---------- 程序化跳舞 ----------

  /** 开始跳舞：参数摇摆 + 循环切换 Dance motion */
  startDance(): boolean {
    if (!this.ensureDanceUpdater()) return false;
    this._danceUpdater.active = true;
    this._danceUpdater.phase = 0;
    this.setExpression('F02');

    // 每 4 秒换一个 Dance motion，让动作更丰富
    this._stopDanceMotionCycler();
    const danceCount = this.listMotions()['Dance'] ?? 0;
    if (danceCount > 0) {
      const playRandom = () => {
        const i = Math.floor(Math.random() * danceCount);
        this.playMotion('Dance', i);
      };
      playRandom();
      this._danceMotionTimer = window.setInterval(playRandom, 4000);
    }
    console.log('[avatar] dance started');
    return true;
  }

  stopDance(): void {
    this._danceUpdater.active = false; // 会平滑淡出
    this._stopDanceMotionCycler();
    this.setState('idle');
    console.log('[avatar] dance stopped');
  }

  private _stopDanceMotionCycler(): void {
    if (this._danceMotionTimer !== null) {
      clearInterval(this._danceMotionTimer);
      this._danceMotionTimer = null;
    }
  }

  // ---------- 模拟对话 ----------

  async playMockConversation(): Promise<void> {
    console.log('[avatar] mock conversation start');
    const wait = (ms: number) => new Promise(r => setTimeout(r, ms));

    this.setState('listening');
    await wait(1500);

    this.setState('thinking');
    await wait(1200);

    this.setExpression('F02');
    this.setState('speaking');
    await wait(2500);

    this.setState('idle');
    console.log('[avatar] mock conversation end');
  }
}

const avatarController = new AvatarControllerImpl();
(window as any).avatar = avatarController;

export { avatarController };
export type { CharacterState };
