/**
 * Karaoke KTV Video Maker - Global State Management
 */
class AppState {
  constructor() {
    this.projectTitle = "未命名伴唱專案";
    this.videoUrl = "";
    this.videoLocalPath = "";
    this.videoDuration = 0;
    this.waveformData = [];
    
    // Style configurations
    this.style = {
      fontFamily: "Microsoft JhengHei",
      fontSize: 110,
      bold: true,
      letterSpacing: 10, // px spacing between characters
      defaultSinger: "female", // 'male' | 'female' | 'duet' default singer role
      customFontUrl: "/uploads/fonts/源泉圓體_GenSenMaruGothic_TW_TTF_Medium_6127415.ttf",
      customFontName: "源泉圓體 TTF Medium",
      baseColor: "#ffffff",
      baseOutlineColor: "#000000",
      outlineWidth: 13,
      shadowWidth: 0,
      shadowColor: "#000000",
      maleWipeColor: "#0000ff",
      maleOutlineColor: "#ffffff",
      femaleWipeColor: "#ff0000",
      femaleOutlineColor: "#ffffff",
      duetWipeColor: "#008000",
      duetOutlineColor: "#ffffff",
      line1X: 0.08, // 8% from left (靠左對齊)
      line1Y: 0.68, // 68% from top
      line2X: 0.94, // 94% from left (靠右對齊右側位置)
      line2Y: 0.84, // 84% from top
      enableFade: true, // 歌詞進場與退場淡入淡出
      fadeDuration: 0.3, // 淡入淡出秒數 (0.1 ~ 1.5s)
      enableCountdown: true,
      bpm: 80, // 歌曲速度 (BPM，依拍子倒數提示點)
      countdownDotCount: 4, // 3 or 4 dots
      countdownDotSize: 59, // diameter in px
      countdownOffsetX: 37, // px relative to text start
      countdownOffsetY: -57, // px above lyrics line
      showFadedGhost: true, // show faded outline for disappearing dots
      // Male dot colors
      maleDotFill: "#0055ff",
      maleDotOutline: "#ffffff",
      maleDotOutlineWidth: 8,
      // Female dot colors
      femaleDotFill: "#ff0000",
      femaleDotOutline: "#ffffff",
      femaleDotOutlineWidth: 8,
      // Duet dot colors
      duetDotFill: "#008000",
      duetDotOutline: "#ffffff",
      duetDotOutlineWidth: 8,
      // Title Cards (Intro & Outro)
      titleCards: {
        intro: {
          enabled: true,
          startTime: 0.9,
          duration: 6.0,
          fadeDuration: 1.0,
          titleText: "歌名大標題",
          titleFontFamily: "",
          titleBold: true,
          titleLetterSpacing: 18,
          titleFontSize: 150,
          titleFillColor: "#2163fd",
          titleOutlineColor: "#ffffff",
          titleOutlineWidth: 18,
          titleY: 0.28,
          creditsText: "主唱：\n作詞：\n作曲：",
          creditsFontFamily: "",
          creditsBold: true,
          creditsLetterSpacing: 10,
          creditsFontSize: 71,
          creditsFillColor: "#ffffff",
          creditsOutlineColor: "#000000",
          creditsOutlineWidth: 15,
          creditsY: 0.43
        },
        outro: {
          enabled: false,
          startTime: 0,
          duration: 4.0,
          fadeDuration: 0.8,
          titleText: "謝謝收看",
          titleFontFamily: "",
          titleBold: true,
          titleLetterSpacing: 6,
          titleFontSize: 76,
          titleFillColor: "#ffffff",
          titleOutlineColor: "#000000",
          titleOutlineWidth: 8,
          titleY: 0.38,
          creditsText: "KTV 伴唱製作：Jet3C Studio\n純屬音樂分享交流 請勿用於商業用途",
          creditsFontFamily: "",
          creditsBold: true,
          creditsLetterSpacing: 2,
          creditsFontSize: 36,
          creditsFillColor: "#E0E0E0",
          creditsOutlineColor: "#000000",
          creditsOutlineWidth: 5,
          creditsY: 0.55
        }
      }
    };
    
    // Sentences: [{ id, singer: 'male'|'female'|'duet', text, words: [{ text, startTime, endTime }] }]
    this.sentences = [];
    
    // UI state
    this.activeSentenceIndex = 0;
    this.activeWordIndex = 0;
    this.currentTime = 0;
    this.isPlaying = false;
    this.isRecordingTap = false;
    this.playbackRate = 1.0;
    this.currentMode = "timeline"; // 'timeline' | 'tap'
    this.dragPositionsMode = false;
    
    // Timeline zoom & scroll
    this.pixelsPerSecond = 80;
    
    this.listeners = {};
    this._saveTimer = null;
    this._cachedSavedStyle = null;
  }

  on(event, callback) {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }
    this.listeners[event].push(callback);
  }

  emit(event, data) {
    if (this.listeners[event]) {
      this.listeners[event].forEach(cb => cb(data));
    }
  }

  getDefaultTitleCards() {
    return {
      intro: {
        enabled: true,
        startTime: 1.0,
        duration: 4.5,
        fadeDuration: 0.6,
        titleText: "疼惜我的吻",
        titleFontFamily: "",
        titleBold: true,
        titleLetterSpacing: 6,
        titleFontSize: 86,
        titleFillColor: "#00E676",
        titleOutlineColor: "#FFFFFF",
        titleOutlineWidth: 10,
        titleY: 0.35,
        creditsText: "主唱：葉佳修\n      林晏如\n作詞：葉佳修\n作曲：葉佳修",
        creditsFontFamily: "",
        creditsBold: true,
        creditsLetterSpacing: 2,
        creditsFontSize: 42,
        creditsFillColor: "#FFFFFF",
        creditsOutlineColor: "#000000",
        creditsOutlineWidth: 6,
        creditsY: 0.55
      },
      outro: {
        enabled: false,
        startTime: 0,
        duration: 4.0,
        fadeDuration: 0.8,
        titleText: "謝謝收看",
        titleFontFamily: "",
        titleBold: true,
        titleLetterSpacing: 6,
        titleFontSize: 76,
        titleFillColor: "#FFFFFF",
        titleOutlineColor: "#000000",
        titleOutlineWidth: 8,
        titleY: 0.38,
        creditsText: "KTV 伴唱製作：Jet3C Studio\n純屬音樂分享交流 請勿用於商業用途",
        creditsFontFamily: "",
        creditsBold: true,
        creditsLetterSpacing: 2,
        creditsFontSize: 36,
        creditsFillColor: "#E0E0E0",
        creditsOutlineColor: "#000000",
        creditsOutlineWidth: 5,
        creditsY: 0.55
      }
    };
  }

  setProjectData(data) {
    if (data.title) this.projectTitle = data.title;
    if (data.videoUrl) this.videoUrl = data.videoUrl;
    if (data.videoLocalPath) this.videoLocalPath = data.videoLocalPath;
    if (data.videoOriginalName) this.videoOriginalName = data.videoOriginalName;
    
    // Support titleCards if placed at top-level or inside style
    const loadedStyle = data.style || {};
    const loadedTitleCards = data.titleCards || loadedStyle.titleCards || {};

    const defaultTitleCards = this.getDefaultTitleCards();
    const mergedTitleCards = {
      intro: { ...(defaultTitleCards.intro || {}), ...(this.style.titleCards?.intro || {}), ...(loadedTitleCards.intro || {}) },
      outro: { ...(defaultTitleCards.outro || {}), ...(this.style.titleCards?.outro || {}), ...(loadedTitleCards.outro || {}) }
    };

    this.style = {
      ...this.style,
      ...loadedStyle,
      titleCards: mergedTitleCards
    };

    if (data.sentences) this.sentences = data.sentences;

    // Persist loaded project style to localStorage and server user_settings.json
    this.saveSettings();

    this.emit("projectLoaded", data);
    this.emit("styleChanged", this.style);
    this.emit("sentencesChanged", this.sentences);
  }

  updateStyle(newStyle) {
    this.style = { ...this.style, ...newStyle };
    this.emit("styleChanged", this.style);
    this.saveSettings();
  }

  updateTitleCard(type, newProps) {
    if (!this.style.titleCards) {
      this.style.titleCards = { intro: {}, outro: {} };
    }
    if (!this.style.titleCards[type]) {
      this.style.titleCards[type] = {};
    }
    this.style.titleCards[type] = { ...this.style.titleCards[type], ...newProps };
    this.emit("styleChanged", this.style);
    this.saveSettings();
  }

  saveSettings() {
    try {
      localStorage.setItem("ktv_user_style", JSON.stringify(this.style));
      this._cachedSavedStyle = { ...this.style };
    } catch (e) {}

    // Debounce persisting to server user_settings.json
    if (this._saveTimer) clearTimeout(this._saveTimer);
    this._saveTimer = setTimeout(async () => {
      try {
        await fetch("/api/settings", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(this.style)
        });
        this.emit("settingsSaved", this.style);
      } catch (err) {
        console.log("Failed to save settings to server:", err);
      }
    }, 300);
  }

  getSavedStyle() {
    if (this._cachedSavedStyle) return this._cachedSavedStyle;
    try {
      const local = localStorage.getItem("ktv_user_style");
      if (local) {
        this._cachedSavedStyle = JSON.parse(local);
        return this._cachedSavedStyle;
      }
    } catch (e) {}
    return null;
  }

  async loadSavedSettings() {
    let saved = {};
    try {
      const local = localStorage.getItem("ktv_user_style");
      if (local) {
        saved = JSON.parse(local);
      }
    } catch (e) {}

    try {
      const res = await fetch("/api/settings");
      if (res.ok) {
        const serverData = await res.json();
        if (serverData && Object.keys(serverData).length > 0) {
          saved = { ...saved, ...serverData };
        }
      }
    } catch (e) {
      console.log("Could not fetch server settings:", e);
    }

    if (saved && Object.keys(saved).length > 0) {
      const mergedTitleCards = {
        intro: { ...(this.style.titleCards?.intro || {}), ...(saved.titleCards?.intro || {}) },
        outro: { ...(this.style.titleCards?.outro || {}) , ...(saved.titleCards?.outro || {}) }
      };
      this.style = { ...this.style, ...saved, titleCards: mergedTitleCards };
      this._cachedSavedStyle = { ...this.style };
      this.emit("styleChanged", this.style);
    }
  }

  setSentences(sentences) {
    this.sentences = sentences;
    this.emit("sentencesChanged", this.sentences);
  }

  updateSentenceSinger(idx, singer) {
    if (this.sentences[idx]) {
      this.sentences[idx].singer = singer;
      if (this.sentences[idx].prefix) {
        const pMap = { male: "男：", female: "女：", duet: "合：" };
        this.sentences[idx].prefix = pMap[singer] || "";
      }
      this.emit("sentencesChanged", this.sentences);
    }
  }

  updateSentenceCountdown(idx, hasCountdown) {
    if (this.sentences && this.sentences[idx]) {
      this.sentences[idx].hasCountdown = !!hasCountdown;
      this.emit("sentencesChanged", this.sentences);
    }
  }

  updateSentenceText(idx, newText) {
    if (this.sentences && this.sentences[idx]) {
      const sentence = this.sentences[idx];
      const oldWords = sentence.words || [];
      let cleanText = newText.trim();

      // If user typed [男] or 男： when editing, extract it to prefix
      const tagRegex = /^[\[【(（](男|女|合)(?:唱)?[\]】)）][:：]?\s*|^([男女合])(?:唱)?[:：/]\s*/;
      const match = cleanText.match(tagRegex);
      if (match) {
        const singerChar = match[1] || match[2];
        if (singerChar === "男") {
          sentence.singer = "male";
          sentence.prefix = "男：";
        } else if (singerChar === "女") {
          sentence.singer = "female";
          sentence.prefix = "女：";
        } else if (singerChar === "合") {
          sentence.singer = "duet";
          sentence.prefix = "合：";
        }
        cleanText = cleanText.replace(tagRegex, "").trim();
      }

      const tokens = cleanText.match(/[\u4e00-\u9fff\u3400-\u4dbf\u3040-\u30ff\uac00-\ud7af]|[a-zA-Z0-9]+|[^\s\w]/g) || cleanText.split("");

      // Preserve existing timing for aligned words where possible
      const newWords = tokens.map((t, i) => {
        if (i < oldWords.length) {
          return {
            text: t,
            startTime: oldWords[i].startTime || 0.0,
            endTime: oldWords[i].endTime || 0.0
          };
        } else {
          return {
            text: t,
            startTime: 0.0,
            endTime: 0.0
          };
        }
      });

      sentence.text = cleanText;
      sentence.words = newWords;
      this.emit("sentencesChanged", this.sentences);
    }
  }

  updateWordTiming(sIdx, wIdx, startTime, endTime) {
    if (this.sentences[sIdx] && this.sentences[sIdx].words[wIdx]) {
      this.sentences[sIdx].words[wIdx].startTime = Math.max(0, startTime);
      this.sentences[sIdx].words[wIdx].endTime = Math.max(startTime + 0.05, endTime);
      this.emit("timingUpdated", { sIdx, wIdx });
    }
  }

  setCurrentTime(t) {
    this.currentTime = Math.max(0, t);
    this.emit("timeUpdate", this.currentTime);
  }
}

window.appState = new AppState();
